Stackedit/public/res/utils.js

761 lines
20 KiB
JavaScript
Raw Normal View History

2013-05-27 19:45:33 +00:00
define([
"jquery",
"underscore",
2013-11-05 23:03:38 +00:00
"storage",
2013-08-04 00:53:46 +00:00
"crel",
2013-10-03 01:02:18 +00:00
"xregexp",
2013-08-30 22:45:29 +00:00
"stacktrace",
2013-11-05 23:03:38 +00:00
"FileSaver",
], function($, _, storage, crel, XRegExp, printStackTrace, saveAs) {
2013-05-25 18:13:59 +00:00
2013-05-29 19:55:23 +00:00
var utils = {};
2014-04-08 23:20:48 +00:00
// Faster than setTimeout (see http://dbaron.org/log/20100309-faster-timeouts)
utils.defer = (function() {
var timeouts = [];
2014-04-12 22:30:18 +00:00
var messageName = "deferMsg";
2014-04-08 23:20:48 +00:00
window.addEventListener("message", function(evt) {
if(evt.source == window && evt.data == messageName) {
evt.stopPropagation();
if(timeouts.length > 0) {
timeouts.shift()();
}
}
}, true);
return function(fn) {
timeouts.push(fn);
window.postMessage(messageName, "*");
};
})();
// Implements underscore debounce using our defer function
utils.debounce = function(func, context) {
var isExpected = false;
function later() {
isExpected = false;
func.call(context);
}
return function() {
if(isExpected === true) {
return;
}
isExpected = true;
utils.defer(later);
};
};
// Generates a 24 chars length random string (should be enough to prevent collisions)
utils.randomString = (function() {
var max = Math.pow(36, 6);
function s6() {
// Linear [0-9a-z]{6} random string
return ('000000' + (Math.random() * max | 0).toString(36)).slice(-6);
}
return function() {
return [s6(), s6(), s6(), s6()].join('');
};
})();
2013-05-29 19:55:23 +00:00
// Return a parameter from the URL
utils.getURLParameter = function(name) {
2013-10-13 19:58:42 +00:00
// Parameter can be either a search parameter (&name=...) or a hash fragment parameter (#!name=...)
var regex = new RegExp("(?:\\?|\\#\\!|&)" + name + "=(.+?)(?:&|\\#|$)");
2013-05-29 19:55:23 +00:00
try {
2013-10-13 19:58:42 +00:00
return decodeURIComponent(regex.exec(location.search + location.hash)[1]);
2013-05-29 19:55:23 +00:00
}
2013-09-03 10:37:59 +00:00
catch(e) {
2013-05-29 19:55:23 +00:00
return undefined;
}
};
// Transform a selector into a jQuery object
function jqElt(element) {
2014-03-22 01:57:31 +00:00
if(_.isString(element) || !element.val) {
2013-05-29 19:55:23 +00:00
return $(element);
}
return element;
}
// For input control
function inputError(element, event) {
if(event !== undefined) {
element.stop(true, true).addClass("error").delay(1000).queue(function() {
$(this).removeClass("error");
$(this).dequeue();
});
2013-05-29 19:55:23 +00:00
event.stopPropagation();
}
}
// Return input value
utils.getInputValue = function(element) {
element = jqElt(element);
return element.val();
};
// Set input value
utils.setInputValue = function(element, value) {
element = jqElt(element);
element.val(value);
};
// Return input text value
utils.getInputTextValue = function(element, event, validationRegex) {
element = jqElt(element);
var value = element.val();
if(value === undefined) {
inputError(element, event);
return undefined;
}
// trim
value = utils.trim(value);
if((value.length === 0) || (validationRegex !== undefined && !value.match(validationRegex))) {
inputError(element, event);
return undefined;
}
return value;
};
// Return input number value
function getInputNumValue(isFloat, element, event, min, max) {
2013-05-29 19:55:23 +00:00
element = jqElt(element);
var value = utils.getInputTextValue(element, event);
if(value === undefined) {
return undefined;
}
value = isFloat ? parseFloat(value) : parseInt(value, 10);
2013-09-12 23:25:25 +00:00
if(isNaN(value) || (min !== undefined && value < min) || (max !== undefined && value > max)) {
2013-05-29 19:55:23 +00:00
inputError(element, event);
return undefined;
}
return value;
}
// Return input integer value
utils.getInputIntValue = _.partial(getInputNumValue, false);
// Return input float value
utils.getInputFloatValue = _.partial(getInputNumValue, true);
2013-09-03 10:37:59 +00:00
2013-05-30 22:16:12 +00:00
// Return input value and check that it's a valid RegExp
utils.getInputRegExpValue = function(element, event) {
element = jqElt(element);
var value = utils.getInputTextValue(element, event);
if(value === undefined) {
return undefined;
}
try {
new RegExp(value);
}
catch(e) {
inputError(element, event);
return undefined;
}
return value;
};
2013-05-29 19:55:23 +00:00
2013-06-22 23:48:57 +00:00
// Return input value and check that it's a valid JavaScript object
utils.getInputJsValue = function(element, event) {
element = jqElt(element);
var value = utils.getInputTextValue(element, event);
if(value === undefined) {
return undefined;
}
try {
2013-11-05 23:03:38 +00:00
/*jshint evil:true */
2013-06-22 23:48:57 +00:00
eval("var test=" + value);
2013-11-05 23:03:38 +00:00
/*jshint evil:false */
2013-06-22 23:48:57 +00:00
}
catch(e) {
inputError(element, event);
return undefined;
}
return value;
};
2013-09-03 10:37:59 +00:00
2013-05-29 19:55:23 +00:00
// Return checkbox boolean value
utils.getInputChecked = function(element) {
element = jqElt(element);
return element.prop("checked");
};
// Set checkbox state
utils.setInputChecked = function(element, checked) {
element = jqElt(element);
2013-08-22 19:10:57 +00:00
element.prop("checked", checked).change();
2013-05-29 19:55:23 +00:00
};
// Get radio button value
utils.getInputRadio = function(name) {
return $("input:radio[name=" + name + "]:checked").prop("value");
};
// Set radio button value
utils.setInputRadio = function(name, value) {
2013-08-22 19:10:57 +00:00
$("input:radio[name=" + name + "][value=" + value + "]").prop("checked", true).change();
2013-05-29 19:55:23 +00:00
};
// Reset input control in all modals
utils.resetModalInputs = function() {
2013-06-16 18:29:54 +00:00
$(".modal input[type=text]:not([disabled]), .modal input[type=password], .modal textarea").val("");
2013-08-14 23:44:51 +00:00
$(".modal input[type=checkbox]").prop("checked", false).change();
2013-05-29 19:55:23 +00:00
};
// Basic trim function
utils.trim = function(str) {
return $.trim(str);
};
// Slug function
var nonWordChars = XRegExp('[^\\p{L}\\p{N}-]', 'g');
2013-05-29 19:55:23 +00:00
utils.slugify = function(text) {
2013-09-03 10:37:59 +00:00
return text.toLowerCase().replace(/\s/g, '-') // Replace spaces with -
2013-10-13 00:59:42 +00:00
.replace(nonWordChars, '') // Remove all non-word chars
2013-05-29 19:55:23 +00:00
.replace(/\-\-+/g, '-') // Replace multiple - with single -
.replace(/^-+/, '') // Trim - from start of text
.replace(/-+$/, ''); // Trim - from end of text
};
// Check an URL
utils.checkUrl = function(url, addSlash) {
if(!url) {
return url;
}
if(url.indexOf("http") !== 0) {
url = "http://" + url;
}
if(addSlash && url.indexOf("/", url.length - 1) === -1) {
url += "/";
}
return url;
};
2013-09-03 10:37:59 +00:00
2013-08-12 22:08:22 +00:00
// Create the modal element and add to the body
2013-08-04 00:53:46 +00:00
utils.addModal = function(id, content) {
var modal = crel('div', {
2013-08-11 00:52:05 +00:00
class: 'modal ' + id
2013-08-04 00:53:46 +00:00
});
modal.innerHTML = content;
2013-08-07 22:34:12 +00:00
document.body.appendChild(modal);
2013-08-04 00:53:46 +00:00
};
2013-05-29 19:55:23 +00:00
2013-08-12 22:08:22 +00:00
// Create a backdrop and add to the body
2014-04-14 00:21:06 +00:00
utils.createBackdrop = function(parent) {
2013-08-12 22:08:22 +00:00
var result = crel('div', {
2014-03-19 22:10:59 +00:00
'class': 'modal-backdrop fade',
2013-08-12 22:08:22 +00:00
});
2014-04-14 00:21:06 +00:00
parent = parent || document.body;
parent.appendChild(result);
2014-03-19 22:10:59 +00:00
result.offsetWidth; // force reflow
result.className = result.className + ' in';
result.removeBackdrop = function() {
result.className = 'modal-backdrop fade';
setTimeout(function() {
result.parentNode.removeChild(result);
}, 150);
};
2013-08-12 22:08:22 +00:00
return result;
};
2013-09-03 10:37:59 +00:00
2013-05-29 19:55:23 +00:00
// Create an centered popup window
utils.popupWindow = function(url, title, width, height) {
var left = (screen.width / 2) - (width / 2);
var top = (screen.height / 2) - (height / 2);
return window.open(url, title, [
'toolbar=no, ',
'location=no, ',
'directories=no, ',
'status=no, ',
'menubar=no, ',
'scrollbars=no, ',
'resizable=no, ',
'copyhistory=no, ',
'width=' + width + ', ',
'height=' + height + ', ',
'top=' + top + ', ',
'left=' + left
].join(""));
};
2014-01-13 18:57:59 +00:00
// Shows a dialog to force the user to click a button before opening oauth popup
var redirectCallbackConfirm;
var redirectCallbackCancel;
utils.redirectConfirm = function(message, callbackConfirm, callbackCancel) {
redirectCallbackConfirm = callbackConfirm;
redirectCallbackCancel = callbackCancel;
$('.modal-redirect-confirm .redirect-msg').html(message);
$('.modal-redirect-confirm').modal("show");
};
2014-03-19 22:10:59 +00:00
2014-01-13 18:57:59 +00:00
utils.init = function() {
$('.action-redirect-confirm').click(function() {
redirectCallbackCancel = undefined;
redirectCallbackConfirm();
});
$('.modal-redirect-confirm').on('hidden.bs.modal', function() {
_.defer(function() {
redirectCallbackCancel && redirectCallbackCancel();
});
});
};
2014-04-02 23:35:07 +00:00
var entityMap = {
"&": "&amp;",
"<": "&lt;",
//">": "&gt;",
'"': '&quot;',
"'": '&#39;',
"/": '&#x2F;',
"\u00a0": ' ',
};
// Escape HTML entities
utils.escape = function(str) {
return String(str).replace(/[&<"'\/\u00a0]/g, function(s) {
return entityMap[s];
});
};
2013-05-29 19:55:23 +00:00
// Export data on disk
2014-01-13 18:57:59 +00:00
utils.saveAs = function(content, filename) {
if(saveAs !== undefined && !/constructor/i.test(window.HTMLElement) /* safari does not support saveAs */) {
2013-09-22 22:16:59 +00:00
if(_.isString(content)) {
content = new Blob([
content
], {
type: "text/plain;charset=utf-8"
});
}
saveAs(content, filename);
2013-05-29 19:55:23 +00:00
}
else {
if(_.isString(content)) {
var uriContent = "data:application/octet-stream;base64," + utils.encodeBase64(content);
window.open(uriContent, 'file');
}
else {
var reader = new FileReader();
reader.onload = function(event) {
2014-01-13 18:57:59 +00:00
utils.redirectConfirm('You are opening a PDF document.', function() {
var uriContent = 'data:application/pdf;' + event.target.result.substring(event.target.result.indexOf('base64'));
window.open(uriContent, 'file');
});
};
2014-01-13 18:57:59 +00:00
reader.readAsDataURL(content); // Convert the blob to base64
}
2013-05-29 19:55:23 +00:00
}
};
// Time shared by others modules
utils.updateCurrentTime = function() {
utils.currentTime = Date.now();
2013-05-29 19:55:23 +00:00
};
utils.updateCurrentTime();
2013-11-05 23:03:38 +00:00
// Serialize sync/publish attributes and store it in the storage
2013-05-29 19:55:23 +00:00
utils.storeAttributes = function(attributes) {
var storeIndex = attributes.syncIndex || attributes.publishIndex;
// Don't store sync/publish index
2013-06-15 01:11:13 +00:00
var storedAttributes = _.omit(attributes, "syncIndex", "publishIndex", "provider");
2013-05-29 19:55:23 +00:00
// Store providerId instead of provider
2013-06-15 01:11:13 +00:00
storedAttributes.provider = attributes.provider.providerId;
2013-11-05 23:03:38 +00:00
storage[storeIndex] = JSON.stringify(storedAttributes);
2013-05-29 19:55:23 +00:00
};
2013-09-03 10:37:59 +00:00
2013-11-05 23:03:38 +00:00
// Retrieve/parse an index array from storage
2013-06-03 22:19:52 +00:00
utils.retrieveIndexArray = function(storeIndex) {
try {
2013-11-05 23:03:38 +00:00
return _.compact(storage[storeIndex].split(";"));
2013-06-03 22:19:52 +00:00
}
catch(e) {
2013-11-05 23:03:38 +00:00
storage[storeIndex] = ";";
2013-06-03 22:19:52 +00:00
return [];
}
};
2013-09-03 10:37:59 +00:00
2013-11-05 23:03:38 +00:00
// Append an index to an array in storage
2013-06-03 22:19:52 +00:00
utils.appendIndexToArray = function(storeIndex, index) {
2013-11-05 23:03:38 +00:00
storage[storeIndex] += index + ";";
2013-06-03 22:19:52 +00:00
};
2013-09-03 10:37:59 +00:00
2013-11-05 23:03:38 +00:00
// Remove an index from an array in storage
2013-06-03 22:19:52 +00:00
utils.removeIndexFromArray = function(storeIndex, index) {
2013-11-05 23:03:38 +00:00
storage[storeIndex] = storage[storeIndex].replace(";" + index + ";", ";");
2013-06-03 22:19:52 +00:00
};
2013-05-29 19:55:23 +00:00
2013-11-05 23:03:38 +00:00
// Retrieve/parse an object from storage. Returns undefined if error.
2013-06-03 22:19:52 +00:00
utils.retrieveIgnoreError = function(storeIndex) {
try {
2013-11-05 23:03:38 +00:00
return JSON.parse(storage[storeIndex]);
2013-06-03 22:19:52 +00:00
}
catch(e) {
return undefined;
}
};
2013-09-03 10:37:59 +00:00
2013-06-16 18:29:54 +00:00
var eventList = [];
utils.logValue = function(value) {
eventList.unshift(value);
if(eventList.length > 5) {
eventList.pop();
}
};
utils.logStackTrace = function() {
eventList.unshift(printStackTrace());
if(eventList.length > 5) {
eventList.pop();
}
};
utils.formatEventList = function() {
var result = [];
_.each(eventList, function(event) {
result.push("\n");
if(_.isString(event)) {
result.push(event);
}
else if(_.isArray(event)) {
result.push(event[5] || "");
result.push(event[6] || "");
}
});
return result.join("");
};
2013-09-03 10:37:59 +00:00
2013-05-29 19:55:23 +00:00
// Base64 conversion
utils.encodeBase64 = function(str) {
if(str.length === 0) {
return "";
}
// UTF-8 to byte array
var bytes = [], offset = 0, length, char;
str = encodeURI(str);
length = str.length;
while (offset < length) {
char = str[offset];
offset += 1;
if('%' !== char) {
bytes.push(char.charCodeAt(0));
}
else {
char = str[offset] + str[offset + 1];
bytes.push(parseInt(char, 16));
offset += 2;
}
}
// byte array to base64
var padchar = '=';
var alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
var i, b10;
var x = [];
var imax = bytes.length - bytes.length % 3;
for (i = 0; i < imax; i += 3) {
b10 = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
x.push(alpha.charAt(b10 >> 18));
x.push(alpha.charAt((b10 >> 12) & 0x3F));
x.push(alpha.charAt((b10 >> 6) & 0x3f));
x.push(alpha.charAt(b10 & 0x3f));
}
switch (bytes.length - imax) {
case 1:
b10 = bytes[i] << 16;
x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) + padchar + padchar);
break;
case 2:
b10 = (bytes[i] << 16) | (bytes[i + 1] << 8);
x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) + alpha.charAt((b10 >> 6) & 0x3f) + padchar);
break;
}
return x.join('');
};
// CRC32 algorithm
var mHash = [
0,
1996959894,
3993919788,
2567524794,
124634137,
1886057615,
3915621685,
2657392035,
249268274,
2044508324,
3772115230,
2547177864,
162941995,
2125561021,
3887607047,
2428444049,
498536548,
1789927666,
4089016648,
2227061214,
450548861,
1843258603,
4107580753,
2211677639,
325883990,
1684777152,
4251122042,
2321926636,
335633487,
1661365465,
4195302755,
2366115317,
997073096,
1281953886,
3579855332,
2724688242,
1006888145,
1258607687,
3524101629,
2768942443,
901097722,
1119000684,
3686517206,
2898065728,
853044451,
1172266101,
3705015759,
2882616665,
651767980,
1373503546,
3369554304,
3218104598,
565507253,
1454621731,
3485111705,
3099436303,
671266974,
1594198024,
3322730930,
2970347812,
795835527,
1483230225,
3244367275,
3060149565,
1994146192,
31158534,
2563907772,
4023717930,
1907459465,
112637215,
2680153253,
3904427059,
2013776290,
251722036,
2517215374,
3775830040,
2137656763,
141376813,
2439277719,
3865271297,
1802195444,
476864866,
2238001368,
4066508878,
1812370925,
453092731,
2181625025,
4111451223,
1706088902,
314042704,
2344532202,
4240017532,
1658658271,
366619977,
2362670323,
4224994405,
1303535960,
984961486,
2747007092,
3569037538,
1256170817,
1037604311,
2765210733,
3554079995,
1131014506,
879679996,
2909243462,
3663771856,
1141124467,
855842277,
2852801631,
3708648649,
1342533948,
654459306,
3188396048,
3373015174,
1466479909,
544179635,
3110523913,
3462522015,
1591671054,
702138776,
2966460450,
3352799412,
1504918807,
783551873,
3082640443,
3233442989,
3988292384,
2596254646,
62317068,
1957810842,
3939845945,
2647816111,
81470997,
1943803523,
3814918930,
2489596804,
225274430,
2053790376,
3826175755,
2466906013,
167816743,
2097651377,
4027552580,
2265490386,
503444072,
1762050814,
4150417245,
2154129355,
426522225,
1852507879,
4275313526,
2312317920,
282753626,
1742555852,
4189708143,
2394877945,
397917763,
1622183637,
3604390888,
2714866558,
953729732,
1340076626,
3518719985,
2797360999,
1068828381,
1219638859,
3624741850,
2936675148,
906185462,
1090812512,
3747672003,
2825379669,
829329135,
1181335161,
3412177804,
3160834842,
628085408,
1382605366,
3423369109,
3138078467,
570562233,
1426400815,
3317316542,
2998733608,
733239954,
1555261956,
3268935591,
3050360625,
752459403,
1541320221,
2607071920,
3965973030,
1969922972,
40735498,
2617837225,
3943577151,
1913087877,
83908371,
2512341634,
3803740692,
2075208622,
213261112,
2463272603,
3855990285,
2094854071,
198958881,
2262029012,
4057260610,
1759359992,
534414190,
2176718541,
4139329115,
1873836001,
414664567,
2282248934,
4279200368,
1711684554,
285281116,
2405801727,
4167216745,
1634467795,
376229701,
2685067896,
3608007406,
1308918612,
956543938,
2808555105,
3495958263,
1231636301,
1047427035,
2932959818,
3654703836,
1088359270,
936918000,
2847714899,
3736837829,
1202900863,
817233897,
3183342108,
3401237130,
1404277552,
615818150,
3134207493,
3453421203,
1423857449,
601450431,
3009837614,
3294710456,
1567103746,
711928724,
3020668471,
3272380065,
1510334235,
755167117
];
utils.crc32 = function(str) {
var n = 0, crc = -1;
2014-03-29 01:22:24 +00:00
str.split('').forEach(function(char) {
n = (crc ^ char.charCodeAt(0)) & 0xFF;
2013-05-29 19:55:23 +00:00
crc = (crc >>> 8) ^ mHash[n];
2014-03-29 01:22:24 +00:00
});
2013-05-29 19:55:23 +00:00
crc = crc ^ (-1);
if(crc < 0) {
crc = 0xFFFFFFFF + crc + 1;
}
return crc.toString(16);
};
2014-03-27 00:20:08 +00:00
window.perfTest = function(cb) {
var startTime = Date.now();
for(var i=0; i<10000; i++) {
cb();
}
console.log('Run 10,000 times in ' + (Date.now() - startTime) + 'ms');
2014-03-29 01:22:24 +00:00
};
2014-03-27 00:20:08 +00:00
2013-05-29 19:55:23 +00:00
return utils;
2013-05-25 18:13:59 +00:00
});