From 9e6645cd5fae91d28bfbe328246834dac31766e9 Mon Sep 17 00:00:00 2001 From: Dave Winer Date: Sun, 18 Jan 2015 10:37:40 -0500 Subject: [PATCH] v0.51 --- lib/utils.js | 890 +++++++++++++++++++++++++++++++++++++++++++++++++++ pagepark.js | 268 +++++----------- 2 files changed, 966 insertions(+), 192 deletions(-) create mode 100644 lib/utils.js diff --git a/lib/utils.js b/lib/utils.js new file mode 100644 index 0000000..9edd16f --- /dev/null +++ b/lib/utils.js @@ -0,0 +1,890 @@ +exports.beginsWith = beginsWith; +exports.endsWith = endsWith; +exports.stringCountFields = stringCountFields; +exports.stringDelete = stringDelete; +exports.stringMid = stringMid; +exports.padWithZeros = padWithZeros; +exports.getDatePath = getDatePath; +exports.secondsSince = secondsSince; +exports.bumpUrlString = bumpUrlString; +exports.stringContains = stringContains; +exports.sameDay = sameDay; +exports.jsonStringify = jsonStringify; +exports.stringNthField = stringNthField; +exports.getBoolean = getBoolean; +exports.isAlpha = isAlpha; +exports.isNumeric = isNumeric; +exports.stringLastField = stringLastField; +exports.multipleReplaceAll = multipleReplaceAll; + +function sameDay (d1, d2) { + //returns true if the two dates are on the same day + d1 = new Date (d1); + d2 = new Date (d2); + return ((d1.getFullYear () == d2.getFullYear ()) && (d1.getMonth () == d2.getMonth ()) && (d1.getDate () == d2.getDate ())); + } +function dayGreaterThanOrEqual (d1, d2) { //9/2/14 by DW + d1 = new Date (d1); + d1.setHours (0); + d1.setMinutes (0); + d1.setSeconds (0); + + d2 = new Date (d2); + d2.setHours (0); + d2.setMinutes (0); + d2.setSeconds (0); + + return (d1 >= d2); + } +function stringLower (s) { + return (s.toLowerCase ()); + } +function secondsSince (when) { + var now = new Date (); + when = new Date (when); + return ((now - when) / 1000); + } +function padWithZeros (num, ctplaces) { + var s = num.toString (); + while (s.length < ctplaces) { + s = "0" + s; + } + return (s); + } +function getDatePath (theDate, flLastSeparator) { + if (theDate === undefined) { + theDate = new Date (); + } + else { + theDate = new Date (theDate); //8/12/14 by DW -- make sure it's a date type + } + if (flLastSeparator === undefined) { + flLastSeparator = true; + } + + var month = padWithZeros (theDate.getMonth () + 1, 2); + var day = padWithZeros (theDate.getDate (), 2); + var year = theDate.getFullYear (); + + if (flLastSeparator) { + return (year + "/" + month + "/" + day + "/"); + } + else { + return (year + "/" + month + "/" + day); + } + } +function multipleReplaceAll (s, adrTable, flCaseSensitive, startCharacters, endCharacters) { + if(flCaseSensitive===undefined){ + flCaseSensitive = false; + } + if(startCharacters===undefined){ + startCharacters=""; + } + if(endCharacters===undefined){ + endCharacters=""; + } + for( var item in adrTable){ + var replacementValue = adrTable[item]; + var regularExpressionModifier = "g"; + if(!flCaseSensitive){ + regularExpressionModifier = "gi"; + } + var regularExpressionString = (startCharacters+item+endCharacters).replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1"); + var regularExpression = new RegExp(regularExpressionString, regularExpressionModifier); + s = s.replace(regularExpression, replacementValue); + } + return s; + } +function endsWith (s, possibleEnding, flUnicase) { + if ((s === undefined) || (s.length == 0)) { + return (false); + } + var ixstring = s.length - 1; + if (flUnicase === undefined) { + flUnicase = true; + } + if (flUnicase) { + for (var i = possibleEnding.length - 1; i >= 0; i--) { + if (stringLower (s [ixstring--]) != stringLower (possibleEnding [i])) { + return (false); + } + } + } + else { + for (var i = possibleEnding.length - 1; i >= 0; i--) { + if (s [ixstring--] != possibleEnding [i]) { + return (false); + } + } + } + return (true); + } +function stringContains (s, whatItMightContain, flUnicase) { //11/9/14 by DW + if (flUnicase === undefined) { + flUnicase = true; + } + if (flUnicase) { + s = s.toLowerCase (); + whatItMightContain = whatItMightContain.toLowerCase (); + } + return (s.indexOf (whatItMightContain) != -1); + } +function beginsWith (s, possibleBeginning, flUnicase) { + if (s.length == 0) { //1/1/14 by DW + return (false); + } + if (flUnicase === undefined) { + flUnicase = true; + } + if (flUnicase) { + for (var i = 0; i < possibleBeginning.length; i++) { + if (stringLower (s [i]) != stringLower (possibleBeginning [i])) { + return (false); + } + } + } + else { + for (var i = 0; i < possibleBeginning.length; i++) { + if (s [i] != possibleBeginning [i]) { + return (false); + } + } + } + return (true); + } +function isAlpha (ch) { + return (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z'))); + } +function isNumeric (ch) { + return ((ch >= '0') && (ch <= '9')); + } +function trimLeading (s, ch) { + while (s.charAt (0) === ch) { + s = s.substr (1); + } + return (s); + } +function trimTrailing (s, ch) { + while (s.charAt (s.length - 1) === ch) { + s = s.substr (0, s.length - 1); + } + return (s); + } +function trimWhitespace (s) { //rewrite -- 5/30/14 by DW + function isWhite (ch) { + switch (ch) { + case " ": case "\r": case "\n": case "\t": + return (true); + } + return (false); + } + if (s === undefined) { //9/10/14 by DW + return (""); + } + while (isWhite (s.charAt (0))) { + s = s.substr (1); + } + while (s.length > 0) { + if (!isWhite (s.charAt (0))) { + break; + } + s = s.substr (1); + } + while (s.length > 0) { + if (!isWhite (s.charAt (s.length - 1))) { + break; + } + s = s.substr (0, s.length - 1); + } + return (s); + } +function addPeriodAtEnd (s) { + s = trimWhitespace (s); + if (s.length == 0) { + return (s); + } + switch (s [s.length - 1]) { + case ".": + case ",": + case "?": + case "\"": + case "'": + case ":": + case ";": + case "!": + return (s); + default: + return (s + "."); + } + } +function getBoolean (val) { //12/5/13 by DW + switch (typeof (val)) { + case "string": + if (val.toLowerCase () == "true") { + return (true); + } + break; + case "boolean": + return (val); + break; + case "number": + if (val == 1) { + return (true); + } + break; + } + return (false); + } +function bumpUrlString (s) { //5/10/14 by DW + if (s === undefined) { + s = "0"; + } + function bumpChar (ch) { + function num (ch) { + return (ch.charCodeAt (0)); + } + if ((ch >= "0") && (ch <= "8")) { + ch = String.fromCharCode (num (ch) + 1); + } + else { + if (ch == "9") { + ch = "a"; + } + else { + if ((ch >= "a") && (ch <= "y")) { + ch = String.fromCharCode (num (ch) + 1); + } + else { + throw "rollover!"; + } + } + } + return (ch); + } + try { + var chlast = bumpChar (s [s.length - 1]); + s = s.substr (0, s.length - 1) + chlast; + return (s); + } + catch (tryError) { + if (s.length == 1) { + return ("00"); + } + else { + s = s.substr (0, s.length - 1); + s = bumpUrlString (s) + "0"; + return (s); + } + } + } +function stringDelete (s, ix, ct) { + var start = ix - 1; + var end = (ix + ct) - 1; + var s1 = s.substr (0, start); + var s2 = s.substr (end); + return (s1 + s2); + } +function replaceAll (s, searchfor, replacewith) { + function escapeRegExp (string) { + return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); + } + return (s.replace (new RegExp (escapeRegExp (searchfor), 'g'), replacewith)); + } +function stringCountFields (s, chdelim) { + var ct = 1; + if (s.length == 0) { + return (0); + } + for (var i = 0; i < s.length; i++) { + if (s [i] == chdelim) { + ct++; + } + } + return (ct) + } +function stringNthField (s, chdelim, n) { + var splits = s.split (chdelim); + if (splits.length >= n) { + return splits [n-1]; + } + return (""); + } +function dateYesterday (d) { + return (new Date (new Date (d) - (24 * 60 * 60 * 1000))); + } +function stripMarkup (s) { //5/24/14 by DW + if ((s === undefined) || (s == null) || (s.length == 0)) { + return (""); + } + return (s.replace (/(<([^>]+)>)/ig, "")); + } +function maxStringLength (s, len, flWholeWordAtEnd, flAddElipses) { + if (flWholeWordAtEnd === undefined) { + flWholeWordAtEnd = true; + } + if (flAddElipses === undefined) { //6/2/14 by DW + flAddElipses = true; + } + if (s.length > len) { + s = s.substr (0, len); + if (flWholeWordAtEnd) { + while (s.length > 0) { + if (s [s.length - 1] == " ") { + if (flAddElipses) { + s += "..."; + } + break; + } + s = s.substr (0, s.length - 1); //pop last char + } + } + } + return (s); + } +function random (lower, upper) { + var range = upper - lower + 1; + return (Math.floor ((Math.random () * range) + lower)); + } +function removeMultipleBlanks (s) { //7/30/14 by DW + return (s.toString().replace (/ +/g, " ")); + } +function stringAddCommas (x) { //5/27/14 by DW + return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); + } +function readHttpFile (url, callback, timeoutInMilliseconds) { //5/27/14 by DW + if (timeoutInMilliseconds === undefined) { + timeoutInMilliseconds = 30000; + } + var jxhr = $.ajax ({ + url: url, + dataType: "text" , + timeout: timeoutInMilliseconds + }) + .success (function (data, status) { + callback (data); + }) + .error (function (status) { + console.log ("readHttpFile: url == " + url + ", error == " + jsonStringify (status)); + callback (undefined); + }); + } +function readHttpFileThruProxy (url, type, callback) { //10/25/14 by DW + var urlReadFileApi = "http://pub.fargo.io/httpReadUrl"; + if (type === undefined) { + type = "text/plain"; + } + var jxhr = $.ajax ({ + url: urlReadFileApi + "?url=" + encodeURIComponent (url) + "&type=" + encodeURIComponent (type), + dataType: "text" , + timeout: 30000 + }) + .success (function (data, status) { + if (callback != undefined) { + callback (data); + } + }) + .error (function (status) { + console.log ("readHttpFileThruProxy: url == " + url + ", error == " + status.statusText + "."); + if (callback != undefined) { + callback (undefined); + } + }); + } +function stringPopLastField (s, chdelim) { //5/28/14 by DW + if (s.length == 0) { + return (s); + } + if (endsWith (s, chdelim)) { + s = stringDelete (s, s.length, 1); + } + while (s.length > 0) { + if (endsWith (s, chdelim)) { + return (stringDelete (s, s.length, 1)); + } + s = stringDelete (s, s.length, 1); + } + return (s); + } +function filledString (ch, ct) { //6/4/14 by DW + var s = ""; + for (var i = 0; i < ct; i++) { + s += ch; + } + return (s); + } +function encodeXml (s) { //7/15/14 by DW + var charMap = { + '<': '<', + '>': '>', + '&': '&', + '"': '&'+'quot;' + }; + s = s.toString(); + s = s.replace(/\u00A0/g, " "); + var escaped = s.replace(/[<>&"]/g, function(ch) { + return charMap [ch]; + }); + return escaped; + } +function decodeXml (s) { //11/7/14 by DW + return (s.replace (/</g,'<').replace(/>/g,'>').replace(/&/g,'&')); + } +function hotUpText (s, url) { //7/18/14 by DW + + if (url === undefined) { //makes it easier to call -- 3/14/14 by DW + return (s); + } + + function linkit (s) { + return ("" + s + ""); + } + var ixleft = s.indexOf ("["), ixright = s.indexOf ("]"); + if ((ixleft == -1) || (ixright == -1)) { + return (linkit (s)); + } + if (ixright < ixleft) { + return (linkit (s)); + } + + var linktext = s.substr (ixleft + 1, ixright - ixleft - 1); //string.mid (s, ixleft, ixright - ixleft + 1); + linktext = "" + linktext + ""; + + var leftpart = s.substr (0, ixleft); + var rightpart = s.substr (ixright + 1, s.length); + s = leftpart + linktext + rightpart; + return (s); + } +function getFavicon (url) { //7/18/14 by DW + function getDomain (url) { + if (( url != null ) && (url != "")) { + url = url.replace("www.","").replace("www2.", "").replace("feedproxy.", "").replace("feeds.", ""); + var root = url.split('?')[0]; // cleans urls of form http://domain.com?a=1&b=2 + var url = root.split('/')[2]; + } + return (url); + }; + var domain = getDomain (url); + return ("http://www.google.com/s2/favicons?domain=" + domain); + }; +function jsonStringify (jstruct) { //7/19/14 by DW + return (JSON.stringify (jstruct, undefined, 4)); + } +function getURLParameter (name) { //7/21/14 by DW + return (decodeURI ((RegExp(name + '=' + '(.+?)(&|$)').exec(location.search)||[,null])[1])); + } +function urlSplitter (url) { //7/15/14 by DW + var pattern = /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/; + var result = pattern.exec (url); + if (result == null) { + result = []; + result [5] = url; + } + var splitUrl = { + scheme: result [1], + host: result [3], + port: result [4], + path: result [5], + query: result [6], + hash: result [7] + }; + return (splitUrl); + } +function innerCaseName (text) { //8/12/14 by DW + var s = "", ch, flNextUpper = false; + text = stripMarkup (text); + for (var i = 0; i < text.length; i++) { + ch = text [i]; + if (isAlpha (ch) || isNumeric (ch)) { + if (flNextUpper) { + ch = ch.toUpperCase (); + flNextUpper = false; + } + else { + ch = ch.toLowerCase (); + } + s += ch; + } + else { + if (ch == ' ') { + flNextUpper = true; + } + } + } + return (s); + } +function hitCounter (counterGroup, counterServer) { //8/12/14 by DW + var defaultCounterGroup = "scripting", defaultCounterServer = "http://counter.fargo.io/counter"; + var thispageurl = location.href; + if (counterGroup === undefined) { + counterGroup = defaultCounterGroup; + } + if (counterServer === undefined) { + counterServer = defaultCounterServer; + } + if (thispageurl === undefined) { + thispageurl = ""; + } + if (endsWith (thispageurl, "#")) { + thispageurl = thispageurl.substr (0, thispageurl.length - 1); + } + var jxhr = $.ajax ({ + url: counterServer + "?group=" + encodeURIComponent (counterGroup) + "&referer=" + encodeURIComponent (document.referrer) + "&url=" + encodeURIComponent (thispageurl), + dataType: "jsonp", + jsonpCallback : "getData", + timeout: 30000 + }) + .success (function (data, status, xhr) { + console.log ("hitCounter: counter ping accepted by server."); + }) + .error (function (status, textStatus, errorThrown) { + console.log ("hitCounter: counter ping error: " + textStatus); + }); + } +function stringMid (s, ix, len) { //8/12/14 by DW + return (s.substr (ix-1, len)); + } +function getCmdKeyPrefix () { //8/15/14 by DW + if (navigator.platform.toLowerCase ().substr (0, 3) == "mac") { + return ("⌘"); + } + else { + return ("Ctrl+"); + } + } +function getRandomSnarkySlogan () { //8/15/14 by DW + var snarkySlogans = [ + "Good for the environment.", + "All baking done on premises.", + "Still diggin!", + "It's even worse than it appears.", + "Ask not what the Internet can do for you...", + "You should never argue with a crazy man.", + "Welcome back my friends to the show that never ends.", + "Greetings, citizen of Planet Earth. We are your overlords. :-)", + "We don't need no stinkin rock stars.", + "This aggression will not stand.", + "Pay no attention to the man behind the curtain.", + "Only steal from the best.", + "Reallll soooon now...", + "What a long strange trip it's been.", + "Ask not what the Internet can do for you.", + "When in doubt, blog.", + "Shut up and eat your vegetables.", + "Don't slam the door on the way out.", + "Yeah well, that's just, you know, like, your opinion, man.", + "So, it has come to this." + ] + return (snarkySlogans [random (0, snarkySlogans.length - 1)]); + } +function dayOfWeekToString (theDay) { //8/23/14 by DW + var weekday = [ + "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" + ]; + return (weekday[theDay]); + } +function viewDate (when, flShortDayOfWeek) { //8/23/14 by DW + var now = new Date (); + when = new Date (when); + if (sameDay (when, now)) { + return (timeString (when, false)) //2/9/13 by DW; + } + else { + var oneweek = 1000 * 60 * 60 * 24 * 7; + var cutoff = now - oneweek; + if (when > cutoff) { //within the last week + var s = dayOfWeekToString (when.getDay ()); + if (flShortDayOfWeek) { + s = s.substring (0, 3); + } + return (s); + } + else { + return (when.toLocaleDateString ()); + } + } + } +function timeString (when, flIncludeSeconds) { //8/26/14 by DW + var hour = when.getHours (), minutes = when.getMinutes (), ampm = "AM", s; + if (hour >= 12) { + ampm = "PM"; + } + if (hour > 12) { + hour -= 12; + } + if (hour == 0) { + hour = 12; + } + if (minutes < 10) { + minutes = "0" + minutes; + } + if (flIncludeSeconds) { + var seconds = when.getSeconds (); + if (seconds < 10) { + seconds = "0" + seconds; + } + s = hour + ":" + minutes + ":" + seconds + ampm; + } + else { + s = hour + ":" + minutes + ampm; + } + return (s); + } +function stringLastField (s, chdelim) { //8/27/14 by DW + var ct = stringCountFields (s, chdelim); + if (ct == 0) { //8/31/14 by DW + return (s); + } + return (stringNthField (s, chdelim, ct)); + } +function maxLengthString (s, maxlength) { //8/27/14 by DW + if (s.length > maxlength) { + s = s.substr (0, maxlength); + while (true) { + var len = s.length; flbreak = false; + if (len == 0) { + break; + } + if (s [len - 1] == " ") { + flbreak = true; + } + s = s.substr (0, len - 1); + if (flbreak) { + break; + } + } + s = s + "..."; + } + return (s); + } +function formatDate (theDate, dateformat, timezone) { //8/28/14 by DW + if (theDate === undefined) { + theDate = new Date (); + } + if (dateformat === undefined) { + dateformat = "%c"; + } + if (timezone === undefined) { + timezone = - (new Date ().getTimezoneOffset () / 60); + } + try { + var offset = new Number (timezone); + var d = new Date (theDate); + var localTime = d.getTime (); + var localOffset = d.getTimezoneOffset () * 60000; + var utc = localTime + localOffset; + var newTime = utc + (3600000 * offset); + return (new Date (newTime).strftime (dateformat)); + } + catch (tryerror) { + return (new Date (theDate).strftime (dateformat)); + } + } +function addPeriodToSentence (s) { //8/29/14 by DW + if (s.length > 0) { + var fladd = true; + var ch = s [s.length - 1]; + switch (ch) { + case "!": case "?": case ":": + fladd = false; + break; + default: + if (endsWith (s, ".\"")) { + fladd = false; + } + else { + if (endsWith (s, ".'")) { + fladd = false; + } + } + } + if (fladd) { + s += "."; + } + } + return (s); + } +function copyScalars (source, dest) { //8/31/14 by DW + for (var x in source) { + var type, val = source [x]; + if (val instanceof Date) { + val = val.toString (); + } + type = typeof (val); + if ((type != "object") && (type != undefined)) { + dest [x] = val; + } + } + } +function linkToDomainFromUrl (url, flshort, maxlength) { //10/10/14 by DW + var splitUrl = urlSplitter (url), host = splitUrl.host.toLowerCase (); + if (flshort === undefined) { + flshort = false; + } + if (flshort) { + var splithost = host.split ("."); + if (splithost.length == 3) { + host = splithost [1]; + } + else { + host = splithost [0]; + } + } + else { + if (beginsWith (host, "www.")) { + host = stringDelete (host, 1, 4); + } + } + + if (maxlength != undefined) { //10/10/14; 10:46:56 PM by DW + if (host.length > maxlength) { + host = stringMid (host, 1, maxlength) + "..."; + } + } + + return ("" + host + ""); + } +function getRandomPassword (ctchars) { //10/14/14 by DW + var s= "", ch; + while (s.length < ctchars) { + ch = String.fromCharCode (random (33, 122)); + if (isAlpha (ch) || isNumeric (ch)) { + s += ch; + } + } + return (s.toLowerCase ()); + } +function monthToString (theMonthNum) { //11/4/14 by DW + + + var theDate; + if (theMonthNum === undefined) { + theDate = new Date (); + } + else { + theDate = new Date ((theMonthNum + 1) + "/1/2014"); + } + return (formatDate (theDate, "%B")); + } +function getCanonicalName (text) { //11/4/14 by DW + var s = "", ch, flNextUpper = false; + text = stripMarkup (text); //6/30/13 by DW + for (var i = 0; i < text.length; i++) { + ch = text [i]; + if (isAlpha (ch) || isNumeric (ch)) { + if (flNextUpper) { + ch = ch.toUpperCase (); + flNextUpper = false; + } + else { + ch = ch.toLowerCase (); + } + s += ch; + } + else { + if (ch == ' ') { + flNextUpper = true; + } + } + } + return (s); + } +function clockNow () { //11/7/14 by DW + return (new Date ()); + } +function sleepTillTopOfMinute (callback) { //11/22/14 by DW + var ctseconds = Math.round (60 - (new Date ().getSeconds () + 60) % 60); + if (ctseconds == 0) { + ctseconds = 60; + } + setTimeout (everyMinute, ctseconds * 1000); + } +function scheduleNextRun (callback, ctMillisecsBetwRuns) { //11/27/14 by DW + var ctmilliseconds = ctMillisecsBetwRuns - (Number (new Date ().getMilliseconds ()) + ctMillisecsBetwRuns) % ctMillisecsBetwRuns; + setTimeout (callback, ctmilliseconds); + } +function urlEncode (s) { //12/4/14 by DW + return (encodeURIComponent (s)); + } +function popTweetNameAtStart (s) { //12/8/14 by DW + var ch; + s = trimWhitespace (s); + if (s.length > 0) { + if (s.charAt (0) == "@") { + while (s.charAt (0) != " ") { + s = s.substr (1) + } + while (s.length > 0) { + ch = s.charAt (0); + if ((ch != " ") && (ch != "-")) { + break; + } + s = s.substr (1) + } + } + } + return (s); + } +function httpHeadRequest (url, callback) { //12/17/14 by DW + var jxhr = $.ajax ({ + url: url, + type: "HEAD", + dataType: "text", + timeout: 30000 + }) + .success (function (data, status, xhr) { + callback (xhr); //you can do xhr.getResponseHeader to get one of the header elements + }) + } +function httpExt2MIME (ext) { //12/24/14 by DW + var lowerext = stringLower (ext); + var map = { + "au": "audio/basic", + "avi": "application/x-msvideo", + "bin": "application/x-macbinary", + "css": "text/css", + "dcr": "application/x-director", + "dir": "application/x-director", + "dll": "application/octet-stream", + "doc": "application/msword", + "dtd": "text/dtd", + "dxr": "application/x-director", + "exe": "application/octet-stream", + "fatp": "text/html", + "ftsc": "text/html", + "fttb": "text/html", + "gif": "image/gif", + "gz": "application/x-gzip", + "hqx": "application/mac-binhex40", + "htm": "text/html", + "html": "text/html", + "jpeg": "image/jpeg", + "jpg": "image/jpeg", + "js": "application/javascript", + "mid": "audio/x-midi", + "midi": "audio/x-midi", + "mov": "video/quicktime", + "mp3": "audio/mpeg", + "pdf": "application/pdf", + "png": "image/png", + "ppt": "application/mspowerpoint", + "ps": "application/postscript", + "ra": "audio/x-pn-realaudio", + "ram": "audio/x-pn-realaudio", + "sit": "application/x-stuffit", + "sys": "application/octet-stream", + "tar": "application/x-tar", + "text": "text/plain", + "txt": "text/plain", + "wav": "audio/x-wav", + "wrl": "x-world/x-vrml", + "xml": "text/xml", + "zip": "application/zip" + }; + for (x in map) { + if (stringLower (x) == lowerext) { + return (map [x]); + } + } + return ("text/plain"); + } + diff --git a/pagepark.js b/pagepark.js index d622b60..3ebceaf 100644 --- a/pagepark.js +++ b/pagepark.js @@ -1,4 +1,4 @@ -var myVersion = "0.49", myProductName = "PagePark"; +var myVersion = "0.51", myProductName = "PagePark"; //The MIT License (MIT) @@ -29,6 +29,7 @@ var http = require ("http"); var marked = require ("marked"); var dns = require ("dns"); var mime = require ("mime"); //1/8/15 by DW +var utils = require ("./lib/utils.js"); //1/18/15 by DW var folderPathFromEnv = process.env.pageparkFolderPath; //1/3/15 by DW @@ -48,171 +49,12 @@ var pageparkStats = { var fnameStats = "prefs/stats.json", flStatsDirty = false; var domainsPath = "domains/"; + var configFname = "/config.json"; var mdTemplatePath = "prefs/mdTemplate.txt"; var urlDefaultTemplate = "http://fargo.io/code/pagepark/defaultmarkdowntemplate.txt"; //routines from utils.js, fs.js - function getBoolean (val) { - switch (typeof (val)) { - case "string": - if (val.toLowerCase () == "true") { - return (true); - } - break; - case "boolean": - return (val); - break; - case "number": - if (val != 0) { - return (true); - } - break; - } - return (false); - } - function isAlpha (ch) { - return (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z'))); - } - function isNumeric (ch) { - return ((ch >= '0') && (ch <= '9')); - } - function jsonStringify (jstruct) { - return (JSON.stringify (jstruct, undefined, 4)); - } - function secondsSince (when) { - var now = new Date (); - when = new Date (when); - return ((now - when) / 1000); - } - function sameDay (d1, d2) { //returns true if the two dates are on the same day - d1 = new Date (d1); - d2 = new Date (d2); - return ((d1.getFullYear () == d2.getFullYear ()) && (d1.getMonth () == d2.getMonth ()) && (d1.getDate () == d2.getDate ())); - } - function beginsWith (s, possibleBeginning, flUnicase) { - if (s.length == 0) { //1/1/14 by DW - return (false); - } - if (flUnicase == undefined) { - flUnicase = true; - } - if (flUnicase) { - for (var i = 0; i < possibleBeginning.length; i++) { - if (s [i].toLowerCase () != possibleBeginning [i].toLowerCase ()) { - return (false); - } - } - } - else { - for (var i = 0; i < possibleBeginning.length; i++) { - if (s [i] != possibleBeginning [i]) { - return (false); - } - } - } - return (true); - } - function endsWith (s, possibleEnding, flUnicase) { - if ((s == undefined) || (s.length == 0)) { - return (false); - } - var ixstring = s.length - 1; - if (flUnicase == undefined) { - flUnicase = true; - } - if (flUnicase) { - for (var i = possibleEnding.length - 1; i >= 0; i--) { - if (s [ixstring--].toLowerCase () != possibleEnding [i].toLowerCase ()) { - return (false); - } - } - } - else { - for (var i = possibleEnding.length - 1; i >= 0; i--) { - if (s [ixstring--] != possibleEnding [i]) { - return (false); - } - } - } - return (true); - } - function stringDelete (s, ix, ct) { - var start = ix - 1; - var end = (ix + ct) - 1; - var s1 = s.substr (0, start); - var s2 = s.substr (end); - return (s1 + s2); - } - function stringContains (s, whatItMightContain, flUnicase) { - if (flUnicase === undefined) { - flUnicase = true; - } - if (flUnicase) { - s = s.toLowerCase (); - whatItMightContain = whatItMightContain.toLowerCase (); - } - return (s.indexOf (whatItMightContain) != -1); - } - function stringCountFields (s, chdelim) { - var ct = 1; - if (s.length == 0) { - return (0); - } - for (var i = 0; i < s.length; i++) { - if (s [i] == chdelim) { - ct++; - } - } - return (ct) - } - function stringNthField (s, chdelim, n) { - var splits = s.split (chdelim); - if (splits.length >= n) { - return splits [n-1]; - } - return (""); - } - function stringLastField (s, chdelim) { - var ct = stringCountFields (s, chdelim); - if (ct == 0) { //8/31/14 by DW - return (s); - } - return (stringNthField (s, chdelim, ct)); - } - function multipleReplaceAll (s, adrTable, flCaseSensitive, startCharacters, endCharacters) { - if(flCaseSensitive===undefined){ - flCaseSensitive = false; - } - if(startCharacters===undefined){ - startCharacters=""; - } - if(endCharacters===undefined){ - endCharacters=""; - } - for( var item in adrTable){ - var replacementValue = adrTable[item]; - var regularExpressionModifier = "g"; - if(!flCaseSensitive){ - regularExpressionModifier = "gi"; - } - var regularExpressionString = (startCharacters+item+endCharacters).replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1"); - var regularExpression = new RegExp(regularExpressionString, regularExpressionModifier); - s = s.replace(regularExpression, replacementValue); - } - return s; - } - function httpExt2MIME (ext) { //12/24/14 by DW - mime.default_type = "text/plain"; - return (mime.lookup (ext)); - } - function httpReadUrl (url, callback) { - request (url, function (error, response, body) { - if (!error && (response.statusCode == 200)) { - callback (body) - } - }); - } function fsSureFilePath (path, callback) { var splits = path.split ("/"); path = ""; //1/8/15 by DW @@ -246,16 +88,27 @@ var urlDefaultTemplate = "http://fargo.io/code/pagepark/defaultmarkdowntemplate. } } +function httpExt2MIME (ext) { //12/24/14 by DW + mime.default_type = "text/plain"; + return (mime.lookup (ext)); + } +function httpReadUrl (url, callback) { + request (url, function (error, response, body) { + if (!error && (response.statusCode == 200)) { + callback (body) + } + }); + } function getFullFilePath (relpath) { //1/3/15 by DW var folderpath = folderPathFromEnv; if (folderpath == undefined) { //the environment variable wasn't specified return (relpath); } - if (!endsWith (folderpath, "/")) { + if (!utils.endsWith (folderpath, "/")) { folderpath += "/"; } - if (beginsWith (relpath, "/")) { - relpath = stringDelete (relpath, 1, 1); + if (utils.beginsWith (relpath, "/")) { + relpath = utils.stringDelete (relpath, 1, 1); } return (folderpath + relpath); } @@ -280,7 +133,7 @@ function getMarkdownTemplate (callback) { } function checkPathForIllegalChars (path) { function isIllegal (ch) { - if (isAlpha (ch) || isNumeric (ch)) { + if (utils.isAlpha (ch) || utils.isNumeric (ch)) { return (false); } switch (ch) { @@ -294,7 +147,7 @@ function checkPathForIllegalChars (path) { return (false); } } - if (stringContains (path, "./")) { + if (utils.stringContains (path, "./")) { return (false); } return (true); @@ -307,18 +160,37 @@ function everySecond () { } } + + function handleHttpRequest (httpRequest, httpResponse) { + function getConfigFile (host, callback) { + var f = getFullFilePath (domainsPath) + host + configFname; + fs.readFile (f, function (err, data) { + if (err) { + callback (undefined); + } + else { + try { + var config = JSON.parse (data.toString ()); + callback (config); + } + catch (err) { + console.log ("getConfigFile: error reading " + configFname + " file for host " + host + ". " + err.message); + callback (undefined); + } + } + }); + } function return404 () { httpResponse.writeHead (404, {"Content-Type": "text/plain"}); httpResponse.end ("The file was not found."); } - function findIndexFile (folder, callback) { fs.readdir (folder, function (err, list) { for (var i = 0; i < list.length; i++) { var fname = list [i]; - if (stringCountFields (fname, ".") == 2) { //something like xxx.yyy - if (stringNthField (fname, ".", 1).toLowerCase () == pageparkPrefs.indexFilename) { //something like index.wtf + if (utils.stringCountFields (fname, ".") == 2) { //something like xxx.yyy + if (utils.stringNthField (fname, ".", 1).toLowerCase () == pageparkPrefs.indexFilename) { //something like index.wtf callback (folder + fname); return; } @@ -333,7 +205,7 @@ function handleHttpRequest (httpRequest, httpResponse) { return404 (); } else { - var ext = stringLastField (f, ".").toLowerCase (), type = httpExt2MIME (ext); + var ext = utils.stringLastField (f, ".").toLowerCase (), type = httpExt2MIME (ext); switch (ext) { case "js": try { @@ -350,8 +222,8 @@ function handleHttpRequest (httpRequest, httpResponse) { getMarkdownTemplate (function (theTemplate) { var mdtext = data.toString (), pagetable = new Object (); pagetable.bodytext = marked (mdtext); - pagetable.title = stringLastField (f, "/"); - var s = multipleReplaceAll (theTemplate, pagetable, false, "[%", "%]"); + pagetable.title = utils.stringLastField (f, "/"); + var s = utils.multipleReplaceAll (theTemplate, pagetable, false, "[%", "%]"); httpResponse.writeHead (200, {"Content-Type": "text/html"}); httpResponse.end (s); }); @@ -370,9 +242,9 @@ function handleHttpRequest (httpRequest, httpResponse) { var lowerpath = parsedUrl.pathname.toLowerCase (), now = new Date (); //set host, port host = httpRequest.headers.host; - if (stringContains (host, ":")) { - port = stringNthField (host, ":", 2); - host = stringNthField (host, ":", 1); + if (utils.stringContains (host, ":")) { + port = utils.stringNthField (host, ":", 2); + host = utils.stringNthField (host, ":", 1); } else { port = 80; @@ -393,7 +265,7 @@ function handleHttpRequest (httpRequest, httpResponse) { pageparkStats.hitsByDomain [lowerhost]++; } //hits today - if (!sameDay (now, pageparkStats.whenLastHit)) { //day rollover + if (!utils.sameDay (now, pageparkStats.whenLastHit)) { //day rollover pageparkStats.ctHitsToday = 0; } pageparkStats.ctHits++; @@ -427,29 +299,41 @@ function handleHttpRequest (httpRequest, httpResponse) { status: pageparkStats } httpResponse.writeHead (200, {"Content-Type": "text/plain"}); - httpResponse.end (jsonStringify (status)); + httpResponse.end (utils.jsonStringify (status)); break; default: //see if it's a path in the domains folder, if not 404 - var f = getFullFilePath (domainsPath) + host + parsedUrl.pathname; + var domainfolder = getFullFilePath (domainsPath) + host; + var f = domainfolder + parsedUrl.pathname; if (checkPathForIllegalChars (f)) { fsSureFilePath (domainsPath, function () { //make sure domains folder exists - fs.stat (f, function (err, stats) { - if (err) { - return404 (); + getConfigFile (host, function (config) { //get config.json, if it exists -- 1/18/15 by DW + if (config != undefined) { + console.log ("handleHttpRequest: config == " + utils.jsonStringify (config)); + if (config.urlSiteRedirect != undefined) { + var urlRedirect = config.urlSiteRedirect + parsedUrl.pathname; + httpResponse.writeHead (302, {"Location": urlRedirect, "Content-Type": "text/plain"}); + httpResponse.end ("Temporary redirect to " + urlRedirect + "."); + return; + } } - else { - if (stats.isDirectory ()) { - if (!endsWith (f, "/")) { - f += "/"; - } - findIndexFile (f, function (findex) { - serveFile (findex); - }); + fs.stat (f, function (err, stats) { + if (err) { + return404 (); } else { - serveFile (f); + if (stats.isDirectory ()) { + if (!utils.endsWith (f, "/")) { + f += "/"; + } + findIndexFile (f, function (findex) { + serveFile (findex); + }); + } + else { + serveFile (f); + } } - } + }); }); }); } @@ -470,7 +354,7 @@ function handleHttpRequest (httpRequest, httpResponse) { function writeStats (fname, stats, callback) { var f = getFullFilePath (fname); fsSureFilePath (f, function () { - fs.writeFile (f, jsonStringify (stats), function (err) { + fs.writeFile (f, utils.jsonStringify (stats), function (err) { if (err) { console.log ("writeStats: error == " + err.message); }