The FooDoo Lounge

baseLib 2.2.1 documentation

Index

  1. Index
  2. Document Scope
  3. Conventions used in the Lib
  4. Handler Descriptions
    1. Files & Folders
      • 0101 itemExists against pathString - check if an item exists
      • 0102 clearThePath for fullPathStr - verify or create a directory path
      • 0103 itemName out of pathString - get an item's name
      • 0104 nameAndPath out of pathString - get an item's name and the path to it
      • 0105 getPath() - get the path to the calling app
    2. Text
      • 0201 findReplace for someString from findChars into replaceChars - multiple find & replace
      • 0202 plainText from str - styled text to plain string
      • 0203 listToString for someList from stringDelimiter - make a list into a string
      • 0204 stringToList for someString from stringDelimiter - tokenise a string
      • 0205 initTids for newDelims - set the TIDS, returning previous value
      • 0206 setTids for newDelims - set the TIDS
    3. Numerics
      • 0301 convertByteSize on rawSize - turn a byte number into a readable string
      • 0302 roundTrunc from someReal for decPlaces - round & truncate a number
      • 0303 elapsedTime from secondTime against separatorCharacter - convert seconds to a readable string
    4. Lists
      • 0401 addToList into inputList at idx for newItem - add an item to a list
      • 0402 deleteFromList out of inputList at itemIndices - delete one or more items from a list
    5. File IO - simple reading from & writing to files
      • 0501 writeWithAppend at pathString from writeString - write to a file, appending to any existing contents
      • 0502 overWriteFile at pathString from writeString - write to a file, overwriting any existing contents
      • 0503 readFirstBytes from pathString for numBytes - read the first n bytes of a file
      • 0504 simpleRead from pathString - read a file
      • Reading Configuration Files - interface for getting preference or config info from text files
      • 0505 getValues out of pathString for keyList against kvDelim
      • 0506 collectLinesWithDelim from pathString against thisDelim
      • 0507 getVals out of keyValList for keyList against thisDelim
    6. User Interface - dialogs with simple calls
      • 0601 stdAlert from paramList - display a standard dialog
      • 0602 fatalErr for callerName from {errStr, errNum} - display an error dialog
    7. Date & Time - short date & time strings
      • 0701 shortAlphaNumericDate from dateObj - ddMonyyyy
      • 0702 isoDate from dateObj - yyyy-mm-dd
      • 0703 monthNum from dateObj - integer corresponding to the month
      • 0704 timeStr from dateObj - hyphen delimited time string
  5. Library Change Log
  6. Documentation Change Log

Document Scope

This document describes the functions of baseLib 2.2.1, build 008. Each handler is described briefly, followed by one or more usage examples. Also included is detailed and specific information as to the accepted parameter values & classes, output values & classes, error handling and dependencies.

Index

Conventions used in the Lib

The basic premise on which the handlers are designed is that they try hard to do the job as quickly as possible, if passed good data. In other words, they attempt to be tolerant of different operating conditions (OS & AS versions, absence of functionality) but intolerant of data in the wrong form.

The reason for the former should be fairly obvious - I want to call them with confidence. The latter is done to try to make problems evident when code is being written and tested, rather than when it's being used. It helps me to be more certain about the data I'm working with and avoid "programming by coincidence" - incorrectly assuming what the code is actually doing.

This code has all been written with reliability as the primary objective, speed as the second and compactness as the third. I test them as thoroughly as I can before they go in here and the lib itself is tested each time I build it. The definition of reliability includes things such as the requirement that a file should never be left open, no matter what; and that a handler should never fail silently.

Unless otherwise noted, the handlers do not trap errors. Most will pass on any error condition encountered without any identifying information as to which handler has errored. This gives the caller the most flexibility with regard to error handling, but it does mean you have to be a bit more organised.

Whilst I try to incorporate parameter class checking as part of the code, this cannot be counted on. Instead of performing the often simple & obvious coercions on input data, I tend to use the inherently strict requirements of some algorithms as parameter class checks. It's up to the caller to make sure the data passed in is of the right class.

Index

Handler Descriptions

01 - Files & Folders

0101 itemExists against pathString

Does the item exist? Pass a path string, returns boolean.

USAGE: set itsHere to itemExists against "No:Way:Mandalay"
        --> false

OR:

if (itemExists against "Path:to:something:that:might:be:there") then
    doSomething() -- action to take if the item exists
else
    doSomethingElse() -- action to take if the item does not exist
end if

Input: string - valid full path to a file or folder that may or may not exist.
Output: boolean - true if the items exists, otherwise false
Error: false for any condition other than the item existing.

Note: Expect this to return erroneous true values when passed a path that includes consecutive colons, like: "existingVolume:existingFolder::"

Index

0102 clearThePath for fullPathStr

Verifies a full path, creating folders as required. Pass a path string that includes a valid disk/volume and legal folder names. Path string may include a file name which is ignored. Returns an alias to the folder, or an appropriate error if the disk is not found, one or more of the folder names are bad, or the path has no colons.

It checks whether the item exists first, so it can be called without great overhead even if the item may already be there but you just want to verify it.

USAGE: set newFolderAlias to clearThePath for "Path:to:some:item:or:another:someFile.txt"
        --> alias "Path:to:some:item:or:another:"

Calls by clearThePath:
    Application: Finder - if creating new folder(s)
    Handler: initTids, setTids, itemExists

Input: string - any potentially valid path: A colon separated string of legal folder names that starts with an existing volume name. File names can be included and are ignored. Note that this handler is not specifically OS X savvy - all names in the path are limited to 31 characters & forward slashes are allowed.
Output: alias - an alias to the verified directory path.
Error: -35 no such volume (bad disk name); -37 for bad folder name; -1703 if the path has no colons.

Index

0103 itemName out of pathString

Takes path string of a file or folder & returns its name. This calls the following handler, (0104) so use it & just take the first item if speed is of the utmost importance.

USAGE: set thisName to itemName out of "Path:to:some:item:or:another"
        --> "another"

Calls by itemName:
    Handler: nameAndPath

Input: colon delimited string - designed for paths of files & folders.
Output: string - the name of the item.
Error:

Index

0104 nameAndPath out of pathString

Takes path string of a file or folder and returns a list containing its name and path.

USAGE: set {thisName, thisPath} to nameAndPath out of "Path:to:some:item:or:another"
        --> {"another", "Path:to:some:item:or:"}

Calls by nameAndPath:
    Handler: initTids, setTids

Input: colon delimited string - designed for paths of files & folders.
Output: list of strings - the file's name & the path to it.
Error:

Index

0105 getPath()

Returns a full path string to the calling application. Only works in script apps (not in compiled scripts unless explicitly supported by the editor or script runner).

USAGE: set myContainer to getPath()
        --> "Path:to:the:calling:app's:folder:"

Calls by getPath:
    uses the "" as alias construct, or path to me if that fails

Input: no parameters
Output: string - path to the calling app's folder.
Error: incorrect value if called from Script Editor (returns the path to the editor, not the script)

Index

02 - Text

0201 findReplace for someString from findChars into replaceChars

A TIDs based find and replace routine for replacing multiple characters in a string. Reasonably fast unless there are a lot of different characters to replace. WIll probably fail if there are more than about 4000 instances of any 'find' string.

USAGE: set newString to findReplace for "this:string:had:colons" from {":", "i", "o"} into {"-", "u", "e"}
        --> "thus-strung-had-celens"

Input:
        inputString string - the text to do the find/replace on.
        oldChars list - list of strings to find.
        newChar list - list of strings to be substituted.
Output: string - inputString with all instances of oldChars replaced with newChars
Error: not ruthlessly tested for non string values.

Index

0202 plainText from str

A little hack from Arthur J. Knapp for removing style information from strings.

USAGE: set unstyledText to plainText from "styled text string"
        --> "styled text string"

Input: string/styled text/Unicode text - a string that may contain style information.
Output: string - the string with style info removed.
Error: Will return the input if it can't remove style info.

Index

0203 listToString for someList from stringDelimiter

Takes a list of strings (or values that AS can coerce to strings) and turns it into a string, using the supplied list item delimiter.

USAGE: set myString to listToString for {"dum", "de", "doo", "dum"} from "-"
        --> "dum-de-doo-dum"

Calls by listToString:
    Handler: initTids, setTids

Input:
        someList list - an AS list of items that can be coerced to string
        stringDelimiter string - the separator for the list items.
Output: string - the list items delimited by stringDelimiter.
Error: not ruthlessly tested for non string values

Index

0204 stringToList for someString from stringDelimiter

Splits a string into a list using the supplied delimiter.

USAGE: set myList to stringToList for "10-20-30-40" from "-"
        --> {"10", "20", "30", "40"}

Calls by stringToList:
    Handler: initTids, setTids

Input:
        someString list - the text to tokenise
        stringDelimiter string - the separator for the list items.
Output: list of strings - components of the string, split at stringDelimiter.
Error: not ruthlessly tested, but works

Index

0205 initTids for newDelims

Set AppleScript's text item delimiters to the supplied value, returning the previous value. Use for initialising any operation requiring TIDS, so they can be reset at the end. Calling this handler adds about as much extra time (over having the code inline) as declaring 2 values on 1 line. In other words, it's pretty fast. The overhead is irrelevant in most cases.

USAGE: set oldDelims to initTids for {":"}
        --> <previousTidsValue>

Input: anything - Anything AS will accept as tids. Designed for string values. Use other data classes at your own risk.
Output: anything - previous tids value. Tids will be set to the supplied value.
Error: I haven't found its boundaries - tids seem to take most things as an argument.

Index

0206 setTids for newDelims

Set AppleScript's text item delimiters to the supplied value - a typing aid.

USAGE: setTids for {":"}
        --> <noResult>

Input: anything - See initTids above.
Output: No result is returned. Tids will be set to the supplied value.
Error: - See initTids above.

Index

03 - Numerics

0301 convertByteSize on rawSize

Turn a byte number into a readable string, meant for file sizes. Not terribly accurate for a sum of lots of small files, (due to block size issues, not a bug in this), but good for any single file, or small number of files.

This doesn't exactly mimic the Finder in all cases: A file that is actually 1023.99 MB will show as 1 GB. in the Finder. Similarly, an 11.03 MB file will show as 11 MB.

USAGE: set fileSizeString to convertByteSize on 7561726
        --> "7.21 MB"

Input: integer - a number of bytes. Accepts integral reals & scientific notation, i.e 7.561726E+6.
Output: string - the byte size returned in bytes, K, MB, or GB as appropriate.
Error: non-numeric values, non integral reals. Propagates any error without traps. No input class checks.

Index

0302 roundTrunc from someReal for decPlaces

Round & truncate a number to the specified number of places. This handler is adapted from a similarly named routine by Nigel Garvey. The original is available online at MacScripter.

USAGE: set roundedNum to roundTrunc from 6.78905432 for 4
        --> 6.7891

Input:
        someReal real - the number to round & truncate
        decPlaces integer - the number of decimal places to round to.
Output: real - the rounded & truncated number. Always rounds 5 upwards.
Error: non-numeric values

Index

0303 elapsedTime from secondTime against separatorCharacter

Return a time string in hours, minutes & seconds, from a time in seconds using the supplied separator character. Supports days up to 99 and only includes them if the time is one day or more. Co-written with Nigel Garvey.

USAGE: set timeString to elapsedTime from 86399 against ":"
        --> "23:59:59"

Input:
        secondTime integer or integral real - the time, in seconds, to convert.
        separatorCharacter string - the separator character for the returned time string.
Output: string - an hours:minutes:seconds OR days:hours:minutes:seconds value.
Error: non numeric or non integral values.

Index

04 - Lists

0401 addToList into inputList at idx for newItem

Add an item to a list. Pass the list to be added to, the index at which the new item is to be added & the item to add. Supports valid negative indices - the example below would return the same result if the index -2 was passed.

USAGE: set myList to addToList into {1, 2, 3, 4, 5} at 5 for "newItem"
        --> {1, 2, 3, 4, "newItem", 5}

Input:
        inputList list - the list to which the item is to be added.
        idx integer - the index (position in list) at which the item will be added; if the index is greater than the list length, the new item will be added to the end. If the index is less than 0, the item will be added at that position from the end.
        newItem anything - the new item to add.
Output: list - returns a new list with the item added at the supplied position. Will not change inputList.
Error: idx can't be 0, or an invalid negative index (-6 with a 5 item list for eg)

Index

0402 deleteFromList out of inputList at itemIndices

Delete one or more items from a list. Pass the list, the index at which the item is to be deleted or a list of indices of items to delete. If deleting multiple items from the list, the indices must be in ascending order (as in the second example).

USAGE: set myList to deleteFromList out of {1, 2, 3, 4, 5} at 4
        --> {1, 2, 3, 5}

OR: set myList to deleteFromList out of {1, 2, 3, 4, 5} at {1, 3, 5}
        --> {2, 4}

Input:
        inputList list - the list from which the item(s) will be deleted.
        itemIndices integer or list of integers - the index (position in list) of the item(s) to be deleted.
Output: list - a new list with the item(s) deleted from the supplied position(s)
Error:

Index

05 - File IO

Bread & butter file operations. Unless otherwise noted, all handlers that write to or read from files require a full path string as one parameter, usually called pathString in the declarations below.

Note that all these, including handlers that just read a file, will create a file if it doesn't exist, so make sure the file exists before calling a read function or else the handler will create a new file and then return an eof error.

0501 writeWithAppend at pathString from writeString

Write a string to a text file, creating it if required & appending to the contents of an existing file, if found. Pass a valid path string and the string (text) to write. Returns an alias to the file.

USAGE: writeWithAppend at "Mac HD:Desktop Folder:myTestFile" from "Hello World"
        --> alias "Mac HD:Desktop Folder:myTestFile"

Calls by writeWithAppend:
    OSAX: Standard Additions read/write commands

Input:
        pathString string - full valid path including legal file name.
        writeString string - some other data types are possible, none guaranteed.
Output: alias - to the file; data will be written to the specified file which will be created if required, appended to if existing.
Error: passes the error if it can't successfully write a file for any reason.

Index

0502 overWriteFile at pathString from writeString

Write a string to a text file, creating if required & overwriting an existing file, if found. Pass a path string and the string (text) to write. Use as above.

Calls by overWriteFile:
    OSAX: Standard Additions read/write commands

Input:
        pathString string - full valid path including legal file name.
        writeString string - some other data types are possible, none guaranteed.
Output: alias - to the file; data will be written to the specified file which will be created if required, overwritten if existing.
Error: passes the error if it can't successfully write a file for any reason.

Index

0503 readFirstBytes from pathString for numBytes

Reads the first n bytes of a file. Use it for making sure you have the right file. Pass a path string to an existing file & the number of bytes to read.

USAGE: set fileHeader to readFirstBytes from "Mac HD:Desktop Folder:myTestFile" for 5
        --> string - the first 5 bytes of the file

Calls by readFirstBytes:
    OSAX: Standard Additions read/write commands

Input:
        pathString string - full path to an existing file.
        numBytes integer - the number of bytes to read from the beginning of the file.
Output: string - the bytes that were read from the file's data fork.
Error: passes the error if it can't successfully read the file for any reason.

Index

0504 simpleRead from pathString

Read the data fork of a file - a simple read.

USAGE: set fileContents to simpleRead from "Mac HD:Desktop Folder:myTestFile"
        --> anything - whatever is in the file

Calls by simpleRead:
    OSAX: Standard Additions read/write commands

Input: string - full path to an existing file.
Output: string - the contents of the file's data fork.
Error: passes the error if it can't successfully read the file for any reason.

Index

Reading Configuration Files

An interface for getting configuration or preference values out of a text file. Designed for key/value pairs on single lines. Pass a path string to the file, a list of strings that correspond to the keys you want values for, and the key/value delimiter. Keys can be out of order & those not found return an error string - "keyNotFoundErr" - as a list place marker.

0505 getValues out of pathString for keyList against kvDelim

An interface for getting configuration or preference values out of a text file. Designed for key/value pairs on single lines. Pass a path string to the file, a list of strings that correspond to the keys you want values for, and the key/value delimiter used in the config file. Keys can be out of order & those not found return an error string - "keyNotFoundErr" - as a list place marker.

USAGE: set myPrefs to getValues out of "Mac HD:testFile" for {"keyTwo", "noKey", "keyOne"} against "="
        --> {"valueTwo", "keyNotFoundErr", "valueOne"}

Calls by getValues:
    Handler: collectLinesWithDelim, getVals

Input:
        pathString list - full path to an existing config file.
        keyList list of strings - keys you want values for.
        kvDelim string - the key/value delimiter in the cfg file.
Output: list of strings - the values of each supplied key, if found, or the string "keyNotFoundErr" if not. Values are returned in the order in which they are requested, not the order in which they appear in the file.
Error: passes any file reading error; "keyNotFoundErr" acts as a list place marker, so remaining values can be used even if one or more are missing.

Index

0506 collectLinesWithDelim from pathString against thisDelim

Just pulls lines out of a file that contain a given string - thisDelim. Limited to files under ~2000 lines - uses paragraphs. Designed to be used by getValues but can be called externally.

Calls by getValues:
    Handler: simpleRead

Input:
        pathString string - full path to an existing config file.
        thisDelim string - the string to search for.
Output: list of strings - every line in the file that contains thisDelim.
Error: passes the error if it can't successfully read the file for any reason.

Index

0507 getVals out of keyValList for keyList against thisDelim

Also here for getValues. Takes a list of strings each containing the same delimiter & returns the 2nd item - the value in a key/value pair.

Calls by getValues:
    Handler: initTids, setTids

Input:
        keyValList list of strings - the key/value pairs.
        keyList list of strings - keys you want values for.
        thisDelim string - the key/value delimiter.
Output: list of strings - values from the key/value pairs.
Error: see getValues

Index

06 - User Interface

A couple of basic dialogs.

0601 stdAlert from paramList

A simple & flexible way to call display dialog. Dialogs display with the system default buttons & a note icon, (1) or with user defined buttons and the note icon, (2) or with user defined buttons and icon (3).

USAGE:
(1) set buttonResult to stdAlert from "This is a simple call to 'stdAlert'"
        --> "OK" -- name of the "OK" button, (changes with system language) or error number -128 (user cancel)

(2) set buttonResult to stdAlert from {"This call to 'stdAlert' uses different buttons", {"Cancel", "Continue"}}
        --> "Continue"

(3) set buttonResult to stdAlert from {"This call to 'stdAlert' uses different buttons and icon", {"Cancel", "Continue"}, 2}
        --> "Continue"

Calls by stdAlert:
    OSAX: Standard Additions display dialog

Input:
        (1) string - the message to display in a dialog with default system buttons.
        (2) list - a two item list containing the string to display and a list of buttons to use.
        (3) list - a three item list containing the string to display, a list of buttons and the number of an icon to use.
Output: string - the name of the button that was clicked, or the user cancel error, number -128, if there was a "Cancel" button and it was clicked.
Error: returns error number -50 (parameter error) if the wrong params are passed.
Notes: display dialog supports a maximum of 3 buttons. If a button list is passed, the default button of the dialog will be the last button in the list.

Index

0602 fatalErr for callerName from {errStr, errNum}

A standard fatal error dialog. The usage example below would display a dialog with a stop icon, a Cancel button and a string looking like this:

        Cool Script V0.9b3 got an error:

        The variable 'initialisedVar' is not defined (-2753)

USAGE: fatalErr for "Cool Script V0.9b3" from {"The variable 'initialisedVar' is not defined", -2753}
        --> error number -128 (user cancel)

Calls by fatalErr:
    Handler: stdAlert

Input:
        callerName string - name of the script or handler invoking the dialog.
        errStr string - the error string returned by a try/error block or generated by the script or handler.
        errNum number - the error number returned by a try/error block or generated by the script or handler.
Output: error number -128 (user cancel).
Error: <sigh>

Index

07 - Date & Time

Short date & time strings. All these routines require an AppleScript date object as the parameter:
        date "Sunday, 9 March 2003 12:00:00 AM"

0701 shortAlphaNumericDate from dateObj

Returns date string in the format ddMonyyyy.

USAGE: set dateString to shortAlphaNumericDate from (current date)
        --> "09Mar2003"

Input: date
Output: string - the shortened alphanumeric date.
Error: any error will be propagated.

Index

0702 isoDate from dateObj

Returns a date string in the ISO format with a hyphen delimiter: YYYY-MM-DD.

USAGE: set dateString to isoDate from (current date)
        --> "2003-03-09"

Input: date
Output: string - the shortened date, formatted according to ISO standard with a hyphen as the delimiter.
Error: any error will be propagated.

Index

0703 monthNum from dateObj

Returns an integer corresponding to the month of the passed date. Designed to be used by the other date handlers but can be called directly if required. This is the French Vanilla method from Emmanuel Levy, with a slight adjustment by Richard Hartman.

USAGE: set m to monthNum from (current date)
        --> 3

Input: date
Output: integer - corresponding to the month of the date object passed.
Error: any error will be propagated.

Index

0704 timeStr from dateObj

Returns a time string with a hyphen (file name friendly) delimiter. Will return 24 hour time if this is the format set on the computer.

USAGE: set t to timeStr from (current date)
        --> "5-32-36 PM" or "17-32-36"

Input: date
Output: string
Error: any error will be propagated.

Index

Library Change Log

This section contains a change information relevant to using the lib only. For full info, read the Change Log in the root level of the relevant library folder.

From Version 2.2 to Version 2.2.1

V2.2 (build 007) created 23 May 2003. V2.2.1 (build 008) created 09 November 2003

Interface Changes:

writeWithAppend - now returns an alias to the file
overWriteFile - as above

Internal Changes & Bug Fixes:

convertByteSize - now returns accurate results for numbers in the gigabyte range. NG's latest offering.
stdAlert - now behaves in the documented manner: was not conforming to behaviour in usage example 2.

Handlers Added:

none

Handlers Removed:

none

Index

From Version 2.1.2 to Version 2.2

V2.1.2 (build 006) created on 02 May 2002. V2.2 (build 007) created 23 May 2003.

Interface Changes:

swapString -> findReplace - single item params oldChar and newChar become the lists findChars and replaceChars
theItemExists -> itemExists
itemNameAndPath -> nameAndPath
getItemName -> itemName
simpleFileRead -> simpleRead

Internal Changes & Bug Fixes:

All osax calls (mainly to Tanaka's, in file operations) removed for OS X.

monthNum - to NG's latest
elapsedTime - to NG's latest
convertByteSize - now returns integers for numbers less than 1KB (fix required due to change in AS)
getPath - removed try for the alias "" construct (doesn't work in X, not supported)
deleteFromList - fixed bug in the recursive call (was using the old handler name)

Handlers Added:

findReplace - replaces swapString
plainText - get a plain string from styled text
stdAlert - standard dialog call
fatalErr - standard error dialog

Handlers Removed:

swapString - replaced by findReplace
changeCreator - not used much & just a 1 liner now without Tanaka's
doFileIO - and all associated handlers: decided to go vanilla, separate handlers. Should NOT require any changes to scripts but SHOULD be checked anyway!

Index

From Version 2.1.1 to Version 2.1.2

V 2.1.1 (build 005) created on 27 April 2002. V 2.1.2 (build 006) created 02 May 2002.

Interface Changes:

None

Internal Changes & Bug Fixes:

itemNameAndPath - was erroring if passed a disk path.
getPath - AS1.7 broke the method it used, this will work with it but still tries original way 1st.

Handlers Added:

elapsedTime - convert seconds to a readable string
addToList - add an item to a list
deleteFromList - delete one or more items from a list

Handlers Removed:

None

Index

Documentation Change Log

12Apr02 Began adding formatting to original documentation.

14Apr02 Completed AW doc. Created pdf.

17Apr02 Made this copy for the new - 006 - build. Changed description (part 2) of itemName & nameAndPath to reflect bug fix.

20Apr02 A bit of reformatting after making changes to getPath description.

27Apr02 Added descriptions for deleteListItems, insertListItem, elapsedTime & changed intro.

02May02 Major revision: decided to get rid of the 2 part idea - too much duplication. Fixed some errors & omissions along the way, but still needs more checking I think.

03May02 Redid the intro, made new pdf.

02Jun02 Checked & prepared for release. New pdf.

10Mar03 Rewrote as html with index so I can find things quickly.

20-21May03 Updated for baseLib 2.2 - build 007. Deleted Intro. Changed numbering system here & in lib. Various corrections.

03Oct03 Various minor corrections.

02Nov03 Added links back to the index at the end of each handler.

08Nov03 Various changes to markup & stylesheet for increased compatibility with Read Me & FooDoo Lounge systems - now uses includes, so it should more or less drop straight into either.

09Nov03 Updated for baseLib 2.2.1 - build 008.

Index

 

The FooDoo Lounge is Copyright © Richard Morton 2002-2005

|| url: http://www.foodoo.sunreal.com.au/tool/baseLib_2.2_docs.html
|| created: 21-Feb-04, 3:18 PM; updated: 21-Jan-05, 1:48 PM
|| size: 140883 bytes