Created
July 8, 2019 11:36
-
-
Save Trevor-/d58b2d5386cff505846530167d08c394 to your computer and use it in GitHub Desktop.
A Log Factory for Adobe Products
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
// LogFactory.jsx // | |
// Version 1 // | |
// 1st Published: 6th Mar 2017 // | |
// Modified: 6th Mar 2017 // | |
// Copyright © 2017 Trevor https://creative-scripts.com // | |
// May be used on condition that this comment block is left in // | |
// May not be distributed on the Web (A link to https://creative-scripts.com/logging-with-a-smile/ is allowed) // | |
// // | |
// DESCRIPTION: A LogFactory with some extras // | |
// Provided 'as is', with no warranty whatsoever. // | |
// Use and adapted the function freely providing you leave in these comment lines and specify if you have made adaptations // | |
// Redistribution on the Web is not allowed. // | |
// The function is wrapped by the LogFactory so that multiple logs can be maintained simultaneously // | |
// THANKS OKO see http://stackoverflow.com/questions/31232157/what-is-the-best-way-to-copy-but-not-clone-a-function-in-javascript/31235167#31235167 // | |
// Minimal Instructions // | |
// 1) Paste or #include the LogFactory.jsx to the start of the script. (One can use either the full version or the squished one) // | |
// 2) Set up the log file // | |
// var log = new LogFactory('pathToLogFile'); // a few other options here can be added // | |
// 3) Do some logging // | |
// log('hello world'); // logs with default status // | |
// log('hello world', 'i'); // logs with 'Info' status // | |
// log('hello world', 't', 'happy'); // logs with 'Trace' status with a smile // | |
// For detailed instructions see https://creative-scripts.com/logging-with-a-smile/ // | |
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
// jshint unused:true, undef: true | |
/* globals $, Folder, File */ | |
/** | |
* [LogFactory description] | |
* @param {String or File Object} file [Optional] The location of the log file to create or append | |
* @@@ This argument can be an object in the form | |
* @@@ {file: 'pathToFile', write: boolean, store: boolean, level: String or Integer, status: string} | |
* All keys in the object are optional | |
* (Use of the object for is recommended if not using default values for the other arguments) | |
* Default file value is pathToTempFolder/LOG_Script Name_Year_Month_Date.log for example LOG_Script2_2017_Feb_21.log | |
* See LOG.setFile below | |
* @param {Boolean} write [Optional] Whether or not to write to the log file | |
* Default true | |
* See LOG.get() below | |
* @param {Boolean} store [Optional] Whether or not to store to the log file as an array in memory | |
* Default false | |
* @param {String or Integer} level [Optional] The level from which the log values should be logged from (Case insensitive) | |
* Default 2 / Debug | |
* See LOG.get() below | |
* @param {String} defaultStatus [Optional] The log default status (Case insensitive) | |
* Default 'LOG' this has the log level of 2 / Debug | |
* See LOG.setLevel below | |
* @param {Boolean} continuing [Optional] Whether or not the log numbering continues from the previous entry recorded in the log | |
* If false the log count will restart each time a new LogFactory is created | |
* If true and the already log exists the next log entry will continue from the last log count | |
* Default false | |
* See LOG.setLevel below | |
* @returns {Function} The LOG function If one does var log = new LogFactory('myLog.log'); | |
* One can then start using log('hello world'); | |
* SEE THE FULL INSTRUCTIONS https://creative-scripts.com/logging-with-a-smile/ | |
*/ | |
var LogFactory = function(file, write, store, level, defaultStatus, continuing) { // jshint ignore: line | |
///////////////////////////// | |
// Allow passing of Object // | |
///////////////////////////// | |
if (file && (file.constructor === String || file.constructor === File)) { | |
file = { | |
file: file | |
}; | |
} else if (!file) file = { | |
file: {} | |
}; | |
write = (file.write !== undefined) ? file.write : write; | |
if (write === undefined) { | |
write = true; | |
} | |
store = (file.store !== undefined) ? file.store || false : store || false; | |
level = (file.level !== undefined) ? file.level : level; | |
defaultStatus = (file.defaultStatus !== undefined) ? file.defaultStatus : defaultStatus; | |
if (defaultStatus === undefined) { | |
defaultStatus = 'LOG'; | |
} | |
continuing = (file.continuing !== undefined) ? file.continuing : continuing || false; | |
file = file.file || {}; | |
var stack, times, logTime, logPoint, icons, statuses, LOG_LEVEL, LOG_STATUS; | |
stack = []; | |
times = []; | |
logTime = new Date(); | |
logPoint = 'Log Factory Start'; | |
icons = { | |
"1": "\ud83d\udd50", "130": "\ud83d\udd5c", "2": "\ud83d\udd51", "230": "\ud83d\udd5d", "3": "\ud83d\udd52", "330": "\ud83d\udd5e", "4": "\ud83d\udd53", "430": "\ud83d\udd5f", "5": "\ud83d\udd54", "530": "\ud83d\udd60", "6": "\ud83d\udd55", "630": "\ud83d\udd61", "7": "\ud83d\udd56", "730": "\ud83d\udd62", "8": "\ud83d\udd57", "830": "\ud83d\udd63", "9": "\ud83d\udd58", "930": "\ud83d\udd64", "10": "\ud83d\udd59", "1030": "\ud83d\udd65", "11": "\ud83d\udd5a", "1130": "\ud83d\udd66", "12": "\ud83d\udd5b", "1230": "\ud83d\udd67", "AIRPLANE": "\ud83d\udee9", "ALARM": "\u23f0", "AMBULANCE": "\ud83d\ude91", "ANCHOR": "\u2693", "ANGRY": "\ud83d\ude20", "ANGUISHED": "\ud83d\ude27", "ANT": "\ud83d\udc1c", "ANTENNA": "\ud83d\udce1", "APPLE": "\ud83c\udf4f", "APPLE2": "\ud83c\udf4e", "ATM": "\ud83c\udfe7", "ATOM": "\u269b", "BABYBOTTLE": "\ud83c\udf7c", "BAD:": "\ud83d\udc4e", "BANANA": "\ud83c\udf4c", "BANDAGE": "\ud83e\udd15", "BANK": "\ud83c\udfe6", "BATTERY": "\ud83d\udd0b", "BED": "\ud83d\udecf", "BEE": "\ud83d\udc1d", "BEER": "\ud83c\udf7a", "BELL": "\ud83d\udd14", "BELLOFF": "\ud83d\udd15", "BIRD": "\ud83d\udc26", "BLACKFLAG": "\ud83c\udff4", "BLUSH": "\ud83d\ude0a", "BOMB": "\ud83d\udca3", "BOOK": "\ud83d\udcd5", "BOOKMARK": "\ud83d\udd16", "BOOKS": "\ud83d\udcda", "BOW": "\ud83c\udff9", "BOWLING": "\ud83c\udfb3", "BRIEFCASE": "\ud83d\udcbc", "BROKEN": "\ud83d\udc94", "BUG": "\ud83d\udc1b", "BUILDING": "\ud83c\udfdb", "BUILDINGS": "\ud83c\udfd8", "BULB": "\ud83d\udca1", "BUS": "\ud83d\ude8c", "CACTUS": "\ud83c\udf35", "CALENDAR": "\ud83d\udcc5", "CAMEL": "\ud83d\udc2a", "CAMERA": "\ud83d\udcf7", "CANDLE": "\ud83d\udd6f", "CAR": "\ud83d\ude98", "CAROUSEL": "\ud83c\udfa0", "CASTLE": "\ud83c\udff0", "CATEYES": "\ud83d\ude3b", "CATJOY": "\ud83d\ude39", "CATMOUTH": "\ud83d\ude3a", "CATSMILE": "\ud83d\ude3c", "CD": "\ud83d\udcbf", "CHECK": "\u2714", "CHEQFLAG": "\ud83c\udfc1", "CHICK": "\ud83d\udc25", "CHICKEN": "\ud83d\udc14", "CHICKHEAD": "\ud83d\udc24", "CIRCLEBLACK": "\u26ab", "CIRCLEBLUE": "\ud83d\udd35", "CIRCLERED": "\ud83d\udd34", "CIRCLEWHITE": "\u26aa", "CIRCUS": "\ud83c\udfaa", "CLAPPER": "\ud83c\udfac", "CLAPPING": "\ud83d\udc4f", "CLIP": "\ud83d\udcce", "CLIPBOARD": "\ud83d\udccb", "CLOUD": "\ud83c\udf28", "CLOVER": "\ud83c\udf40", "CLOWN": "\ud83e\udd21", "COLDSWEAT": "\ud83d\ude13", "COLDSWEAT2": "\ud83d\ude30", "COMPRESS": "\ud83d\udddc", "CONFOUNDED": "\ud83d\ude16", "CONFUSED": "\ud83d\ude15", "CONSTRUCTION": "\ud83d\udea7", "CONTROL": "\ud83c\udf9b", "COOKIE": "\ud83c\udf6a", "COOKING": "\ud83c\udf73", "COOL": "\ud83d\ude0e", "COOLBOX": "\ud83c\udd92", "COPYRIGHT": "\u00a9", "CRANE": "\ud83c\udfd7", "CRAYON": "\ud83d\udd8d", "CREDITCARD": "\ud83d\udcb3", "CROSS": "\u2716", "CROSSBOX:": "\u274e", "CRY": "\ud83d\ude22", "CRYCAT": "\ud83d\ude3f", "CRYSTALBALL": "\ud83d\udd2e", "CUSTOMS": "\ud83d\udec3", "DELICIOUS": "\ud83d\ude0b", "DERELICT": "\ud83c\udfda", "DESKTOP": "\ud83d\udda5", "DIAMONDLB": "\ud83d\udd37", "DIAMONDLO": "\ud83d\udd36", "DIAMONDSB": "\ud83d\udd39", "DIAMONDSO": "\ud83d\udd38", "DICE": "\ud83c\udfb2", "DISAPPOINTED": "\ud83d\ude1e", "CRY2": "\ud83d\ude25", "DIVISION": "\u2797", "DIZZY": "\ud83d\ude35", "DOLLAR": "\ud83d\udcb5", "DOLLAR2": "\ud83d\udcb2", "DOWNARROW": "\u2b07", "DVD": "\ud83d\udcc0", "EJECT": "\u23cf", "ELEPHANT": "\ud83d\udc18", "EMAIL": "\ud83d\udce7", "ENVELOPE": "\ud83d\udce8", "ENVELOPE2": "\u2709", "ENVELOPE_DOWN": "\ud83d\udce9", "EURO": "\ud83d\udcb6", "EVIL": "\ud83d\ude08", "EXPRESSIONLESS": "\ud83d\ude11", "EYES": "\ud83d\udc40", "FACTORY": "\ud83c\udfed", "FAX": "\ud83d\udce0", "FEARFUL": "\ud83d\ude28", "FILEBOX": "\ud83d\uddc3", "FILECABINET": "\ud83d\uddc4", "FIRE": "\ud83d\udd25", "FIREENGINE": "\ud83d\ude92", "FIST": "\ud83d\udc4a", "FLOWER": "\ud83c\udf37", "FLOWER2": "\ud83c\udf38", "FLUSHED": "\ud83d\ude33", "FOLDER": "\ud83d\udcc1", "FOLDER2": "\ud83d\udcc2", "FREE": "\ud83c\udd93", "FROG": "\ud83d\udc38", "FROWN": "\ud83d\ude41", "GEAR": "\u2699", "GLOBE": "\ud83c\udf0d", "GLOWINGSTAR": "\ud83c\udf1f", "GOOD:": "\ud83d\udc4d", "GRIMACING": "\ud83d\ude2c", "GRIN": "\ud83d\ude00", "GRINNINGCAT": "\ud83d\ude38", "HALO": "\ud83d\ude07", "HAMMER": "\ud83d\udd28", "HAMSTER": "\ud83d\udc39", "HAND": "\u270b", "HANDDOWN": "\ud83d\udc47", "HANDLEFT": "\ud83d\udc48", "HANDRIGHT": "\ud83d\udc49", "HANDUP": "\ud83d\udc46", "HATCHING": "\ud83d\udc23", "HAZARD": "\u2623", "HEADPHONE": "\ud83c\udfa7", "HEARNOEVIL": "\ud83d\ude49", "HEARTBLUE": "\ud83d\udc99", "HEARTEYES": "\ud83d\ude0d", "HEARTGREEN": "\ud83d\udc9a", "HEARTYELLOW": "\ud83d\udc9b", "HELICOPTER": "\ud83d\ude81", "HERB": "\ud83c\udf3f", "HIGH_BRIGHTNESS": "\ud83d\udd06", "HIGHVOLTAGE": "\u26a1", "HIT": "\ud83c\udfaf", "HONEY": "\ud83c\udf6f", "HOT": "\ud83c\udf36", "HOURGLASS": "\u23f3", "HOUSE": "\ud83c\udfe0", "HUGGINGFACE": "\ud83e\udd17", "HUNDRED": "\ud83d\udcaf", "HUSHED": "\ud83d\ude2f", "ID": "\ud83c\udd94", "INBOX": "\ud83d\udce5", "INDEX": "\ud83d\uddc2", "JOY": "\ud83d\ude02", "KEY": "\ud83d\udd11", "KISS": "\ud83d\ude18", "KISS2": "\ud83d\ude17", "KISS3": "\ud83d\ude19", "KISS4": "\ud83d\ude1a", "KISSINGCAT": "\ud83d\ude3d", "KNIFE": "\ud83d\udd2a", "LABEL": "\ud83c\udff7", "LADYBIRD": "\ud83d\udc1e", "LANDING": "\ud83d\udeec", "LAPTOP": "\ud83d\udcbb", "LEFTARROW": "\u2b05", "LEMON": "\ud83c\udf4b", "LIGHTNINGCLOUD": "\ud83c\udf29", "LINK": "\ud83d\udd17", "LITTER": "\ud83d\udeae", "LOCK": "\ud83d\udd12", "LOLLIPOP": "\ud83c\udf6d", "LOUDSPEAKER": "\ud83d\udce2", "LOW_BRIGHTNESS": "\ud83d\udd05", "MAD": "\ud83d\ude1c", "MAGNIFYING_GLASS": "\ud83d\udd0d", "MASK": "\ud83d\ude37", "MEDAL": "\ud83c\udf96", "MEMO": "\ud83d\udcdd", "MIC": "\ud83c\udfa4", "MICROSCOPE": "\ud83d\udd2c", "MINUS": "\u2796", "MOBILE": "\ud83d\udcf1", "MONEY": "\ud83d\udcb0", "MONEYMOUTH": "\ud83e\udd11", "MONKEY": "\ud83d\udc35", "MOUSE": "\ud83d\udc2d", "MOUSE2": "\ud83d\udc01", "MOUTHLESS": "\ud83d\ude36", "MOVIE": "\ud83c\udfa5", "MUGS": "\ud83c\udf7b", "NERD": "\ud83e\udd13", "NEUTRAL": "\ud83d\ude10", "NEW": "\ud83c\udd95", "NOENTRY": "\ud83d\udeab", "NOTEBOOK": "\ud83d\udcd4", "NOTEPAD": "\ud83d\uddd2", "NUTANDBOLT": "\ud83d\udd29", "O": "\u2b55", "OFFICE": "\ud83c\udfe2", "OK": "\ud83c\udd97", "OKHAND": "\ud83d\udc4c", "OLDKEY": "\ud83d\udddd", "OPENLOCK": "\ud83d\udd13", "OPENMOUTH": "\ud83d\ude2e", "OUTBOX": "\ud83d\udce4", "PACKAGE": "\ud83d\udce6", "PAGE": "\ud83d\udcc4", "PAINTBRUSH": "\ud83d\udd8c", "PALETTE": "\ud83c\udfa8", "PANDA": "\ud83d\udc3c", "PASSPORT": "\ud83d\udec2", "PAWS": "\ud83d\udc3e", "PEN": "\ud83d\udd8a", "PEN2": "\ud83d\udd8b", "PENSIVE": "\ud83d\ude14", "PERFORMING": "\ud83c\udfad", "PHONE": "\ud83d\udcde", "PILL": "\ud83d\udc8a", "PING": "\u2757", "PLATE": "\ud83c\udf7d", "PLUG": "\ud83d\udd0c", "PLUS": "\u2795", "POLICE": "\ud83d\ude93", "POLICELIGHT": "\ud83d\udea8", "POSTOFFICE": "\ud83c\udfe4", "POUND": "\ud83d\udcb7", "POUTING": "\ud83d\ude21", "POUTINGCAT": "\ud83d\ude3e", "PRESENT": "\ud83c\udf81", "PRINTER": "\ud83d\udda8", "PROJECTOR": "\ud83d\udcfd", "PUSHPIN": "\ud83d\udccc", "QUESTION": "\u2753", "RABBIT": "\ud83d\udc30", "RADIOACTIVE": "\u2622", "RADIOBUTTON": "\ud83d\udd18", "RAINCLOUD": "\ud83c\udf27", "RAT": "\ud83d\udc00", "RECYCLE": "\u267b", "REGISTERED": "\u00ae", "RELIEVED": "\ud83d\ude0c", "ROBOT": "\ud83e\udd16", "ROCKET": "\ud83d\ude80", "ROLLING": "\ud83d\ude44", "ROOSTER": "\ud83d\udc13", "RULER": "\ud83d\udccf", "SATELLITE": "\ud83d\udef0", "SAVE": "\ud83d\udcbe", "SCHOOL": "\ud83c\udfeb", "SCISSORS": "\u2702", "SCREAMING": "\ud83d\ude31", "SCROLL": "\ud83d\udcdc", "SEAT": "\ud83d\udcba", "SEEDLING": "\ud83c\udf31", "SEENOEVIL": "\ud83d\ude48", "SHIELD": "\ud83d\udee1", "SHIP": "\ud83d\udea2", "SHOCKED": "\ud83d\ude32", "SHOWER": "\ud83d\udebf", "SLEEPING": "\ud83d\ude34", "SLEEPY": "\ud83d\ude2a", "SLIDER": "\ud83c\udf9a", "SLOT": "\ud83c\udfb0", "SMILE": "\ud83d\ude42", "SMILING": "\ud83d\ude03", "SMILINGCLOSEDEYES": "\ud83d\ude06", "SMILINGEYES": "\ud83d\ude04", "SMILINGSWEAT": "\ud83d\ude05", "SMIRK": "\ud83d\ude0f", "SNAIL": "\ud83d\udc0c", "SNAKE": "\ud83d\udc0d", "SOCCER": "\u26bd", "SOS": "\ud83c\udd98", "SPEAKER": "\ud83d\udd08", "SPEAKEROFF": "\ud83d\udd07", "SPEAKNOEVIL": "\ud83d\ude4a", "SPIDER": "\ud83d\udd77", "SPIDERWEB": "\ud83d\udd78", "STAR": "\u2b50", "STOP": "\u26d4", "STOPWATCH": "\u23f1", "SULK": "\ud83d\ude26", "SUNFLOWER": "\ud83c\udf3b", "SUNGLASSES": "\ud83d\udd76", "SYRINGE": "\ud83d\udc89", "TAKEOFF": "\ud83d\udeeb", "TAXI": "\ud83d\ude95", "TELESCOPE": "\ud83d\udd2d", "TEMPORATURE": "\ud83e\udd12", "TENNIS": "\ud83c\udfbe", "THERMOMETER": "\ud83c\udf21", "THINKING": "\ud83e\udd14", "THUNDERCLOUD": "\u26c8", "TICKBOX": "\u2705", "TICKET": "\ud83c\udf9f", "TIRED": "\ud83d\ude2b", "TOILET": "\ud83d\udebd", "TOMATO": "\ud83c\udf45", "TONGUE": "\ud83d\ude1b", "TOOLS": "\ud83d\udee0", "TORCH": "\ud83d\udd26", "TORNADO": "\ud83c\udf2a", "TOUNG2": "\ud83d\ude1d", "TRADEMARK": "\u2122", "TRAFFICLIGHT": "\ud83d\udea6", "TRASH": "\ud83d\uddd1", "TREE": "\ud83c\udf32", "TRIANGLE_LEFT": "\u25c0", "TRIANGLE_RIGHT": "\u25b6", "TRIANGLEDOWN": "\ud83d\udd3b", "TRIANGLEUP": "\ud83d\udd3a", "TRIANGULARFLAG": "\ud83d\udea9", "TROPHY": "\ud83c\udfc6", "TRUCK": "\ud83d\ude9a", "TRUMPET": "\ud83c\udfba", "TURKEY": "\ud83e\udd83", "TURTLE": "\ud83d\udc22", "UMBRELLA": "\u26f1", "UNAMUSED": "\ud83d\ude12", "UPARROW": "\u2b06", "UPSIDEDOWN": "\ud83d\ude43", "WARNING": "\u26a0", "WATCH": "\u231a", "WAVING": "\ud83d\udc4b", "WEARY": "\ud83d\ude29", "WEARYCAT": "\ud83d\ude40", "WHITEFLAG": "\ud83c\udff3", "WINEGLASS": "\ud83c\udf77", "WINK": "\ud83d\ude09", "WORRIED": "\ud83d\ude1f", "WRENCH": "\ud83d\udd27", "X": "\u274c", "YEN": "\ud83d\udcb4", "ZIPPERFACE": "\ud83e\udd10", "UNDEFINED": "", "": "" | |
}; | |
statuses = { | |
/* OFF */ | |
/* FATAL */ | |
F: 'FATAL', | |
/* ERROR */ | |
B: 'BUG', | |
C: 'CRITICAL', | |
E: 'ERROR', | |
/* WARN */ | |
W: 'WARNING', | |
/* INFO */ | |
I: 'INFO', | |
IM: 'IMPORTANT', | |
/* DEBUG */ | |
D: 'DEBUG', | |
L: 'LOG', | |
CO: 'CONSTANT', | |
FU: 'FUNCTION', | |
R: 'RETURN', | |
V: 'VARIABLE', | |
S: 'STACK', | |
/* TRACE */ | |
RE: 'RESULT', | |
ST: 'STOPPER', | |
TI: 'TIMER', | |
T: 'TRACE' | |
/* ALL */ | |
}; | |
LOG_LEVEL = { | |
NONE: 7, | |
OFF: 7, | |
FATAL: 6, | |
ERROR: 5, | |
WARN: 4, | |
INFO: 3, | |
UNDEFINED: 2, | |
'': 2, | |
DEFAULT: 2, // Should be the same as UNDEFINED and '' so if desired change them all; | |
DEBUG: 2, | |
TRACE: 1, | |
ON: 0, | |
ALL: 0, | |
}; | |
LOG_STATUS = { | |
OFF: LOG_LEVEL.OFF, | |
NONE: LOG_LEVEL.OFF, | |
NO: LOG_LEVEL.OFF, | |
NOPE: LOG_LEVEL.OFF, | |
FALSE: LOG_LEVEL.OFF, | |
FATAL: LOG_LEVEL.FATAL, | |
BUG: LOG_LEVEL.ERROR, | |
CRITICAL: LOG_LEVEL.ERROR, | |
ERROR: LOG_LEVEL.ERROR, | |
WARNING: LOG_LEVEL.WARN, | |
INFO: LOG_LEVEL.INFO, | |
IMPORTANT: LOG_LEVEL.INFO, | |
DEBUG: LOG_LEVEL.DEBUG, | |
LOG: LOG_LEVEL.DEBUG, | |
STACK: LOG_LEVEL.DEBUG, | |
CONSTANT: LOG_LEVEL.DEBUG, | |
FUNCTION: LOG_LEVEL.DEBUG, | |
VARIABLE: LOG_LEVEL.DEBUG, | |
RETURN: LOG_LEVEL.DEBUG, | |
RESULT: LOG_LEVEL.TRACE, | |
STOPPER: LOG_LEVEL.TRACE, | |
TIMER: LOG_LEVEL.TRACE, | |
TRACE: LOG_LEVEL.TRACE, | |
ALL: LOG_LEVEL.ALL, | |
YES: LOG_LEVEL.ALL, | |
YEP: LOG_LEVEL.ALL, | |
TRUE: LOG_LEVEL.ALL | |
}; | |
var logFile, logFolder; | |
/** | |
* [LOG] This is our main hero function that's created by calling the LogFactory | |
* i.e. var log = new LogFactory(); | |
* See the annotations to the LogFactory function for the default values | |
* Once create we use log('Hello'); log('Hello', 'i'); or log('Hello', 'i' ,'happy'); | |
* It has many methods and properties. | |
* Methods include : LOG.setFile(), LOG.setStatus(), LOG.setLevel(), LOG.cookie(), | |
* LOG.args(), LOG.stack(), LOG.values(), LOG.sumDiff(), | |
* LOG.stopper(), LOG.start(), LOG.stop(), log.setCount(), | |
* LOG.get(), LOG.openFolder(), LOG.execute(), LOG.show(), | |
* LOG.close(), LOG.file(), LOG.reset(), | |
* Properties include: LOG.write, LOG.store, LOG.level, LOG.status, LOG.count | |
* @param {String} message 'say something here' | |
* @param {String} status [Optional] (takes abbreviations) (Case insensitive) DEFAULT value LOG.status | |
* The levels of the statuses are explained by the annotation to the LOG.setLevel() function | |
* Only logs entries with statuses equal or higher than the LOG.level value will be logged | |
* @param {[String]} icon [Optional] (Adds emojis to the beginning of the message) | |
* (Case insensitive) Valid values are listed https://creative-scripts.com/logging-with-a-smile/#LOG | |
* See also the object near to the beginning of the LogFactory function 'icons = {'' | |
* | |
*/ | |
var LOG = function(message, status, icon) { | |
if (LOG.level !== LOG_LEVEL.OFF && (LOG.write || LOG.store) && LOG.arguments.length) return LOG.addMessage(message, status, icon); | |
}; | |
/** | |
* [LOG.logDecodeLevel description] | |
* @param {String, Integer, Boole} level (Case insensitive) valid values include: | |
* 7, NONE, OFF, NONE, NOPE, FALSE, | |
* 6, FATAL, FA, | |
* 5, ERROR, BUG, CRITICAL, e, b, c, | |
* 4, WARN, WARNING, w, | |
* 3, INFO, IMPORTANT, ON, i, im, | |
* 2, DEBUG, LOG, STACK, CONSTANT, FUNCTION, VARIABLE, RETURN, d, l, co, f, r, v, s, | |
* 1, TRACE, RESULT, STOPPER, TIMER, tr, re, st, ti, t, | |
* 0, ALL,YES, YEP, TRUE | |
* @return {Integer} The integer value of the level string | |
*/ | |
LOG.logDecodeLevel = function(level) { | |
// accept integers | |
if (level == ~~level) return Math.abs(level); | |
var lev; | |
level += ''; | |
level = level.toUpperCase(); | |
// accept abbreviations | |
if (level in statuses) { | |
level = statuses[level]; | |
} | |
// accept generic levels like WARN etc. stored in the LOG_LEVEL object | |
lev = LOG_LEVEL[level]; | |
if (lev !== undefined) return lev; | |
// accept level by status like CONSTANT stored in LOG_STATUS object | |
lev = LOG_STATUS[level]; | |
if (lev !== undefined) return lev; | |
// we did try (and fail) so resort to default level | |
return LOG_LEVEL.DEFAULT; | |
}; | |
///////////////////////////////// | |
// Set some defaults if needed // | |
///////////////////////////////// | |
LOG.write = write; | |
LOG.store = store; | |
LOG.level = LOG.logDecodeLevel(level); | |
LOG.status = defaultStatus; | |
/** | |
* See annotations to main LOG functions | |
* Use LOG(message, status, icon) when calling from a created log factory i.e. | |
* var log = new LogFactory(); | |
* use log('Hi'); and not log.addMessage('Hi'); | |
*/ | |
LOG.addMessage = function(message, status, icon) { | |
var date = new Date(), | |
count, bool, logStatus; | |
if (status && status.constructor.name === 'String') { | |
status = status.toUpperCase(); | |
status = statuses[status] || status; | |
} else status = LOG.status; | |
///////////////////////////////////////////////////////////// | |
// Check the status is above or equal to the set LOG.level // | |
///////////////////////////////////////////////////////////// | |
logStatus = LOG_STATUS[status] || LOG_STATUS.ALL; | |
if (logStatus < LOG.level) return; | |
////////////////////////////////////////////////////// | |
// If the LOG status is appropriate then log it <span class="wp-font-emots-emo-happy"></span> // | |
////////////////////////////////////////////////////// | |
date = ' \t[' + date + ' ' + date.getMilliseconds() + 'ms]'; | |
status = '[' + status + '] '; | |
if (status.length < 11) status = (status + ' ').substr(0, 11); | |
if (icon) { | |
icon = ('' + icon).toUpperCase(); | |
icon = (icon in icons && icons[icon]) || ''; | |
} else { | |
icon = ''; | |
} | |
if (LOG.count !== ~~LOG.count) { | |
LOG.count = 1; | |
} | |
count = (LOG.count > 999) ? '[' + LOG.count + '] ' : (' [' + LOG.count + '] ').slice(-7); | |
message = count + status + icon + (message instanceof Object ? message.toSource() : message) + date; | |
if (LOG.store) { | |
stack.push(message); | |
} | |
if (LOG.write) { | |
bool = file && file.writable && logFile.writeln(message); | |
// if the file could not be written to try again | |
// it could be that it was closed or | |
// was not created because LOG.write was set to false and this is the first write attempt after setting to true. | |
if (!bool) { | |
file.writable = true; | |
LOG.setFile(logFile); | |
logFile.writeln(message); | |
} | |
} | |
LOG.count++; | |
return true; | |
}; | |
/** | |
* [logNewFile description] Internal function for creating / preparing the log and cookie files | |
* When used for the log file it sets the logFile and logFolder properties | |
* @param {File Object} file The log or cookie file | |
* @param {Boolean} isCookie [Optional] Whether or not the file is a cookie file see annotation by the LOG.writeFile() method | |
* Default value false; | |
* @param {Boolean} overwrite [Optional] Whether or not to overwrite the value stored in the COOKIE file | |
* Default value true; has no effect unless isCookie is set to true | |
* @return {File (or Boolean)} The file. if isCookie is set to true and the file could not be opened then false is returned | |
*/ | |
var logNewFile = function(file, isCookie, overwrite) { | |
file.encoding = 'UTF-8'; // Might be a good idea to add a '\uFEFF' BOM character at the beginning of the file if it's not there | |
file.lineFeed = ($.os[0] == 'M') ? 'Macintosh' : ' Windows'; | |
if (isCookie) return file.open(overwrite ? 'w' : 'e') && file; | |
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
// Can't use readonly on non existent files, so we made up this. // | |
// The point of using the writable flag is so that the log file can be setup without being created // | |
// a practical use of this would be if we only want the log file to be created if a certain trigger happens (like and error) // | |
// if the stack option is set then the log file can retrospectively save a range of previous logs. // | |
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
file.writable = LOG.write; | |
logFile = file; | |
logFolder = file.parent; | |
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
// if LOG.write is set to false then we can return true no matter what // | |
// if the LOG.write is later set to true then the setFile function will be called on the next log call and thus call this function // | |
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
if (continuing) { | |
LOG.count = LOG.setCount(file); | |
} | |
return (!LOG.write && file || (file.open('a') && file)); | |
}; | |
/** | |
* [setFile description] Used for setting the log file | |
* When used for the log file it sets the logFile and logFolder properties | |
* @param {File Object or String} file The log or cookie file | |
* @param {Boolean} isCookie [Optional] Whether or not the file is a cookie file see annotation by the LOG.writeFile() method | |
* Default value false; | |
* @param {Boolean} overwrite [Optional] Whether or not to overwrite the value stored in the COOKIE file | |
* Default value true; has no effect unless isCookie is set to true | |
* @return {File (or Boolean)} The file. if isCookie is set to true and the file could not be opened then false is returned | |
*/ | |
LOG.setFile = function(file, isCookie, overwrite) { | |
// the file can be accessed using log.File | |
var bool, folder, fileName, suffix, newFileName, f, d, safeFileName; | |
d = new Date(); | |
// For ESTK Get the file name from the $.stack, this works from non-saved files $.fileName doesn't | |
// For external runners (like Sublime Text) use $.fileName in cases that the stack returns a number | |
// Such as when the log factory is included using #include | |
f = $.stack.split("\n")[0].replace(/^\[\(?/, '').replace(/\)?\]$/, ''); | |
if (f == ~~f) { | |
f = $.fileName.replace(/[^\/]+\//g, ''); | |
} | |
safeFileName = File.encode( | |
(isCookie ? '/COOKIE_' : '/LOG_') + | |
f.replace(/^\//, '') + '_' + (1900 + d.getYear()) + ('' + d).replace(/... (...) (..).+/, '_$1_$2') + | |
(isCookie ? '.txt' : '.log') | |
); | |
if (file && file.constructor.name == 'String') { | |
file = (file.match('/')) ? new File(file) : new File((logFolder || Folder.temp) + '/' + file); // if the file contains a folder path then use that folder path if it just contains a file name then use the logFolder and if the logFolder is undefined then use the temp folder | |
} | |
if (file instanceof File) { | |
// for some reason the file cannot be written so will try write the log using the same folder and different but similar file name. | |
folder = file.parent; | |
bool = folder.exists || folder.create(); | |
if (!bool) folder = Folder.temp; | |
fileName = File.decode(file.name); | |
suffix = fileName.match(/\.[^.]+$/); | |
suffix = suffix ? suffix[0] : ''; | |
fileName = '/' + fileName; | |
newFileName = fileName.replace(/\.[^.]+$/, '') + '_' + (+(new Date()) + suffix); | |
f = logNewFile(file, isCookie, overwrite); | |
if (f) return f; // try open the file as is | |
f = logNewFile(new File(folder + newFileName), isCookie, overwrite); | |
if (f) return f; // try first to keep the file folder and different but similar file name | |
f = logNewFile(new File(folder + safeFileName), isCookie, overwrite); | |
if (f) return f; // nope, so try write to the folder with a safe file name. | |
if (folder != Folder.temp) { | |
f = logNewFile(new File(Folder.temp + fileName), isCookie, overwrite); | |
if (f) return f; // nope so try again but use the temp folder | |
// LAST ATTEMPT, IF THIS FAILS IT MEANS TROUBLE (but you can't say we didn't try) | |
// p.s. It will only fail if you have very major permissions problems or your disk is full | |
f = logNewFile(new File(Folder.temp + safeFileName), isCookie, overwrite); | |
// NO MATTER WHAT !! | |
return f || new File(Folder.temp + safeFileName); | |
} | |
} | |
return LOG.setFile( | |
((logFile && !isCookie) ? new File(logFile) : new File(Folder.temp + safeFileName)), | |
isCookie, | |
overwrite | |
); // no string or file given so use the file stored in logFile if there is and if not use the temp folder with a safe name. | |
}; | |
/** | |
* [setCount] | |
* @param file {File Object, String of full file path, Integer, undefined} [OPTIONAL] | |
* Either the path of the log file to from which to continue the log count. | |
* Default value is the path to the current log file. | |
* Or an integer to set the current log count. | |
* By default the log count resets to 1 each time a new LogFactory is created | |
* If this function is called either on the creation of the LogFactory | |
* var log = new LogFactory({file: 'myLog.log', continuing: true}); | |
* or by calling it directly log.setCount(); then if the log file exists | |
* the log count will not automatically reset to 1 | |
* rather it will continue from the last entry in the log | |
* The function is not 100% reliable but close enough. | |
* To make it 100% fail proof would cost too much resources than it's value | |
*/ | |
LOG.setCount = function(file) { | |
if (~~file === file) { // files really an integer and not a file | |
LOG.count = file; | |
return LOG.count; | |
} | |
if (file === undefined) { | |
file = logFile; | |
} | |
if (file && file.constructor === String) { | |
file = new File(file); | |
} | |
var logNumbers, contents; | |
if (!file.length || !file.exists) { | |
LOG.count = 1; | |
return 1; | |
} | |
file.open('r'); | |
file.encoding = 'utf-8'; | |
// The log could be very long and we wouldn't want to read it all | |
// So we'll just check the last 10000 characters for a line number | |
// In theory the last log entry could be really log so if the below test fails we'll increase the range | |
file.seek(10000, 2); | |
contents = '\n' + file.read(); | |
// this will match all line number entries | |
// it could match non line number entries but it's quite unlikely and will have to do! | |
logNumbers = contents.match(/\n {0,3}\[\d+\] \[\w+\] +/g); | |
if (logNumbers) { | |
logNumbers = +logNumbers[logNumbers.length - 1].match(/\d+/) + 1; | |
file.close(); | |
LOG.count = logNumbers; | |
return logNumbers; | |
} | |
// if we get here it means we didn't find a line number yet | |
// if we checked the whole file already then we know to start at 1 | |
if (file.length < 10001) { | |
file.close(); | |
LOG.count = 1; | |
return 1; | |
} | |
// if we get here it means we didn't find a line number yet | |
// we didn't check the whole file yet so we'll increase the range | |
// we'll do up to about 10MB, if the last log entry is longer than that then this will fail | |
// in such a case the log count will start again at 1 | |
// if you don't like that then you can just use file.seek(0); but it's probably not a good idea | |
file.seek(10000000, 2); | |
contents = '\n' + file.read(); | |
// this will match all line number entries | |
// it could match non line number entries but it's quite unlikely and will have to do! | |
logNumbers = contents.match(/\n {0,3}\[\d+\] \[\w+\] +/g); | |
if (logNumbers) { | |
logNumbers = +logNumbers[logNumbers.length - 1].match(/\d+/) + 1; | |
file.close(); | |
LOG.count = logNumbers; | |
return logNumbers; | |
} | |
// if we get here it means we didn't find a line number yet | |
file.close(); | |
LOG.count = 1; | |
return 1; | |
}; | |
/** | |
* [setLevel description] Used for setting the LOG.level | |
* @param {String, Integer, Boole} level Takes any of the values described in the logDecodeLevel function above | |
* There are 8 levels for 0 to 7, any of the below values may be used in setting the level by the log.setLevel() and the new LogFactory() methods. | |
* 7, NONE, OFF, NONE, NOPE, FALSE | |
* 6, FATAL, FA | |
* 5, ERROR, BUG, CRITICAL, e, b, c | |
* 4, WARN, WARNING, w | |
* 3, INFO, IMPORTANT, ON, i, im | |
* 2, DEBUG, LOG, STACK, CONSTANT, FUNCTION, VARIABLE, RETURN, d, l, co, f, r, v, s | |
* 1, TRACE, RESULT, STOPPER, TIMER, tr, re, st, ti, t | |
* 0, ALL,YES, YEP, TRUE | |
* Only logs whose statuses are of equal or higher level than the level given here will be logged | |
* If the log level was set to 'error': | |
* log.setLevel('e'); | |
* log('Be Careful', 'w'); => diddlysquat the WARNING [4] level doesn't meet our minimum error [5] level | |
* log('De2ad', 'fa'); // => [1] [FATAL] Dead [Sun Mar 05 2017 10:05:02 GMT+0200 562ms] | |
* @returns {Integer} the set log level | |
*/ | |
LOG.setLevel = function(level) { | |
LOG.level = LOG.logDecodeLevel(level); | |
return LOG.level; | |
}; | |
/** | |
* setStatus Sets the default status to be assigned then the log function is called without specifying a status | |
* @param {Sting} status Accepts the following abbreviations, | |
FA: 'FATAL', | |
B: 'BUG', C: 'CRITICAL', E: 'ERROR', | |
W: 'WARNING', | |
I: 'INFO', IM: 'IMPORTANT', | |
D: 'DEBUG', L: 'LOG', CO: 'CONSTANT', F: 'FUNCTION', R: 'RETURN', V: 'VARIABLE', S: 'STACK', | |
RE: 'RESULT', ST: 'STOPPER', TI: 'TIMER', T: 'TRACE' | |
These recognized statuses are assigned levels as described in the LOG.setLevel method above | |
Custom level 0 statuses can be used | |
*/ | |
LOG.setStatus = function(status) { | |
status = ('' + status).toUpperCase(); | |
LOG.status = statuses[status] || status; | |
return LOG.status; | |
}; | |
/** | |
* [cookie description] For getting and setting a level to a cookie file | |
* A typical usage of the log.cookie method would be in a distributed production script. | |
* Normally one wouldn't want the log to be automatically created, | |
* instead one would want a method for the user to activate the logging by either | |
* manually changing the contents of the log cookie or by using a provided UI option. | |
* @param {File Object or String} file - The file path of the log cookie. | |
* The same rules apply for the file cookie file name as in the log.setFile() method, | |
* as such the file name and path might not be the expected one. | |
* The actual cookie file path can be extracted from the object returned by the method. | |
* [Optional - default: A cookie file will be created in the temporary folder and called | |
* COOKIE_Script Name_Year_Month_Date.txt for example COOKIE_Script2_2017_Feb_21.txt | |
* effectively leaving out the file name creates a temporary cookie that will be valid until midnight] | |
* @param {String or Integer} level - accepts the values that the setLevel() method does, see there | |
* @param {Boolean} overwrite - [Optional - default: false] if true then will overwrite any existing value in the cookie | |
* @param {Boolean} setLevel - [Optional - default: true] If the log level should be set to the cookies level value | |
* @return {Object} {path: The actual path of the file cookie, level: the integer value of the level} | |
* @Note This method also accepts use an object in the form of | |
* var myCookie = log.cookie({file: 'myCookie.txt', level: 'error', overwrite: true, setLevel: true}); | |
*/ | |
LOG.cookie = function(file, level, overwrite, setLevel) { | |
var log, cookie; | |
if (!file) { | |
file = { | |
file: file | |
}; | |
} | |
// allow for objects and argument lists | |
if (file && (file.constructor === String || file.constructor === File)) { | |
file = { | |
file: file | |
}; | |
} | |
log = file; | |
if (log.level === undefined) { | |
log.level = (level !== undefined) ? level : 'NONE'; | |
} | |
if (log.overwrite === undefined) { | |
log.overwrite = (overwrite !== undefined) ? overwrite : false; | |
} | |
if (log.setLevel === undefined) { | |
log.setLevel = (setLevel !== undefined) ? setLevel : true; | |
} | |
setLevel = log.setLevel; | |
overwrite = log.overwrite; | |
level = log.level; | |
file = log.file; | |
file = LOG.setFile(file, true, overwrite); | |
if (overwrite) { | |
file.write(level); | |
} else { | |
cookie = file.read(); | |
if (cookie.length) { | |
level = cookie; | |
} else { | |
file.write(level); | |
} | |
} | |
file.close(); | |
if (setLevel) { | |
LOG.setLevel(level); | |
} | |
return { | |
path: file, | |
level: level | |
}; | |
}; | |
/** | |
* [args] Logs a well formatted report of a function with all it arguments values and names if named | |
* also shows the function name (if it has one) and more info | |
* Note: The info is assigned a FUNCTION status which has a level of 2 | |
* If the LOG.level is set to 3 or above then the LOG.args() method will not do anything | |
* @param {Function arguments} args The arguments to be parsed | |
* Typically one would place the line log.args(arguments); // no quote around arguments | |
* inside a function to get a snapshot of the arguments values | |
* @param {Function} funct [Optional] For es5 strict mode compliance one can pass the function | |
* (whose arguments one is processing) to this argument (currently not needed) | |
* @param {Integer} line [Optional] The line number of the function caller. | |
* can use log.args(arguments, $.line); | |
* @return {String} The report produced | |
*/ | |
LOG.args = function(args, funct, line) { | |
// IF the debug level is not set to the DEBUG / FUNCTION level then return without waisting resources | |
if (LOG.level > LOG_STATUS.FUNCTION) return; | |
// The 5th edition of ECMAScript (ES5) forbids use of arguments.callee() in strict mode. | |
// So if you want to play safe for that then pass both the arguments and the function | |
// If the functions anonymous then you could have difficulty passing the function | |
// A non-airtight check if the args being feed are function arguments | |
// This function might be quite resource greedy | |
if (!(args && ('' + args.constructor).replace(/\s+/g, '') === 'functionObject(){[nativecode]}')) return; | |
// The general regex method for getting the arguments names appears several places on the web | |
// I can't remember were I saw it and can't give credits for it | |
// This particular implementation of it here is made by me Trevor https://creative-scripts.com | |
if (!LOG.args.STRIP_COMMENTS) { | |
LOG.args.STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; | |
} | |
if (!LOG.args.ARGUMENT_NAMES) { | |
LOG.args.ARGUMENT_NAMES = /([^\s,]+)/g; | |
} | |
if (!LOG.args.OUTER_BRACKETS) { | |
LOG.args.OUTER_BRACKETS = /^\((.+)?\)$/; | |
} | |
if (!LOG.args.NEW_SOMETHING) { | |
LOG.args.NEW_SOMETHING = /^new \w+\((.+)?\)$/; | |
} | |
var functionString, argumentNames, stackInfo, report, | |
functionName, arg, argsL, n, argName, argValue, argsTotal; | |
// The funct value might actually be the line number as it's an optional value so | |
if (funct === ~~funct) { | |
line = funct; | |
} | |
if (!(funct instanceof Function)) { | |
funct = args.callee; | |
} | |
if (!(funct instanceof Function)) return; | |
functionName = funct.name; | |
functionString = ('' + funct).replace(LOG.args.STRIP_COMMENTS, ''); | |
argumentNames = functionString.slice(functionString.indexOf('(') + 1, functionString.indexOf(')')).match(LOG.args.ARGUMENT_NAMES); | |
// could be there's no matches so | |
argumentNames = argumentNames || []; | |
report = []; | |
report.push('--------------'); | |
report.push('Function Data:'); | |
report.push('--------------'); | |
report.push('Function Name: ' + functionName); | |
argsL = args.length; | |
// could be there some useful info in the $.stack | |
stackInfo = $.stack.split(/[\n\r]/); | |
stackInfo.pop(); | |
stackInfo = stackInfo.join('\n '); | |
report.push('Call stack: ' + stackInfo); | |
if (line) { | |
report.push('Function Line around: ' + line); | |
} | |
report.push('Arguments Provided: ' + argsL); | |
report.push('Named Arguments: ' + argumentNames.length); | |
if (argumentNames.length) { | |
report.push('Arguments Names: ' + argumentNames.join(', ')); | |
} | |
if (argsL) { | |
report.push('----------------'); | |
report.push('Argument Values:'); | |
report.push('----------------'); | |
} | |
argsTotal = Math.max(argsL, argumentNames.length); | |
for (n = 0; n < argsTotal; n++) { | |
argName = argumentNames[n]; | |
arg = args[n]; | |
if (n >= argsL) { | |
argValue = 'NO VALUE PROVIDED'; | |
} else if (arg === undefined) { | |
argValue = 'undefined'; | |
} else if (arg === null) { | |
argValue = 'null'; | |
} else { | |
argValue = arg.toSource().replace(LOG.args.OUTER_BRACKETS, '$1').replace(LOG.args.NEW_SOMETHING, '$1'); | |
} | |
report.push((argName ? argName : 'arguments[' + n + ']') + ': ' + argValue); | |
} | |
report.push(''); | |
report = report.join('\n '); | |
LOG(report, 'f'); | |
return report; | |
}; | |
/** | |
* [stack] Logs the current call stack info formated | |
* @param {Boolean} reverse [Optional] if true the last function called in the call stack is at the top | |
* Default false | |
* @return {String} The formatted stack (function names and argument values but not names) | |
* Can use the more detailed log.args(arguments); method for more detailed info | |
*/ | |
LOG.stack = function(reverse) { | |
var st = $.stack.split('\n'); | |
st.pop(); | |
st.pop(); | |
if (reverse) { | |
st.reverse(); | |
} | |
return LOG(st.join('\n '), 's'); | |
}; | |
/** | |
* [values Logs a neatly presented list of values of a array or object] | |
* @param {Array or Object} values [description] | |
* @return {String} The log produced | |
*/ | |
LOG.values = function(values) { | |
var n, value, map = []; | |
if (!(values instanceof Object || values instanceof Array)) { | |
return; | |
} | |
if (!LOG.values.OUTER_BRACKETS) { | |
LOG.values.OUTER_BRACKETS = /^\((.+)?\)$/; | |
} | |
if (!LOG.values.NEW_SOMETHING) { | |
LOG.values.NEW_SOMETHING = /^new \w+\((.+)?\)$/; | |
} | |
for (n in values) { | |
try { | |
value = values[n]; | |
if (value === undefined) { | |
value = 'undefined'; | |
} else if (value === null) { | |
value = 'null'; | |
} else { | |
value = value.toSource().replace(LOG.values.OUTER_BRACKETS, '$1').replace(LOG.values.NEW_SOMETHING, '$1'); | |
} | |
} catch (e) { | |
value = '\uD83D\uDEAB ' + e; | |
} | |
map.push(n + ': ' + value); | |
} | |
if (map.length) { | |
map = map.join('\n ') + '\n '; | |
return LOG(map, 'v'); | |
} | |
}; | |
/** | |
* reset For reseting the count and log stack or all the log values | |
* @param {Boolean} all [Optional] whether only the stack and count are reset or if also additional properties | |
* @return void | |
* Probably this function need some playing with to reset missed out properties, enjoy! | |
*/ | |
LOG.reset = function(all) { | |
stack.length = 0; | |
LOG.count = 1; | |
if (all !== false) { | |
if (logFile instanceof File) { | |
logFile.close(); | |
} | |
logFile = LOG.store = LOG.writeToFile = undefined; | |
LOG.write = true; | |
logFolder = Folder.temp; | |
logTime = new Date(); | |
logPoint = 'After Log Reset'; | |
} | |
}; | |
/** | |
* stopper description log the time elapsed between one log.stopper() call and the next log.stopper call | |
* @param {String or false} message [Optional] if false then just the time will be recored with no logging | |
* Otherwise the timing will be logged | |
* @return {String} The log produced | |
*/ | |
LOG.stopper = function(message) { | |
var newLogTime, t, m, newLogPoint; | |
newLogTime = new Date(); | |
newLogPoint = (LOG.count !== undefined) ? 'LOG#' + LOG.count : 'BEFORE LOG#1'; | |
LOG.time = t = newLogTime - logTime; | |
if (message === false) { | |
return; | |
} | |
message = message || 'Stopper start point'; | |
t = LOG.prettyTime(t); | |
m = message + '\n ' + | |
'From ' + logPoint + ' to ' + newLogPoint + | |
' took ' + t + ' Starting ' + logTime + ' ' + logTime.getMilliseconds() + 'ms' + | |
' Ending ' + newLogTime + ' ' + newLogTime.getMilliseconds() + 'ms'; | |
LOG(m, 'st'); | |
logPoint = newLogPoint; | |
logTime = newLogTime; | |
return m; | |
}; | |
/** | |
* start sets up the start times to be logged when calling the LOG.stop() method. | |
* @param {String} message [Optional] | |
* @return {String} The timing report | |
*/ | |
LOG.start = function(message) { | |
var t = new Date(); | |
times.push([t, (message !== undefined) ? message + '' : '']); | |
}; | |
/** | |
* stop logs times from the paired LOG.start registered times. | |
* Differs from the stopper method in that the times act as brackets | |
* log.start('point 1'); | |
* log.start('point 2'); | |
* log.stop('This will give the time from point 2'); | |
* log.stop('This will give the time from point 1'); | |
* @param {String} message [Optional] | |
* @return {String} The timing report | |
*/ | |
LOG.stop = function(message) { | |
if (!times.length) return; | |
message = (message) ? message + ' ' : ''; | |
var nt, startLog, ot, om, td, m; | |
nt = new Date(); | |
// startLog = times[0]; | |
startLog = times.pop(); | |
// times.splice(0, 1); | |
ot = startLog[0]; | |
om = startLog[1]; | |
td = nt - ot; | |
if (om.length) { | |
om += ' '; | |
} | |
m = om + 'STARTED [' + ot + ' ' + ot.getMilliseconds() + 'ms]\n ' + message + 'FINISHED [' + nt + ' ' + nt.getMilliseconds() + 'ms]\n TOTAL TIME [' + LOG.prettyTime(td) + ']'; | |
LOG(m, 'ti'); | |
return m; | |
}; | |
/** | |
* [prettyTime] Represents ms times in the more friendly hours, minutes, seconds, ms format | |
* @param {Integer} t the amount of ms | |
* @return {Sting} Formatted time 3 hours 25 minutes 45 seconds & 678ms is more meaningful than 12345678ms (to me anyway!) | |
*/ | |
LOG.prettyTime = function(t) { | |
var h, m, s, ms; | |
h = Math.floor(t / 3600000); | |
m = Math.floor((t % 3600000) / 60000); | |
s = Math.floor((t % 60000) / 1000); | |
ms = t % 1000; | |
t = (!t) ? '<1ms' : ((h) ? h + ' hours ' : '') + ((m) ? m + ' minutes ' : '') + ((s) ? s + ' seconds ' : '') + ((ms && (h || m || s)) ? ' & ' : '') + ((ms) ? ms + 'ms' : ''); | |
return t; | |
}; | |
/** | |
* [get description] alerts the selected lines i.e. | |
* log.alert(3,6,-1); will alert (1 alert not 3) the 3rd, 6th and last log entry in the stack | |
* Note: by default the stack is set not to record so unless the stack has been set either | |
* at the formation of the LogFactory or by using log.store = true; | |
* Note if the log.store was not set at the same time as the log.write then the numbers won't be in sync | |
* @return {undefined} | |
*/ | |
LOG.get = function() { | |
if (!stack.length) return 'THE LOG IS NOT SET TO STORE'; | |
var a = fetchLogLines(arguments); | |
return a ? '\n' + a.join('\n') : 'NO LOGS AVAILABLE'; | |
}; | |
/** [fetchLogLines] interprets the arguments of the LOG.alert, LOG.wl and LOG.range methods */ | |
var fetchLogLines = function() { | |
var args = arguments[0]; | |
if (!args.length) return stack; // will return the whole log array to be outputted | |
var c, n, l, a = [], | |
ln, start, end, j, sl; | |
l = args.length; | |
sl = stack.length - 1; | |
n = 0; | |
for (c = 0; c < l; c++) { | |
ln = args[c]; | |
if (~~ln === ln) { // the argument is an integer | |
ln = (0 > ln) ? sl + ln + 1 : ln - 1; | |
if (ln >= 0 && ln <= sl) a[n++] = stack[ln]; | |
} else if (ln instanceof Array && ln.length === 2) { | |
start = ln[0]; | |
end = ln[1]; | |
if (!(~~start === start && ~~end === end)) continue; // the arguments are invalid so continue | |
// ln = (0 > ln) ? sl + ln + 1 : ln - 1; | |
start = (0 > start) ? sl + start + 1 : start - 1; | |
end = (0 > end) ? sl + end + 1 : end - 1; | |
start = Math.max(Math.min(sl, start), 0); | |
end = Math.min(Math.max(end, 0), sl); | |
if (start <= end) | |
for (j = start; j <= end; j++) a[n++] = stack[j]; | |
else | |
for (j = start; j >= end; j--) a[n++] = stack[j]; // go backwards | |
} | |
} | |
return (n) ? a : false; | |
}; | |
// to allow external access to logFile | |
LOG.file = function() { | |
return logFile; | |
}; | |
/** [openFolder opens the containing folder of the log file if there is one] */ | |
LOG.openFolder = function() { | |
if (logFolder) return logFolder.execute(); | |
}; | |
/** [openFolder opens the log file if there is one with the default application for opening the file suffix you picked for the log] */ | |
LOG.show = LOG.execute = function() { | |
if (logFile) return logFile.execute(); | |
}; | |
/** [close Closes the log file] */ | |
LOG.close = function() { | |
if (logFile) return logFile.close(); | |
}; | |
LOG.setFile(file); | |
//////////////////////////////////////////////////////////////////////////////////////// | |
// Thanks to Dirk Becker http://www.ixta.com/scripts/utilities/summaryDifference.html // | |
// For permission to use his summaryDifference function // | |
//////////////////////////////////////////////////////////////////////////////////////// | |
if (!$.summary.difference) { | |
$.summary.difference = function() { | |
return $.summary().replace( | |
/ *([0-9]+) ([^ ]+)(\n?)/g, | |
$.summary.updateSnapshot | |
); | |
}; | |
} | |
if (!$.summary.updateSnapshot) { | |
$.summary.updateSnapshot = function(full, count, name, lf) { | |
var snapshot = $.summary.snapshot; | |
count = Number(count); | |
var prev = snapshot[name] ? snapshot[name] : 0; | |
snapshot[name] = count; | |
var diff = count - prev; | |
if (diff === 0) return ""; | |
return " ".substring(String(diff).length) + diff + " " + name + lf; | |
}; | |
} | |
if (!$.summary.snapshot) { | |
$.summary.snapshot = []; | |
$.summary.difference(); | |
} | |
$.gc(); | |
$.gc(); | |
$.summary.difference(); | |
/** | |
* [sumDiff description] | |
* @param {[type]} message [description] | |
* @return {[type]} [description] | |
*/ | |
LOG.sumDiff = function(message) { | |
$.gc(); | |
$.gc(); | |
var diff = $.summary.difference(); | |
if (diff.length < <span class="wp-font-emots-emo-sunglasses"></span> { | |
diff = ' - NONE -'; | |
} | |
if (message === undefined) { | |
message = ''; | |
} | |
message += diff; | |
return LOG('$.summary.difference(): ' + message, 'v'); | |
}; | |
return LOG; | |
}; // end of LogFactory |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment