protoblast
Advanced tools
Comparing version 0.6.7 to 0.7.0
@@ -1,9 +0,48 @@ | ||
## 0.6.7 (2019-06-18) | ||
## 0.7.0 (2020-03-13) | ||
* Fix the `magic.js` file breaking the client-side file | ||
* Backport `String.tokenizeHTML()` and `String#encodeHTML()` fixes | ||
* Backport add `Protoblast#version_string` property, use it to add to the `#loaded_versions` property | ||
* Backport `RURL` should keep slashes when no protocol is given | ||
* Backport Fix `Date#timeAgo()` returning big hours, minutes & seconds when date is more than a month ago | ||
* Backport Fix Request class not accepting a RURL instance | ||
* Bumped node.js version requirement to at least v8.9.0 | ||
* Added `Blast.parseUseragent(ua)` method | ||
* You can now add files with `Blast.require()` and specify specific browser versions | ||
* Add `Blast.checksumSymbol` properties to serveral protoblast objects | ||
* Add `modify_prototypes` option to the client template | ||
* You can now also override the rejection error when using `Function.series` or `Function.parallel` | ||
* `String#encodeHTML()` will no longer encode newlines | ||
* Only report Pledge progress when #report_progress is truthy | ||
* Add `Protoblast#version_string` property, use it to add to the `#loaded_versions` property | ||
* `RURL` should keep slashes when no protocol is given | ||
* `Function.enforceProperty` will already add the used symbol to the prototype | ||
* Make `String.decodeAttributes()` decode tag attributes by default (no separator) | ||
* Fix `Date#timeAgo()` returning big hours, minutes & seconds when date is more than a month ago | ||
* Make `Array#sortByPath()` handle comparing `undefined` and objects better | ||
* Passing `headers` option to `Request#setOptions()` will no longer unset earlier headers | ||
* Add `RegExp.interpretWildcard(str, flags)` | ||
* Add some tweaks to `Function.tokenize` so it'll recognize regular expressions better | ||
* Fix `Object.walk` throwing an error when handling `Object.create(null)` objects | ||
* Add more easing methods to the `Math` object | ||
* The `State` class will no longer turn off-line when a stale request times out | ||
* Rename `State#online_status_duration` property to `State#current_status_duration` | ||
* Abort xhrs made by `Request` when a timeout occurs | ||
* Add `delay` option to `Function.throttle()` to always enforce a minimum delay | ||
* Passing a single object to `RURL#param()` will now set the key-values as parameters | ||
* Add detection for Electron, like Nwjs | ||
* Add some infinite-loop prevention to `Function.enforceProperty` setter/getters | ||
* `Request` now has a `max_timeout` property that defaults to 30 seconds, instead of a hardcoded 8 seconds | ||
* Remove the `__enumerate` trap from the Magic class | ||
* Added `Pledge.done(promise, callback)` as a static method | ||
* Add `time_started` and `time_ended` properties to the `Request` class | ||
* Add `Array#safesort()`, which won't throw an error when sorting Objects with no prototype | ||
* Don't trust the `type` property of an XMLHttpRequest's `response` object | ||
* Add locale support to `Date#format()` | ||
* Add `cast` argument to `String#assign()` method | ||
* Speed up the `String#underscore()` method | ||
* `String#decodeJSONURI()` will now only try to decode JSON if it contains certain json characters | ||
* Remove the `Array#createIterator()` method as it breaks CKEditor | ||
* Add `Date.secondsToDuration()` | ||
* Add `Date.firstWeekOfYear(year)` & `Date.firstDayOfWeek(year, week)` | ||
* Allow retries of `Blast.getClientPath()` | ||
* Add `Request#download_if_inline` boolean option | ||
* Add the `StringBuilder` class | ||
* Allow passing another pledge instance in `Pledge#done()` | ||
* Add `Pledge.isThenable()` and `Pledge.hasPromiseInterface()` | ||
* Add `Pledge.cast()` to turn something into a pledge | ||
@@ -10,0 +49,0 @@ ## 0.6.6 (2019-02-25) |
@@ -850,2 +850,10 @@ module.exports = function BlastArray(Blast, Collection, Bound, Obj) { | ||
return ord; | ||
} else if (alpha === beta) { | ||
// Ignore | ||
} else { | ||
if (alpha == null) { | ||
return 0 - ord; | ||
} else if (beta == null) { | ||
return ord; | ||
} | ||
} | ||
@@ -1019,15 +1027,2 @@ } | ||
/** | ||
* Create an iterator for this array | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.1.2 | ||
* @version 0.1.2 | ||
* | ||
* @return {Iterator} | ||
*/ | ||
Blast.definePrototype('Array', function createIterator() { | ||
return new Blast.Classes.Iterator(this); | ||
}); | ||
/** | ||
* Get all elements after the given needle | ||
@@ -1034,0 +1029,0 @@ * |
@@ -332,2 +332,3 @@ module.exports = function BlastBenchmark(Blast, Collection, Bound) { | ||
if (callback) { | ||
result.name = fn.name || ''; | ||
callback(null, result); | ||
@@ -391,2 +392,3 @@ } else { | ||
if (callback) { | ||
result.name = fn.name || ''; | ||
callback(err, result); | ||
@@ -393,0 +395,0 @@ } else { |
@@ -78,3 +78,3 @@ module.exports = function BlastBrowserShims(Blast, Collection, Bound) { | ||
* @since 0.1.4 | ||
* @version 0.1.4 | ||
* @version 0.7.0 | ||
* | ||
@@ -87,9 +87,6 @@ * @param {String} html | ||
var result, | ||
wrap; | ||
var result; | ||
wrap = document.createElement('div'); | ||
wrap.innerHTML = html; | ||
result = wrap.children; | ||
result = document.createRange().createContextualFragment(html); | ||
result = result.childNodes; | ||
@@ -96,0 +93,0 @@ if (result.length > 1) { |
(function() { | ||
var useCommon, | ||
client_extras = []; | ||
var modify_prototypes, | ||
client_extras = [], | ||
use_common; | ||
@@ -11,3 +12,5 @@ function require(p){ | ||
if (!mod) throw new Error('failed to require "' + p + '"'); | ||
if (!mod) { | ||
throw new Error('failed to require "' + p + '"'); | ||
} | ||
@@ -24,3 +27,3 @@ if (!mod.exports) { | ||
require.resolve = function (path){ | ||
require.resolve = function resolve(path) { | ||
var orig = path, | ||
@@ -35,10 +38,13 @@ reg = path + '.js', | ||
require.register = function (path, fn){ | ||
require.register = function register(path, fn){ | ||
require.modules[path] = fn; | ||
}; | ||
require.relative = function (parent) { | ||
return function(p){ | ||
if ('.' != p.substr(0, 1)) return require(p); | ||
require.relative = function relative(parent) { | ||
return function gotRelative(p) { | ||
if ('.' != p.substr(0, 1)) { | ||
return require(p); | ||
} | ||
var path = parent.split('/'), | ||
@@ -61,3 +67,3 @@ segs = p.split('/'); | ||
if (useCommon) { | ||
if (use_common) { | ||
if (typeof module !== 'undefined' && module.exports) { | ||
@@ -69,5 +75,5 @@ module.exports = require('init.js'); | ||
} else { | ||
self.Protoblast = require('init.js')(); | ||
self.Protoblast = require('init.js')(modify_prototypes); | ||
} | ||
}()); |
@@ -10,3 +10,3 @@ /** | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy | ||
* of this software and associated documentation files (the "Software"), to deal | ||
* of this software and associated documentation files (the 'Software'), to deal | ||
* in the Software without restriction, including without limitation the rights | ||
@@ -19,3 +19,3 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
@@ -30,12 +30,34 @@ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
var shortMonths, | ||
var has_full_icu, | ||
shortMonths, | ||
longMonths, | ||
shortDays, | ||
longDays, | ||
methods; | ||
methods, | ||
test; | ||
shortMonths = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; | ||
longMonths = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; | ||
shortDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; | ||
longDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; | ||
shortMonths = [ | ||
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', | ||
'jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec', | ||
'janv', 'févr', 'mars', 'avr', 'mai', 'juin', 'juil', 'août', 'sept', 'oct', 'nov', 'déc' | ||
]; | ||
longMonths = [ | ||
'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', | ||
'januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december', | ||
'janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre' | ||
]; | ||
shortDays = [ | ||
'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', | ||
'ma', 'di', 'wo', 'do', 'vr', 'za', 'zo', | ||
'lun', 'mar', 'mer', 'jeu', 'ven', 'sam', 'dim' | ||
]; | ||
longDays = [ | ||
'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', | ||
'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag', 'zondag', | ||
'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche' | ||
]; | ||
methods = {}; | ||
@@ -49,2 +71,12 @@ | ||
if (Blast.isNode) { | ||
test = new Intl.DateTimeFormat('es',{month:'long'}).format(new Date(9E8)); | ||
if (test == 'enero') { | ||
has_full_icu = true; | ||
} | ||
} else { | ||
has_full_icu = true; | ||
} | ||
/** | ||
@@ -55,7 +87,11 @@ * Format a date | ||
* @since 0.1.4 | ||
* @version 0.1.12 | ||
* @version 0.7.0 | ||
* | ||
* @param {String} pattern | ||
* @param {String} locale | ||
* | ||
* @return {String} | ||
*/ | ||
Blast.definePrototype('Date', function format(pattern) { | ||
Blast.definePrototype('Date', function format(pattern, locale) { | ||
var date = this; | ||
@@ -68,3 +104,3 @@ | ||
return pattern.replace(/(\\?)(.)/g, function(_, esc, chr) { | ||
return (esc === '' && methods[chr]) ? methods[chr].call(date) : chr; | ||
return (esc === '' && methods[chr]) ? methods[chr].call(date, locale) : chr; | ||
}); | ||
@@ -74,2 +110,24 @@ }); | ||
/** | ||
* Get the date locale | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @return {String} | ||
*/ | ||
Blast.definePrototype('Date', function getLocale(locale) { | ||
if (locale) { | ||
return locale; | ||
} | ||
if (this.locale) { | ||
return this.locale; | ||
} | ||
return 'en'; | ||
}); | ||
/** | ||
* Day of the month, 2 digits with leading zeros | ||
@@ -89,3 +147,3 @@ * | ||
/** | ||
* A textual representation of a day, three letters | ||
* A short textual representation of a day | ||
* | ||
@@ -95,8 +153,23 @@ * @author Jacob Wright | ||
* @since 0.1.4 | ||
* @version 0.1.4 | ||
* @version 0.7.0 | ||
* | ||
* @return {String} | ||
*/ | ||
methods.D = function getDayName() { | ||
return shortDays[this.getDay()]; | ||
methods.D = function getDayName(locale) { | ||
locale = this.getLocale(locale); | ||
if (!has_full_icu && locale != 'en') { | ||
let weekday = methods.N.call(this) - 1; | ||
if (locale == 'nl') { | ||
weekday += 7; | ||
} else if (locale == 'fr') { | ||
weekday += 14; | ||
} | ||
return shortDays[weekday]; | ||
} | ||
return this.toLocaleDateString(locale, {weekday: 'short'}); | ||
}; | ||
@@ -124,8 +197,22 @@ | ||
* @since 0.1.4 | ||
* @version 0.1.4 | ||
* @version 0.7.0 | ||
* | ||
* @return {String} | ||
*/ | ||
methods.l = function getDayName() { | ||
return longDays[this.getDay()]; | ||
methods.l = function getDayName(locale) { | ||
locale = this.getLocale(locale); | ||
if (!has_full_icu && locale != 'en') { | ||
let weekday = methods.N.call(this) - 1; | ||
if (locale == 'nl') { | ||
weekday += 7; | ||
} else if (locale == 'fr') { | ||
weekday += 14; | ||
} | ||
return longDays[weekday]; | ||
} | ||
return this.toLocaleDateString(locale, {weekday: 'long'}); | ||
}; | ||
@@ -211,6 +298,60 @@ | ||
/** | ||
* A textual representation of a month, three letters | ||
* | ||
* @author Jacob Wright | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.1.4 | ||
* @version 0.7.0 | ||
* | ||
* @return {String} | ||
*/ | ||
methods.M = function getShortMonthName(locale) { | ||
var locale = this.getLocale(locale); | ||
if (!has_full_icu && locale != 'en') { | ||
let month = this.getMonth(); | ||
if (locale == 'nl') { | ||
month += 12; | ||
} else if (locale == 'fr') { | ||
month += 24; | ||
} | ||
return shortMonths[month]; | ||
} | ||
return this.toLocaleDateString(locale, {month: 'short'}); | ||
}; | ||
/** | ||
* A textual representation of the month, long version | ||
* | ||
* @author Jacob Wright | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.1.4 | ||
* @version 0.7.0 | ||
* | ||
* @return {String} | ||
*/ | ||
methods.F = function getLongMonthName(locale) { | ||
var locale = this.getLocale(locale); | ||
if (!has_full_icu && locale != 'en') { | ||
let month = this.getMonth(); | ||
if (locale == 'nl') { | ||
month += 12; | ||
} else if (locale == 'fr') { | ||
month += 24; | ||
} | ||
return longMonths[month]; | ||
} | ||
return this.toLocaleDateString(locale, {month: 'long'}); | ||
}; | ||
// Month | ||
methods.F = function() { return longMonths[this.getMonth()]; }; | ||
methods.m = function() { return (this.getMonth() < 9 ? '0' : '') + (this.getMonth() + 1); }; | ||
methods.M = function() { return shortMonths[this.getMonth()]; }; | ||
methods.n = function() { return this.getMonth() + 1; }; | ||
@@ -238,3 +379,3 @@ methods.t = function() { var d = this; return new Date(d.getFullYear(), d.getMonth(), 0).getDate() }; | ||
// Timezone | ||
methods.e = function() { return "Not Yet Supported"; }; | ||
methods.e = function() { return 'Not Yet Supported'; }; | ||
methods.I = function() { | ||
@@ -258,3 +399,3 @@ var DST = null; | ||
// Full Date/Time | ||
methods.c = function() { return this.format("Y-m-d\\TH:i:sP"); }; | ||
methods.c = function() { return this.format('Y-m-d\\TH:i:sP'); }; | ||
methods.r = function() { return this.toString(); }; | ||
@@ -261,0 +402,0 @@ methods.U = function() { return this.getTime() / 1000; }; |
277
lib/date.js
@@ -96,9 +96,10 @@ module.exports = function BlastDate(Blast, Collection, Bound, Obj) { | ||
* @since 0.5.7 | ||
* @version 0.5.7 | ||
* @version 0.7.0 | ||
* | ||
* @param {String} unit | ||
* @param {Date} context | ||
* | ||
* @return {Number} | ||
*/ | ||
Blast.defineStatic('Date', function getUnitMs(unit) { | ||
Blast.defineStatic('Date', function getUnitMs(unit, context) { | ||
@@ -108,5 +109,13 @@ var result = ms_units[unit]; | ||
if (!result) { | ||
result = ms_units[unit.toLowerCase()]; | ||
unit = unit.toLowerCase(); | ||
result = ms_units[unit]; | ||
} | ||
// Do a leap year check if we need to add a year | ||
if (context != null && unit == 'y' || unit == 'yr' || unit == 'year') { | ||
if (Bound.Date.isLeapYear(context)) { | ||
result += ms_units.day; | ||
} | ||
} | ||
return result || 0; | ||
@@ -324,2 +333,65 @@ }); | ||
/** | ||
* Get the first week of the year | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @param {Number} year | ||
* | ||
* @return {Date} | ||
*/ | ||
Blast.defineStatic('Date', function firstWeekOfYear(year) { | ||
var day_offset, | ||
date = new Date(year, 0, 1), | ||
day = date.getDay(); | ||
// Make the days monday-sunday equals to 1-7 instead of 0-6 | ||
day = (day === 0) ? 7 : day; | ||
// day_offset will correct the date in order to get a Monday | ||
day_offset = -day + 1; | ||
if (7 - day + 1 < 4) { | ||
// the current week has not the minimum 4 days | ||
// required by iso 8601 => add one week | ||
day_offset += 7; | ||
} | ||
return new Date(date.getTime() + day_offset * 24 * 60 * 60 * 1000); | ||
}); | ||
/** | ||
* Return the date of the first day of the given week | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @param {Number} year | ||
* @param {Number} week | ||
* | ||
* @return {Date} | ||
*/ | ||
Blast.defineStatic('Date', function firstDayOfWeek(week, year) { | ||
var target_time, | ||
week_time, | ||
date; | ||
if (year == null) { | ||
year = (new Date()).getFullYear(); | ||
} | ||
date = Bound.Date.firstWeekOfYear(year); | ||
week_time = 1000 * 60 * 60 * 24 * 7 * (week - 1); | ||
target_time = date.getTime() + week_time; | ||
date.setTime(target_time); | ||
return date; | ||
}); | ||
/** | ||
* Timestamp getter | ||
@@ -459,3 +531,3 @@ * | ||
unittime = Collection.Date.getUnitMs(unit); | ||
unittime = Collection.Date.getUnitMs(unit, this); | ||
@@ -750,2 +822,16 @@ newtime += unittime * amount; | ||
/** | ||
* Is this in a leap year? | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @return {Boolean} | ||
*/ | ||
Blast.definePrototype('Date', function isLeapYear() { | ||
var year = this.getFullYear(); | ||
return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); | ||
}); | ||
/** | ||
* Determine if the current date is between (or equal to) start and end. | ||
@@ -812,2 +898,27 @@ * | ||
/** | ||
* Time duration strings | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.6.0 | ||
* @version 0.6.0 | ||
* | ||
* @return {String} | ||
*/ | ||
Blast.definePrototype('Date', 'time_duration_settings', { | ||
second : '1s', | ||
seconds : '%d seconds', | ||
minute : '1 minute', | ||
minutes : '%d minutes', | ||
hour : '1 hour', | ||
hours : '%d hours', | ||
day : '1 day', | ||
days : '%d days', | ||
month : '1 month', | ||
months : '%d months', | ||
year : '1 year', | ||
years : '%d years', | ||
separator : ' ' | ||
}); | ||
function numberString(string, number) { | ||
@@ -821,5 +932,6 @@ return string.replace(/%d/i, number); | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.6.0 | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @param {Number} ms | ||
* @param {Object} settings | ||
@@ -829,7 +941,27 @@ * | ||
*/ | ||
Blast.definePrototype('Date', function timeAgo(settings) { | ||
Blast.defineStatic('Date', function timeDuration(ms, settings) { | ||
var difference = Date.now() - this, | ||
seconds = Math.abs(difference) / 1000, | ||
minutes = seconds / 60, | ||
if (typeof ms == 'object') { | ||
settings = ms; | ||
ms = settings.ms; | ||
} | ||
return Bound.Date.secondsToDuration(ms / 1000, settings); | ||
}); | ||
/** | ||
* Return a time ago string | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @param {Number} seconds | ||
* @param {Object} settings | ||
* | ||
* @return {String} | ||
*/ | ||
Blast.defineStatic('Date', function secondsToDuration(seconds, settings) { | ||
var minutes = seconds / 60, | ||
hours = minutes / 60, | ||
@@ -839,3 +971,6 @@ days = ~~(hours / 24), | ||
result = '', | ||
future = difference < 0, | ||
minimize = false, | ||
base_settings, | ||
context, | ||
future, | ||
months, | ||
@@ -850,8 +985,26 @@ temp; | ||
if (this.time_ago_settings) { | ||
context = this; | ||
} else { | ||
context = Bound.Date.prototype; | ||
} | ||
if (!settings || !settings.time_ago) { | ||
base_settings = context.time_duration_settings; | ||
} else { | ||
base_settings = context.time_ago_settings; | ||
} | ||
if (!settings) { | ||
settings = this.time_ago_settings; | ||
settings = base_settings; | ||
} else { | ||
settings = Object.assign({}, this.time_ago_settings, settings); | ||
settings = Object.assign({}, base_settings, settings); | ||
if (settings.minimize != null) { | ||
minimize = settings.minimize; | ||
} | ||
} | ||
future = settings.future; | ||
if (years == 1) { | ||
@@ -876,3 +1029,3 @@ result = numberString(settings.year, 1); | ||
if (!years) { | ||
if (!years || !minimize) { | ||
@@ -883,31 +1036,44 @@ if (days == 1) { | ||
temp = numberString(settings.days, days); | ||
} else if (!months && hours == 1) { | ||
temp = numberString(settings.hour, 1); | ||
} else if (!months && hours > 1) { | ||
temp = numberString(settings.hours, hours); | ||
} else if (!months && minutes == 1) { | ||
temp = numberString(settings.minute, 1); | ||
} else if (!months && minutes > 1) { | ||
temp = numberString(settings.minutes, minutes); | ||
} else if (!months && (future || seconds > 30)) { | ||
temp = numberString(settings.seconds, seconds); | ||
} else if (!result) { | ||
return numberString(settings.now, seconds); | ||
} else if (!minimize) { | ||
temp = numberString(settings.days, 0); | ||
} | ||
if (temp) { | ||
if (result) { | ||
result += settings.separator; | ||
// Add the day | ||
result = addToResult(result, temp, settings); | ||
if (!minimize || !months) { | ||
if (hours == 1) { | ||
result = addToResult(result, numberString(settings.hour, 1), settings); | ||
} else if ((!result && hours) || (!minimize || (minimize && hours))) { | ||
result = addToResult(result, numberString(settings.hours, hours), settings); | ||
} | ||
result += temp; | ||
if (!minimize || !result) { | ||
if (minutes == 1) { | ||
result = addToResult(result, numberString(settings.minute, 1), settings); | ||
} else if ((!result && minutes) || (!minimize || (minimize && minutes))) { | ||
result = addToResult(result, numberString(settings.minutes, minutes), settings); | ||
} | ||
} | ||
if (!minimize || !result) { | ||
if (seconds > 30 || future) { | ||
result = addToResult(result, numberString(settings.seconds, seconds), settings); | ||
} | ||
} | ||
} | ||
if (!result) { | ||
return numberString(settings.now, seconds); | ||
} | ||
} | ||
result += ' '; | ||
if (settings.time_ago) { | ||
result += ' '; | ||
if (future) { | ||
result += settings.from; | ||
} else { | ||
result += settings.ago; | ||
if (future) { | ||
result += settings.from; | ||
} else { | ||
result += settings.ago; | ||
} | ||
} | ||
@@ -917,2 +1083,45 @@ | ||
}); | ||
function addToResult(current, addition, settings) { | ||
if (!addition) { | ||
return current; | ||
} | ||
if (current) { | ||
current += settings.separator; | ||
} else { | ||
current = ''; | ||
} | ||
return current + addition; | ||
} | ||
/** | ||
* Return a time ago string | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.6.0 | ||
* @version 0.7.0 | ||
* | ||
* @param {Object} settings | ||
* | ||
* @return {String} | ||
*/ | ||
Blast.definePrototype('Date', function timeAgo(settings) { | ||
var difference = Date.now() - this, | ||
seconds = Math.abs(difference) / 1000, | ||
future = difference < 0; | ||
if (!settings) { | ||
settings = {}; | ||
} | ||
settings.future = future; | ||
settings.time_ago = true; | ||
settings.minimize = true; | ||
return Bound.Date.secondsToDuration(seconds, settings); | ||
}); | ||
}; |
@@ -541,3 +541,3 @@ module.exports = function BlastDeck(Blast, Collection) { | ||
* @since 0.1.2 | ||
* @version 0.1.4 | ||
* @version 0.7.0 | ||
* | ||
@@ -550,3 +550,3 @@ * @return {Iterator} | ||
return Collection.Array.prototype.createIterator.call(values); | ||
return new Blast.Classes.Iterator(values); | ||
}); | ||
@@ -559,3 +559,3 @@ | ||
* @since 0.1.2 | ||
* @version 0.1.4 | ||
* @version 0.7.0 | ||
* | ||
@@ -568,3 +568,3 @@ * @return {Iterator} | ||
return Collection.Array.prototype.createIterator.call(items); | ||
return new Blast.Classes.Iterator(items); | ||
}); | ||
@@ -571,0 +571,0 @@ |
@@ -141,3 +141,3 @@ module.exports = function BlastDiacritics(Blast, Collection, Bound) { | ||
* @since 0.0.1 | ||
* @version 0.6.4 | ||
* @version 0.7.0 | ||
*/ | ||
@@ -158,3 +158,3 @@ Blast.definePrototype('String', function romanize() { | ||
// Remove any other combining marks | ||
result = Bound.String.removeCombiningMarks(result); | ||
result = Collection.String.prototype.removeCombiningMarks.call(result); | ||
@@ -161,0 +161,0 @@ return result; |
@@ -135,3 +135,3 @@ module.exports = function BlastDecorators(Blast, Collection) { | ||
* @since 0.6.2 | ||
* @version 0.6.2 | ||
* @version 0.7.0 | ||
* | ||
@@ -153,2 +153,3 @@ * @param {Object|Number} config Config object or timeout in ms | ||
config.reset_on_call = config.reset_on_call || false; | ||
config.delay = config.delay || 0; | ||
@@ -155,0 +156,0 @@ if (config.method == null) { |
@@ -97,3 +97,3 @@ module.exports = function BlastFunctionFlow(Blast, Collection, Bound) { | ||
* @since 0.1.2 | ||
* @version 0.6.3 | ||
* @version 0.7.0 | ||
* | ||
@@ -217,5 +217,3 @@ * @param {Boolean} _forceAsync Force asynchronous behaviour [TRUE] | ||
if (err) { | ||
stop = true; | ||
pledge.reject(err); | ||
return callback(err); | ||
return doRejection(err); | ||
} | ||
@@ -229,4 +227,9 @@ | ||
// Increment the counter | ||
next = keys[++i]; | ||
i += 1; | ||
if (i < length) { | ||
// Increment the counter | ||
next = keys[i]; | ||
} | ||
// Report the progress | ||
@@ -236,3 +239,3 @@ pledge.reportProgressPart(1); | ||
// See if we need to do another task | ||
if (tasks[next]) { | ||
if (i < length && tasks[next]) { | ||
scheduler(function nextSerialTask() { | ||
@@ -245,6 +248,4 @@ | ||
if (count == 1) { | ||
stop = true; | ||
err = new Error('Next handler has been called multiple times'); | ||
pledge.reject(err); | ||
return callback(err); | ||
return doRejection(err); | ||
} else if (count > 1) { | ||
@@ -273,5 +274,3 @@ // Just ignore further calls | ||
stop = true; | ||
pledge.reject(err); | ||
return callback(err); | ||
return doRejection(err); | ||
} | ||
@@ -299,2 +298,20 @@ }); | ||
function doRejection(err) { | ||
stop = true; | ||
if (callback.name != 'throwWhenNotCaught') { | ||
let temp = callback(err); | ||
if (temp && temp.constructor && ~temp.constructor.name.indexOf('Error')) { | ||
err = temp; | ||
} | ||
pledge.reject(err); | ||
} else { | ||
pledge.reject(err); | ||
callback(err); | ||
} | ||
} | ||
return pledge; | ||
@@ -467,5 +484,3 @@ }); | ||
if (err) { | ||
stop = true; | ||
pledge.reject(err); | ||
return callback(err); | ||
return doRejection(err); | ||
} | ||
@@ -493,6 +508,4 @@ | ||
if (count == 1) { | ||
stop = true; | ||
err = new Error('Next handler has been called multiple times'); | ||
pledge.reject(err); | ||
return callback(err); | ||
return doRejection(err); | ||
} else if (count > 1) { | ||
@@ -508,5 +521,3 @@ // Just ignore further calls | ||
} catch (err) { | ||
stop = true; | ||
pledge.reject(err); | ||
return callback(err); | ||
doRejection(err); | ||
} | ||
@@ -554,5 +565,3 @@ }); | ||
stop = true; | ||
pledge.reject(err); | ||
return callback(err); | ||
doRejection(err); | ||
} | ||
@@ -582,2 +591,20 @@ }); | ||
function doRejection(err) { | ||
stop = true; | ||
if (callback.name != 'throwWhenNotCaught') { | ||
let temp = callback(err); | ||
if (temp && temp.constructor && ~temp.constructor.name.indexOf('Error')) { | ||
err = temp; | ||
} | ||
pledge.reject(err); | ||
} else { | ||
pledge.reject(err); | ||
callback(err); | ||
} | ||
} | ||
return pledge; | ||
@@ -1135,3 +1162,3 @@ }); | ||
* @since 0.1.9 | ||
* @version 0.6.5 | ||
* @version 0.7.0 | ||
* | ||
@@ -1176,2 +1203,3 @@ * @param {Function} fnc Function to throttle | ||
context, | ||
timer, | ||
that, | ||
@@ -1229,2 +1257,10 @@ args, | ||
timer = minimum_wait - ms_since_last_exec; | ||
// If a delay is configured, | ||
// make sure the function isn't called before that | ||
if (config.delay && timer < config.delay) { | ||
timer = config.delay; | ||
} | ||
context[queued] = setTimeout(function throttleQueue() { | ||
@@ -1248,3 +1284,3 @@ | ||
}, minimum_wait - ms_since_last_exec); | ||
}, timer); | ||
} | ||
@@ -1251,0 +1287,0 @@ }; |
@@ -127,3 +127,3 @@ module.exports = function BlastInheritance(Blast, Collection) { | ||
* @since 0.2.1 | ||
* @version 0.6.5 | ||
* @version 0.7.0 | ||
* | ||
@@ -136,3 +136,4 @@ * @param {String} namespace | ||
var result, | ||
var file_options, | ||
result, | ||
name, | ||
@@ -148,2 +149,12 @@ data; | ||
if (Blast.isNode && namespace && (file_options = Blast[Blast.ACTIVE_FILE])) { | ||
if (!file_options.used_namespaces) { | ||
file_options.used_namespaces = []; | ||
} | ||
if (file_options.used_namespaces.indexOf(namespace) == -1) { | ||
file_options.used_namespaces.push(namespace); | ||
} | ||
} | ||
if (result == null) { | ||
@@ -1395,3 +1406,3 @@ name = namespace.split('.'); | ||
* @since 0.6.6 | ||
* @version 0.6.6 | ||
* @version 0.7.0 | ||
* | ||
@@ -1404,3 +1415,4 @@ * @param {Function} target Target object or function | ||
var symbol, | ||
var setting_symbol, | ||
symbol, | ||
keys; | ||
@@ -1417,13 +1429,38 @@ | ||
symbol = Symbol(keys[0]); | ||
setting_symbol = Symbol('setting_' + keys[0]); | ||
// Already add the symbol to the prototype | ||
Fn.setProperty(target, symbol, undefined); | ||
Fn.setProperty(target, setting_symbol, false); | ||
// Prepare the setter wrapper function | ||
function _setter(value) { | ||
if (this[setting_symbol]) { | ||
return; | ||
} | ||
this[setting_symbol] = true; | ||
try { | ||
this[symbol] = setter.call(this, value, this[symbol]); | ||
this[setting_symbol] = false; | ||
} catch (err) { | ||
this[setting_symbol] = false; | ||
throw err; | ||
} | ||
return this[symbol]; | ||
} | ||
return Fn.setProperty(target, keys, function getter() { | ||
if (this[symbol] == null && !(symbol in this)) { | ||
this[symbol] = setter.call(this); | ||
let val = this[symbol]; | ||
if (!this[setting_symbol] && val == null && typeof val == 'undefined') { | ||
_setter.call(this); | ||
} | ||
return this[symbol]; | ||
}, function _setter(value) { | ||
return this[symbol] = setter.call(this, value, this[symbol]); | ||
}); | ||
}, _setter); | ||
}); | ||
@@ -1430,0 +1467,0 @@ |
@@ -10,2 +10,3 @@ module.exports = function BlastFunction(Blast, Collection) { | ||
operators, | ||
rx_regex, | ||
keywords; | ||
@@ -96,2 +97,4 @@ | ||
rx_regex = /^\/(?:(?:\\\/|[^\n\/]))*?\/(?:[gimuy]*)$/; | ||
patternNames = { | ||
@@ -105,5 +108,6 @@ string1 : 'string', | ||
Blast.ready(function BlastReadyFunction() { | ||
Blast._fn_token_prepare = function BlastReadyFunction() { | ||
var temp, | ||
var patterns = [], | ||
temp, | ||
name, | ||
@@ -127,5 +131,9 @@ key, | ||
for (key in token_patterns) { | ||
patterns.push(token_patterns[key]); | ||
} | ||
try { | ||
// Create the matches | ||
tokenMatches = Collection.RegExp.combine.apply(null, Collection.Object.values(token_patterns)); | ||
tokenMatches = Collection.RegExp.combine.apply(null, patterns); | ||
} catch (err) { | ||
@@ -144,3 +152,3 @@ combineError = err; | ||
haveCombined = true; | ||
}); | ||
}; | ||
@@ -198,3 +206,3 @@ /** | ||
* @since 0.3.8 | ||
* @version 0.3.8 | ||
* @version 0.7.0 | ||
* | ||
@@ -209,2 +217,7 @@ * @param {String} name The name of the function to test | ||
// Do a simple space check | ||
if (name.indexOf(' ') > -1) { | ||
return false; | ||
} | ||
try { | ||
@@ -257,3 +270,3 @@ eval('result = function ' + name + '() {};'); | ||
* @since 0.1.2 | ||
* @version 0.1.2 | ||
* @version 0.7.0 | ||
* | ||
@@ -302,3 +315,10 @@ * @param {String} sourceCode | ||
let was_declaring, | ||
declaring, | ||
assigning, | ||
result = [], | ||
prev; | ||
for (i = 0; i < tokens.length; i++) { | ||
was_declaring = declaring; | ||
@@ -310,4 +330,38 @@ obj = { | ||
if (declaring) { | ||
if (obj.value == ';') { | ||
declaring = false; | ||
assigning = false; | ||
} else { | ||
if (assigning && obj.value == ',') { | ||
assigning = false; | ||
} else if (obj.value == '=') { | ||
assigning = true; | ||
} | ||
if (obj.type != 'whitespace' && obj.type != 'name') { | ||
if (assigning) { | ||
if (obj.type != 'punct' && obj.value != 'this') { | ||
assigning = false; | ||
} | ||
} else if (obj.value != '=' && obj.value != ',') { | ||
declaring = false; | ||
} | ||
} | ||
} | ||
} | ||
if (obj.type == 'keyword') { | ||
obj.name = obj.value; | ||
if (obj.value !== 'this') { | ||
if (obj.value == 'var' || obj.value == 'let' || obj.value == 'const') { | ||
declaring = true; | ||
} else { | ||
declaring = false; | ||
} | ||
} | ||
} else if (operators[tokens[i]]) { | ||
@@ -317,10 +371,71 @@ obj.name = operators[tokens[i]]; | ||
if (obj.value === '/') { | ||
i = checkTogenizeRegex(i, tokens, obj, prev, was_declaring, assigning); | ||
} | ||
// Replace the original string with the object | ||
tokens[i] = obj; | ||
result.push(obj); | ||
if (obj.type != 'whitespace') { | ||
prev = obj; | ||
} | ||
} | ||
return tokens; | ||
return result; | ||
}); | ||
/** | ||
* Fix regex literals in tokenized objects | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @param {Number} index The current index | ||
* @param {Array} tokens Array of all the tokens | ||
* @param {Object} obj The current object | ||
* @param {Object} prev The previous object | ||
* @param {Boolean} declaring Are we currently declaring something? | ||
* | ||
* @return {Number} | ||
*/ | ||
function checkTogenizeRegex(index, tokens, obj, prev, declaring, assigning) { | ||
if (prev && (prev.type != 'punct' && prev.type != 'parens')) { | ||
if (!declaring || ((assigning && prev.type == 'name') || prev.type == 'string' || prev.type == 'number')) { | ||
return index; | ||
} | ||
} | ||
let matched = false, | ||
next = tokens[index + 1], | ||
temp = obj.value, | ||
str = obj.value, | ||
i; | ||
for (i = index + 1; i < tokens.length; i++) { | ||
temp += tokens[i]; | ||
if (rx_regex.test(temp)) { | ||
matched = i; | ||
str = temp; | ||
} else { | ||
// If it matched before, that'll be correct | ||
if (matched) { | ||
i = matched; | ||
break; | ||
} | ||
} | ||
} | ||
if (str.length > 1) { | ||
obj.value = str; | ||
obj.type = obj.name = 'regexp'; | ||
return i; | ||
} | ||
return index; | ||
} | ||
/** | ||
* Get the name of a function's arguments | ||
@@ -428,3 +543,3 @@ * | ||
* @since 0.1.0 | ||
* @version 0.3.9 | ||
* @version 0.7.0 | ||
* | ||
@@ -454,3 +569,3 @@ * @param {String} name The name to use for the wrapper | ||
if (!Collection.Function.isNameAllowed(name)) { | ||
name = '_' + name; | ||
name = '_' + Collection.String.prototype.slug.call(name, '_'); | ||
} | ||
@@ -494,3 +609,3 @@ | ||
* @since 0.1.0 | ||
* @version 0.3.9 | ||
* @version 0.7.0 | ||
* | ||
@@ -520,3 +635,3 @@ * @param {String} name The name to use for the wrapper | ||
if (!Collection.Function.isNameAllowed(name)) { | ||
name = '_' + name; | ||
name = '_' + Collection.String.prototype.slug.call(name, '_'); | ||
} | ||
@@ -523,0 +638,0 @@ |
@@ -149,8 +149,10 @@ /** | ||
// These are regular expressions used for converting between String formats | ||
id_suffix: RegExp('(_ids|_id)$', 'g'), | ||
underbar: RegExp('_', 'g'), | ||
space_or_underbar: RegExp('[\ _]', 'g'), | ||
uppercase: RegExp('([A-Z])', 'g'), | ||
underbar_prefix: RegExp('^_'), | ||
id_suffix : RegExp('(_ids|_id)$', 'g'), | ||
underbar : RegExp('_', 'g'), | ||
space_or_underbar : RegExp('[\ _]', 'g'), | ||
uppercase : RegExp('([A-Z])', 'g'), | ||
underbar_prefix : RegExp('^_'), | ||
spaces_or_underscores : /[\s_]+/g, | ||
underscores : /_+/g, | ||
/* | ||
@@ -202,3 +204,3 @@ This is a helper method that applies rules based replacement to a String | ||
Blast.ready(function getBoundString() { | ||
Blast.once('pre-extra-files', function getBoundString() { | ||
S = Blast.Bound.String; | ||
@@ -450,3 +452,3 @@ }); | ||
* @since 0.0.1 | ||
* @version 0.1.0 | ||
* @version 0.7.0 | ||
* | ||
@@ -456,20 +458,71 @@ * @return {String} | ||
Blast.definePrototype('String', function underscore() { | ||
var str = this, | ||
str_path, | ||
var previous_underscore = true, // Act as if it already starts with an underscore | ||
result = '', | ||
length = this.length, | ||
code, | ||
char, | ||
i; | ||
str_path = str.split('::'); | ||
for (i = 0; i < length; i++) { | ||
char = this[i]; | ||
code = char.charCodeAt(); | ||
for (i = 0; i < str_path.length; i++) { | ||
str_path[i] = str_path[i].replace(InflectionJS.uppercase, '_$1'); | ||
str_path[i] = str_path[i].replace(InflectionJS.underbar_prefix, ''); | ||
} | ||
// 95 = underscore | ||
if (code == 95) { | ||
if (!previous_underscore) { | ||
result += char; | ||
previous_underscore = true; | ||
} | ||
str = str_path.join('/').toLowerCase(); | ||
continue; | ||
} | ||
// Replace strings with underscores | ||
str = str.replace(/ /g, '_'); | ||
/* | ||
A whitespace (\s) in a regular expression matches any one of the | ||
characters: space, formfeed (\f), newline (\n), return (\r), | ||
tab (\t), vertical tab (\v), non-breaking space (\xA0), | ||
as well as the Unicode characters \u00A0 \u2028 \u2029. | ||
return str; | ||
\t = 9 | ||
\n = 10 | ||
\v = 11 | ||
\f = 12 | ||
\r = 13 | ||
= 32 | ||
\xA0 = 160 | ||
\u2028 = 8232 | ||
\u2029 = 8233 | ||
// We also add dashed | ||
- = 45 | ||
*/ | ||
if ((code >= 9 && code <= 13) || code == 32 || code == 45 || code == 16 || code == 8232 || code == 8233) { | ||
if (!previous_underscore) { | ||
result += '_'; | ||
previous_underscore = true; | ||
} | ||
continue; | ||
} | ||
// Uppercase? | ||
if (code >= 65 && code <= 90) { | ||
if (previous_underscore) { | ||
// Do nothing | ||
} else { | ||
result += '_'; | ||
} | ||
result += char.toLowerCase(); | ||
} else { | ||
result += char; | ||
} | ||
previous_underscore = false; | ||
} | ||
return result; | ||
}); | ||
@@ -476,0 +529,0 @@ |
@@ -22,5 +22,11 @@ module.exports = function BlastInformer(Blast, Collection) { | ||
Informer.setProperty('_simpleListeners', null); | ||
Informer.setProperty('_filterListeners', null); | ||
Informer.setProperty('_listenTypes', null); | ||
Informer.setProperty('_simpleSeen', null); | ||
Informer.setProperty('_filterSeen', null); | ||
// Functions that only listen to the type (string) | ||
Informer.setProperty(function simpleListeners() { | ||
return this._simpleListeners || (this._simpleListeners = {}); | ||
return this._simpleListeners || (this._simpleListeners = new Map()); | ||
}); | ||
@@ -30,3 +36,3 @@ | ||
Informer.setProperty(function filterListeners() { | ||
return this._filterListeners || (this._filterListeners = {}); | ||
return this._filterListeners || (this._filterListeners = new Map()); | ||
}); | ||
@@ -41,3 +47,3 @@ | ||
Informer.setProperty(function simpleSeen() { | ||
return this._simpleSeen || (this._simpleSeen = {}); | ||
return this._simpleSeen || (this._simpleSeen = new Map()); | ||
}); | ||
@@ -276,3 +282,4 @@ | ||
target, | ||
entry; | ||
entry, | ||
arr; | ||
@@ -303,13 +310,15 @@ // Throw an error if no valid listener function is given | ||
// Emit the 'newListener' event | ||
if (this.simpleListeners.newListener || this.filterListeners.newListener) { | ||
if (this.simpleListeners.has('newListener') || this.filterListeners.has('newListener')) { | ||
this.emit('newListener', type, listener.listener ? listener.listener : listener, typeName); | ||
} | ||
if (!target[typeName]) { | ||
target[typeName] = []; | ||
if (!target.has(typeName)) { | ||
target.set(typeName, []); | ||
this.listenTypes.push(typeName); | ||
} | ||
target[typeName].push(entry); | ||
arr = target.get(typeName); | ||
arr.push(entry); | ||
return this; | ||
@@ -528,3 +537,3 @@ } | ||
listeners = this.simpleListeners[type]; | ||
listeners = this.simpleListeners.get(type); | ||
@@ -536,3 +545,3 @@ if (listeners && listeners.length) { | ||
if (this.simpleListeners.removeListener) { | ||
if (this.simpleListeners.has('removeListener')) { | ||
// Emit an event, with a null value as last argument, | ||
@@ -556,3 +565,3 @@ // so the removed function won't be seen as a callback | ||
listeners = this.filterListeners[typeName]; | ||
listeners = this.filterListeners.get(typeName); | ||
@@ -585,3 +594,3 @@ if (listeners && listeners.length) { | ||
if (temp && this.simpleListeners.removeListener) { | ||
if (temp && this.simpleListeners.has('removeListener')) { | ||
this.emit('removeListener', type, temp[0], null); | ||
@@ -620,3 +629,3 @@ } | ||
listeners = this.simpleListeners[type]; | ||
listeners = this.simpleListeners.get(type); | ||
@@ -638,3 +647,3 @@ if (listeners && listeners.length) { | ||
// Get all the filter listeners that listen to this type name | ||
listeners = this.filterListeners[type]; | ||
listeners = this.filterListeners.get(type); | ||
@@ -662,3 +671,3 @@ if (listeners && listeners.length) { | ||
listeners = this.filterListeners[typeName]; | ||
listeners = this.filterListeners.get(typeName); | ||
@@ -724,3 +733,3 @@ if (listeners && listeners.length) { | ||
if (typeof type === 'string') { | ||
if (this.simpleSeen[type]) { | ||
if (this.simpleSeen.has(type)) { | ||
return true; | ||
@@ -791,3 +800,3 @@ } | ||
if (typeof type == 'string') { | ||
this.simpleSeen[type] = false; | ||
this.simpleSeen.delete(type); | ||
@@ -817,3 +826,3 @@ // Create a filter object for the filterSeen removal | ||
if (count == 1) { | ||
this.simpleSeen[type] = false; | ||
this.simpleSeen.delete(type); | ||
} | ||
@@ -890,3 +899,3 @@ } | ||
if (mark_as_seen) { | ||
this.simpleSeen[typeName] = true; | ||
this.simpleSeen.set(typeName, true); | ||
} | ||
@@ -920,3 +929,3 @@ | ||
for (i = 0; i < types.length; i++) { | ||
listeners = this.filterListeners[types[i]]; | ||
listeners = this.filterListeners.get(types[i]); | ||
@@ -964,3 +973,3 @@ // Check class specific filter listeners | ||
if (typeName) { | ||
listeners = this.simpleListeners[typeName]; | ||
listeners = this.simpleListeners.get(typeName); | ||
@@ -967,0 +976,0 @@ // Check class specific simple listeners |
469
lib/init.js
@@ -25,2 +25,3 @@ module.exports = function BlastInitLoader(modifyPrototype) { | ||
ua, | ||
fs, | ||
i; | ||
@@ -46,3 +47,7 @@ | ||
var platform, | ||
if (!ua) { | ||
return null; | ||
} | ||
let platform, | ||
webview = false, | ||
@@ -81,2 +86,10 @@ version, | ||
engine = 'edgehtml'; | ||
} else if (~(index = ua.indexOf('edg/'))) { | ||
name = 'edge'; | ||
engine = 'blink'; | ||
} else if (~(index = ua.indexOf('samsungbrowser/'))) { | ||
name = 'samsung browser'; | ||
engine = 'blink'; | ||
} else if (~(index = ua.indexOf('chromium/'))) { | ||
name = 'chromium'; | ||
} else if (~(index = ua.indexOf('chrome/'))) { | ||
@@ -110,2 +123,3 @@ name = 'chrome'; | ||
case 'chromium': | ||
case 'chrome': | ||
@@ -164,2 +178,5 @@ if (major < 28) { | ||
// Is it running in an Electron window? | ||
Blast.isElectron = false; | ||
// Is it running in a webview? | ||
@@ -178,2 +195,5 @@ Blast.isWebview = false; | ||
// Define custom argument configurations | ||
Blast.arguments = {}; | ||
// See if we can modify class prototypes | ||
@@ -184,3 +204,7 @@ if (typeof modifyPrototype === 'undefined') { | ||
if (typeof process === 'object' && (process.__node_webkit || process.__nwjs)) { | ||
if (typeof process === 'object' && typeof process.electronBinding == 'function') { | ||
Blast.isElectron = true; | ||
Blast.isNode = true; | ||
Globals = global; | ||
} else if (typeof process === 'object' && (process.__node_webkit || process.__nwjs)) { | ||
Blast.isNW = true; | ||
@@ -265,4 +289,7 @@ Blast.isNode = true; | ||
fs = require('fs'); | ||
Blast.version = version; | ||
Blast.version_string = r_package.version; | ||
Blast.ACTIVE_FILE = Symbol('active_file'); | ||
} | ||
@@ -353,3 +380,3 @@ // PROTOBLAST END CUT | ||
// Extra files to load go here | ||
extras = []; | ||
Blast.extra_files = extras = []; | ||
@@ -361,3 +388,2 @@ // Already required files | ||
'Function', | ||
'Symbol', | ||
'Object', | ||
@@ -384,2 +410,3 @@ 'Array', | ||
'String', | ||
'StringBuilder', | ||
'RURL', | ||
@@ -562,3 +589,3 @@ 'Cache' | ||
* @since 0.1.3 | ||
* @version 0.1.4 | ||
* @version 0.7.0 | ||
* | ||
@@ -571,4 +598,8 @@ * @return {String} | ||
if (this._name) { | ||
return this._name; | ||
} | ||
// Turn the function into a string and extract the name using a regex | ||
fncName = this.toString().match(/^\s*function\s*(\S*)\s*\(/); | ||
fncName = this.toString().match(/^\s*function\s*(\S*?)\s*\(/); | ||
@@ -583,3 +614,3 @@ // If no name is found, use an empty string | ||
// Store the name property on the function itself | ||
this.name = fncName; | ||
this._name = fncName; | ||
@@ -780,3 +811,3 @@ // Return the name | ||
* @since 0.1.0 | ||
* @version 0.6.2 | ||
* @version 0.7.0 | ||
* | ||
@@ -800,2 +831,6 @@ * @param {Object} target The object to add the property to | ||
if (!name) { | ||
throw new Error('Unable to define static property, could not find name value'); | ||
} | ||
if (typeof targetClass == 'string') { | ||
@@ -853,2 +888,3 @@ | ||
} else { | ||
Blast.defineValue(objTarget, name, value, true); | ||
@@ -914,2 +950,6 @@ | ||
//PROTOBLAST START CUT | ||
let os = require('os'), | ||
tmpdir = fs.mkdtempSync(libpath.resolve(os.tmpdir(), 'protoblast')), | ||
cache = {}; | ||
/** | ||
@@ -920,50 +960,90 @@ * Server side: create client side file | ||
* @since 0.1.1 | ||
* @version 0.5.1 | ||
* @version 0.7.0 | ||
* | ||
* @return {String} | ||
* @param {Object} options | ||
* | ||
* @return {Pledge} | ||
*/ | ||
Blast.getClientPath = function getClientPath(useCommon) { | ||
Blast.getClientPath = function getClientPath(options) { | ||
var client_extras = [], | ||
template, | ||
result, | ||
cpath, | ||
files, | ||
code, | ||
temp, | ||
id, | ||
fs; | ||
var refresh = false, | ||
ua, | ||
id; | ||
if (useCommon) { | ||
if (Blast.clientPathCommon) { | ||
return Blast.clientPathCommon; | ||
if (!options) { | ||
options = {}; | ||
} else { | ||
if (options.ua) { | ||
ua = Blast.parseUseragent(options.ua); | ||
id = ua.family + '-' + ua.major + '.' + ua.minor; | ||
} | ||
cpath = libpath.resolve(__dirname, '..', 'client-file-common.js'); | ||
Blast.clientPathCommon = cpath; | ||
} else { | ||
if (Blast.clientPath) { | ||
return Blast.clientPath; | ||
if (options.refresh) { | ||
refresh = true; | ||
} | ||
} | ||
cpath = libpath.resolve(__dirname, '..', 'client-file.js'); | ||
Blast.clientPath = cpath; | ||
if (!id) { | ||
id = 'full'; | ||
} | ||
// Require fs | ||
fs = require('fs'); | ||
if (options.use_common) { | ||
id = 'common_' + id; | ||
} else if (options.modify_prototypes) { | ||
id = 'global_' + id; | ||
} | ||
// Get the main template | ||
template = fs.readFileSync(libpath.resolve(__dirname, 'client.js'), {encoding: 'utf8'}); | ||
if (cache[id] && !refresh) { | ||
return cache[id]; | ||
} | ||
code = ''; | ||
let extra_files = [], | ||
compose_id = '', | ||
extra, | ||
i; | ||
files = [ | ||
// Now iterate over the extras | ||
for (i = 0; i < extras.length; i++) { | ||
extra = extras[i]; | ||
if (!extra.client) { | ||
continue; | ||
} | ||
// See if we've been given a useragent | ||
if (ua && extra.versions && id != 'full' && id != 'full_common') { | ||
let entry = extra.versions[ua.family]; | ||
// If the user's browser version is higher than the required max, | ||
// it is also not needed | ||
if (entry && ua.version.float > entry.max) { | ||
continue; | ||
} | ||
} | ||
extra_files.push(extra); | ||
compose_id += i + '-'; | ||
} | ||
compose_id = Blast.Bound.Object.checksum(compose_id); | ||
if (cache[compose_id] && !refresh) { | ||
cache[id] = cache[compose_id]; | ||
return cache[id]; | ||
} | ||
let files = [ | ||
'init', | ||
'json-dry' | ||
].concat(Names); | ||
'json-dry', | ||
'browsershims', | ||
'request_browser' | ||
]; | ||
// This file should only be for browsers | ||
files.push('browsershims', 'request_browser'); | ||
let code = '', | ||
tasks = []; | ||
// The first file should be the template | ||
tasks.push(Blast.getCachedFile('client.js')); | ||
// Queue some basic, pre-wrapped files | ||
files.forEach(function eachFile(name, index) { | ||
@@ -981,54 +1061,146 @@ | ||
temp = fs.readFileSync(path, {encoding: 'utf8'}); | ||
tasks.push(function getFile(next) { | ||
Blast.getCachedFile(path).then(function gotCode(code) { | ||
var data = 'require.register("' + name + '.js", function(module, exports, require){\n'; | ||
data += code; | ||
data += '});\n'; | ||
next(null, data); | ||
}).catch(next); | ||
}); | ||
}); | ||
// The "magic.js" file still needs to be wrapped | ||
if (name == 'magic') { | ||
temp = 'module.exports = function(Blast, Collection, Bound, Obj) {"use strict";' + temp + '\n};'; | ||
} | ||
extra_files.forEach(function eachExtraFile(options) { | ||
tasks.push(function getExtraFile(next) { | ||
Blast.getCachedFile(options.path).then(function gotCode(code) { | ||
code += 'require.register("' + name + '.js", function(module, exports, require){\n'; | ||
code += temp; | ||
code += '});\n'; | ||
if (options.add_wrapper !== false) { | ||
if (options.add_wrapper || code.slice(0, 14) != 'module.exports') { | ||
let data = 'module.exports = function('; | ||
if (options.arguments) { | ||
data += Blast.getArgumentConfiguration(options.arguments).names.join(','); | ||
} else { | ||
data += 'Blast, Collection, Bound, Obj'; | ||
} | ||
data += ') {'; | ||
code = data + code + '\n};'; | ||
} | ||
} | ||
code = 'require.register("' + (options.name_id || options.name) + '", function(module, exports, require){\n' | ||
+ code | ||
+ '});\n'; | ||
next(null, code); | ||
}).catch(next); | ||
}); | ||
}); | ||
// Now iterate over the extras | ||
extras.forEach(function eachExtra(options) { | ||
cache[id] = new Blast.Classes.Pledge(); | ||
cache[compose_id] = cache[id]; | ||
var temp = fs.readFileSync(options.path, {encoding: 'utf8'}); | ||
Blast.Bound.Function.parallel(tasks, function gotFiles(err, files) { | ||
if (options.added_wrapper) { | ||
temp = 'module.exports = function(Blast, Collection, Bound, Obj) {"use strict";' + temp + '\n};'; | ||
if (err) { | ||
return cache[id].reject(err); | ||
} | ||
code += 'require.register("' + options.name + '", function(module, exports, require){\n'; | ||
code += temp; | ||
code += '});\n'; | ||
}); | ||
let template = files.shift(), | ||
index = template.indexOf('//_REGISTER_//'), | ||
filename = libpath.resolve(tmpdir, compose_id + '.js'), | ||
code = files.join('\n'); | ||
id = template.indexOf('//_REGISTER_//'); | ||
if (options.use_common) { | ||
code += '\nuse_common = true;\n'; | ||
} else if (options.modify_prototypes) { | ||
code += '\nmodify_prototypes = true;\n'; | ||
} | ||
if (useCommon) { | ||
code += '\nuseCommon = true;\n'; | ||
} | ||
let client_extras = []; | ||
// Add the extras to the client | ||
extras.forEach(function eachExtra(options) { | ||
extra_files.forEach(function eachExtraFile(options) { | ||
if (options.client === false || options.is_extra === false) { | ||
return; | ||
} | ||
if (options.client === false) { | ||
return; | ||
client_extras.push([options.name_id, options.arguments]); | ||
}); | ||
code += '\nclient_extras = ' + JSON.stringify(client_extras) + ';\n'; | ||
template = template.slice(0, index) + code + template.slice(index); | ||
// Remove everything between "PROTOBLAST START CUT" and "PROTOBLAST END CUT" (with slashes) | ||
template = template.replace(/\/\/\s?PROTOBLAST\s?START\s?CUT([\s\S]*?)(\/\/\s?PROTOBLAST\s?END\s?CUT)/gm, ''); | ||
let retries = 0; | ||
function retryWithTempdir(filename, template) { | ||
retries++; | ||
fs.mkdtemp(libpath.resolve(os.tmpdir(), 'protoblast'), function madeDir(err, result) { | ||
if (err) { | ||
return cache[id].reject(err); | ||
} | ||
tmpdir = result; | ||
filename = libpath.resolve(tmpdir, compose_id + '.js') | ||
writeFile(filename, template); | ||
}); | ||
} | ||
client_extras.push(options.name); | ||
function writeFile(filename, template) { | ||
fs.writeFile(filename, template, function written(err) { | ||
if (err) { | ||
if (retries == 0) { | ||
return retryWithTempdir(filename, template); | ||
} | ||
return cache[id].reject(err); | ||
} | ||
cache[id].resolve(filename); | ||
}); | ||
} | ||
writeFile(filename, template); | ||
}); | ||
code += '\nclient_extras = ' + JSON.stringify(client_extras) + ';\n'; | ||
return cache[id]; | ||
}; | ||
template = template.slice(0, id) + code + template.slice(id); | ||
/** | ||
* Get a file and cache it | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @param {String} path | ||
* | ||
* @return {Promise} | ||
*/ | ||
Blast.getCachedFile = function getCachedFile(path) { | ||
// Remove everything between "PROTOBLAST START CUT" and "PROTOBLAST END CUT" (with slashes) | ||
template = template.replace(/\/\/\s?PROTOBLAST\s?START\s?CUT([\s\S]*?)(\/\/\s?PROTOBLAST\s?END\s?CUT)/gm, ''); | ||
if (path[0] != '/') { | ||
path = libpath.resolve(__dirname, path); | ||
} | ||
fs.writeFileSync(cpath, template); | ||
return new Promise(function doReadFile(resolve, reject) { | ||
fs.readFile(path, 'utf8', function gotResult(err, data) { | ||
return cpath; | ||
if (err) { | ||
return reject(err); | ||
} | ||
resolve(data); | ||
}); | ||
}); | ||
}; | ||
@@ -1222,2 +1394,26 @@ //PROTOBLAST END CUT | ||
/** | ||
* Get an argument configuration by name | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
*/ | ||
Blast.getArgumentConfiguration = function getArgumentConfiguration(name) { | ||
var config; | ||
if (typeof name == 'string') { | ||
config = Blast.arguments[name]; | ||
} else { | ||
config = name; | ||
} | ||
if (!config) { | ||
throw new Error('Unable to find custom argument configuration "' + name + '"'); | ||
} | ||
return config; | ||
}; | ||
//PROTOBLAST START CUT | ||
@@ -1263,3 +1459,3 @@ /** | ||
if (options.arguments) { | ||
head += options.arguments.names.join(','); | ||
head += Blast.getArgumentConfiguration(options.arguments).names.join(','); | ||
} else { | ||
@@ -1317,3 +1513,3 @@ head += 'Blast, Collection, Bound, Obj'; | ||
* @since 0.4.1 | ||
* @version 0.6.6 | ||
* @version 0.7.0 | ||
* | ||
@@ -1327,3 +1523,5 @@ * @param {String} name | ||
exported_fnc, | ||
from_core, | ||
result, | ||
index, | ||
args; | ||
@@ -1345,5 +1543,3 @@ | ||
if (options.is_extra !== false) { | ||
extras.push(options); | ||
} | ||
index = extras.push(options) - 1; | ||
@@ -1357,2 +1553,4 @@ if (options.pwd) { | ||
options.name_id = name.join('/'); | ||
name.unshift(options.pwd); | ||
@@ -1362,3 +1560,12 @@ } | ||
if (Array.isArray(name)) { | ||
name = libpath.resolve.apply(libpath, name); | ||
if (!options.name_id) { | ||
options.name_id = name.join('/'); | ||
} | ||
if (Blast.isBrowser) { | ||
name = options.name_id; | ||
} else { | ||
name = libpath.resolve.apply(libpath, name); | ||
} | ||
} | ||
@@ -1375,2 +1582,3 @@ | ||
if (name == libpath.basename(name)) { | ||
from_core = true; | ||
options.name = name; | ||
@@ -1382,4 +1590,24 @@ options.path = libpath.resolve(__dirname, name + '.js'); | ||
} | ||
if (options.path.slice(-3) != '.js') { | ||
options.path += '.js'; | ||
} | ||
} | ||
if (options.client && !from_core) { | ||
if (options.is_extra === false) { | ||
if (options.extra_name) { | ||
options.name_id = options.extra_name + '/' + (options.name_id || options.name); | ||
} | ||
} else { | ||
if (!options.name_id) { | ||
options.name_id = index + '_' + options.name; | ||
} else { | ||
options.name_id = index + '_' + options.name_id; | ||
} | ||
} | ||
} else if (!options.name_id) { | ||
options.name_id = options.name; | ||
} | ||
if (Blast.isNode && options.server === false) { | ||
@@ -1393,2 +1621,16 @@ return; | ||
if (Blast.isBrowser) { | ||
if (options.is_extra === false && !options.path) { | ||
options.path = options.name_id; | ||
if (options.extra_name) { | ||
options.path = options.extra_name + '/' + options.path; | ||
} | ||
} | ||
} | ||
if (Blast.isNode) { | ||
Blast[Blast.ACTIVE_FILE] = options; | ||
} | ||
// Get the exported function | ||
@@ -1398,3 +1640,3 @@ exported_fnc = require(options.path || name); | ||
if (options.arguments) { | ||
args = options.arguments.values; | ||
args = Blast.getArgumentConfiguration(options.arguments).values; | ||
} else { | ||
@@ -1404,5 +1646,16 @@ args = [Blast, Collection, Blast.Bound, Blast.Bound.Object]; | ||
if (typeof exported_fnc != 'function') { | ||
if (Blast.isNode) { | ||
Blast[Blast.ACTIVE_FILE] = false; | ||
} | ||
throw new Error('Module "' + (options.path || name) + '" did not export a function'); | ||
} | ||
// Execute the exported function | ||
result = exported_fnc.apply(null, args); | ||
if (Blast.isNode) { | ||
Blast[Blast.ACTIVE_FILE] = false; | ||
} | ||
if (result != null) { | ||
@@ -1426,10 +1679,30 @@ return result; | ||
Blast.require('symbol', { | ||
server : false, | ||
versions : { | ||
chrome : {max: 37}, | ||
edge : {max: 0}, | ||
firefox : {max: 35}, | ||
opera : {max: 24}, | ||
safari : {max: 8} | ||
} | ||
}); | ||
// Make sure WeakMap is available first! | ||
Blast.require('weakmap'); | ||
Blast.require('weakmap', { | ||
server : false, | ||
versions : { | ||
chrome : {max: 35}, | ||
edge : {max: 0}, | ||
firefox : {max: 5}, | ||
opera : {max: 22}, | ||
safari : {max: 7} | ||
} | ||
}); | ||
// Load the inheritance methods | ||
Blast.require('function_inheritance'); | ||
Blast.require('function_inheritance', {is_extra: false}); | ||
// Load the predefined decorators | ||
Blast.require('function_decorators'); | ||
Blast.require('function_decorators', {is_extra: false}); | ||
@@ -1459,18 +1732,17 @@ // Require the scripts | ||
Blast.require('string_entities', {add_wrapper: false}); | ||
Blast.require('function_flow', {add_wrapper: false}); | ||
Blast.require('setimmediate', {add_wrapper: false}); | ||
Blast.require('inflections', {add_wrapper: false}); | ||
Blast.require('date_format', {add_wrapper: false}); | ||
Blast.require('diacritics', {add_wrapper: false}); | ||
Blast.require('benchmark', {add_wrapper: false}); | ||
Blast.require('sorting', {add_wrapper: false}); | ||
Blast.require('string_entities', {add_wrapper: false, is_extra: false}); | ||
Blast.require('function_flow', {add_wrapper: false, is_extra: false}); | ||
Blast.require('setimmediate', {add_wrapper: false, is_extra: false}); | ||
Blast.require('inflections', {add_wrapper: false, is_extra: false}); | ||
Blast.require('date_format', {add_wrapper: false, is_extra: false}); | ||
Blast.require('diacritics', {add_wrapper: false, is_extra: false}); | ||
Blast.require('benchmark', {add_wrapper: false, is_extra: false}); | ||
Blast.require('sorting', {add_wrapper: false, is_extra: false}); | ||
Blast._fn_token_prepare(); | ||
Blast.emit('pre-extra-files'); | ||
if (Blast.isBrowser) { | ||
Blast.require('browsershims'); | ||
Blast.require('request_browser'); | ||
client_extras.forEach(function eachExtra(name) { | ||
Blast.require(name); | ||
}); | ||
} | ||
@@ -1530,2 +1802,8 @@ | ||
if (Blast.isBrowser) { | ||
client_extras.forEach(function eachExtra(entry) { | ||
Blast.require(entry[0], {arguments: entry[1]}); | ||
}); | ||
} | ||
for (var i = 0; i < when_ready.length; i++) { | ||
@@ -1552,3 +1830,6 @@ when_ready[i](); | ||
// All files have been required | ||
Blast.emit('requiring'); | ||
return Blast; | ||
}; |
@@ -10,3 +10,3 @@ module.exports = function BlastPath(Blast, Collection) { | ||
vm = { | ||
runInNewContext: function(expr, context) { with (context) return eval(expr); } | ||
runInNewContext: (Blast.Globals.eval)('temp = function(expr, context) { with (context) return eval(expr); }') | ||
}; | ||
@@ -243,3 +243,3 @@ } | ||
*/ | ||
Blast.defineValue(JSONPath.prototype, function eval(code, _v, _vname, path) { | ||
Blast.defineValue(JSONPath.prototype, 'eval', function evaluate(code, _v, _vname, path) { | ||
if (!$ || !_v) return false; | ||
@@ -246,0 +246,0 @@ if (code.indexOf("@path") > -1) { |
@@ -15,3 +15,3 @@ /** | ||
* @since 0.6.6 | ||
* @version 0.6.6 | ||
* @version 0.7.0 | ||
* | ||
@@ -29,2 +29,6 @@ * @param {Function} fnc | ||
Blast.defineGet(fnc, 'super', function getSuper() { | ||
return result.wrapper.super; | ||
}); | ||
return result; | ||
@@ -79,5 +83,2 @@ }); | ||
}, | ||
enumerate: function enumerate(context) { | ||
return context.__enumerate(); | ||
}, | ||
ownKeys: function ownKeys(context) { | ||
@@ -84,0 +85,0 @@ return context.__ownKeys(); |
@@ -470,2 +470,40 @@ module.exports = function BlastMath(Blast, Collection, Bound) { | ||
/** | ||
* In Quad easing | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @param {Number} t current time | ||
* @param {Number} b start value | ||
* @param {Number} c change in value | ||
* @param {Number} d duration | ||
* | ||
* @return {Number} | ||
*/ | ||
Blast.defineStatic('Math', function easeInQuad(t, b, c, d) { | ||
t /= d; | ||
return c*t*t + b; | ||
}); | ||
/** | ||
* Out Quad easing | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @param {Number} t current time | ||
* @param {Number} b start value | ||
* @param {Number} c change in value | ||
* @param {Number} d duration | ||
* | ||
* @return {Number} | ||
*/ | ||
Blast.defineStatic('Math', function easeOutQuad(t, b, c, d) { | ||
t /= d; | ||
return -c * t*(t-2) + b; | ||
}); | ||
/** | ||
* In/Out Quad easing | ||
@@ -492,2 +530,63 @@ * | ||
/** | ||
* In Quart easing | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @param {Number} t current time | ||
* @param {Number} b start value | ||
* @param {Number} c change in value | ||
* @param {Number} d duration | ||
* | ||
* @return {Number} | ||
*/ | ||
Blast.defineStatic('Math', function easeInQuart(t, b, c, d) { | ||
t /= d; | ||
return c*t*t*t*t + b; | ||
}); | ||
/** | ||
* Out Quart easing | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @param {Number} t current time | ||
* @param {Number} b start value | ||
* @param {Number} c change in value | ||
* @param {Number} d duration | ||
* | ||
* @return {Number} | ||
*/ | ||
Blast.defineStatic('Math', function easeOutQuart(t, b, c, d) { | ||
t /= d; | ||
t--; | ||
return -c * (t*t*t*t - 1) + b; | ||
}); | ||
/** | ||
* In/Out Quart easing: | ||
* Accelerate to the halfway point, then decelerate | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @param {Number} t current time | ||
* @param {Number} b start value | ||
* @param {Number} c change in value | ||
* @param {Number} d duration | ||
* | ||
* @return {Number} | ||
*/ | ||
Blast.defineStatic('Math', function easeInOutQuart(t, b, c, d) { | ||
t /= d/2; | ||
if (t < 1) return c/2*t*t*t*t + b; | ||
t -= 2; | ||
return -c/2 * (t*t*t*t - 2) + b; | ||
}); | ||
/** | ||
* See if the 2 given ranges overlap | ||
@@ -494,0 +593,0 @@ * |
@@ -1194,3 +1194,3 @@ module.exports = function BlastObject(Blast, Collection, Bound, Obj) { | ||
* @since 0.0.1 | ||
* @version 0.0.1 | ||
* @version 0.7.0 | ||
* | ||
@@ -1205,4 +1205,10 @@ * @param {Object} obj | ||
for (key in obj) { | ||
if (Symbol.polyfilled && Symbol.isSymbol(key)) { | ||
continue; | ||
} | ||
fnc(obj[key], key, obj); | ||
} | ||
}); | ||
@@ -1215,3 +1221,3 @@ | ||
* @since 0.1.6 | ||
* @version 0.4.2 | ||
* @version 0.7.0 | ||
* | ||
@@ -1250,6 +1256,10 @@ * @param {Object} obj The object to walk over | ||
if (!obj.hasOwnProperty(key)) { | ||
if (!Object.hasOwnProperty.call(obj, key)) { | ||
continue; | ||
} | ||
if (Symbol.polyfilled && Symbol.isSymbol(key)) { | ||
continue; | ||
} | ||
// Fire the function | ||
@@ -1467,2 +1477,8 @@ if (fnc != null) { | ||
// Set it on some objects | ||
Blast[Blast.checksumSymbol] = 'Protoblast'; | ||
Blast.Bound[Blast.checksumSymbol] = 'Blast.Bound'; | ||
Blast.Classes[Blast.checksumSymbol] = 'Blast.Classes'; | ||
Blast.Collection[Blast.checksumSymbol] = 'Blast.Collection'; | ||
/** | ||
@@ -1473,13 +1489,43 @@ * Calculate the checksum for the given value | ||
* @since 0.1.3 | ||
* @version 0.6.5 | ||
* @version 0.7.0 | ||
* | ||
* @param {Object|Array} obj The object to checksum | ||
* @param {Boolean} sort_arrays Sort arrays before checksumming? (true) | ||
* @param {Array} seen | ||
* | ||
* @return {String} | ||
*/ | ||
Blast.defineStatic('Object', function checksum(obj, sort_arrays, seen) { | ||
Blast.defineStatic('Object', function checksum(obj, sort_arrays) { | ||
var result, | ||
seen = []; | ||
if (typeof sort_arrays != 'boolean') { | ||
sort_arrays = true; | ||
} | ||
if (typeof obj == 'object' && obj) { | ||
seen.push(obj); | ||
} | ||
result = _checksum(obj, sort_arrays, seen, 0); | ||
return result; | ||
}); | ||
/** | ||
* The actual function that does the checksuming | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.1.3 | ||
* @version 0.7.0 | ||
* | ||
* @param {Object|Array} obj The object to checksum | ||
* @param {Boolean} sort_arrays Sort arrays before checksumming? (true) | ||
* | ||
* @return {String} | ||
*/ | ||
function _checksum(obj, sort_arrays, seen, level) { | ||
var split_length, | ||
custom_type, | ||
counter, | ||
@@ -1499,12 +1545,10 @@ result, | ||
if (typeof sort_arrays != 'boolean') { | ||
seen = sort_arrays; | ||
sort_arrays = true; | ||
} | ||
// Make sure primitives are primitive | ||
if (type == 'object' && obj != null) { | ||
custom_type = typeof obj[Blast.checksumSymbol]; | ||
if (typeof obj[Blast.checksumSymbol] == 'function') { | ||
return checksum(obj[Blast.checksumSymbol](), sort_arrays, seen); | ||
if (custom_type == 'string') { | ||
return obj[Blast.checksumSymbol]; | ||
} else if (custom_type == 'function') { | ||
return _checksum(obj[Blast.checksumSymbol](), sort_arrays, seen, level + 1); | ||
} else if (typeof obj.valueOf == 'function') { | ||
@@ -1561,3 +1605,3 @@ // Get the value of the object | ||
} else if (obj.constructor && obj.constructor.name == 'RegExp') { | ||
return 'R' + checksum(String(obj)); | ||
return 'R' + _checksum(String(obj)); | ||
} | ||
@@ -1571,3 +1615,3 @@ | ||
if (sort_arrays) { | ||
obj.sort(); | ||
Bound.Array.safesort(obj); | ||
} | ||
@@ -1612,11 +1656,6 @@ | ||
// Make sure seen exists | ||
if (!seen) { | ||
seen = [obj]; | ||
} | ||
// Handle objects recursively, but beware of circular references | ||
if ((idx = seen.indexOf(val)) == -1) { | ||
seen.push(val); | ||
val = checksum(val, sort_arrays, seen); | ||
val = _checksum(val, sort_arrays, seen, level + 1); | ||
} else { | ||
@@ -1641,6 +1680,6 @@ val = 'R' + idx; | ||
// Now create another checksum | ||
result += checksum(temp); | ||
result += _checksum(temp); | ||
return result; | ||
}); | ||
}; | ||
@@ -1647,0 +1686,0 @@ /** |
@@ -48,2 +48,24 @@ module.exports = function BlastPledge(Blast, Collection) { | ||
/** | ||
* When the pledge was created | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @type {Number} | ||
*/ | ||
Pledge.setProperty('_created', 0); | ||
/** | ||
* Do not report progress by default, it's very expensive | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @type {Boolean} | ||
*/ | ||
Pledge.setProperty('report_progress', false); | ||
/** | ||
* The initial progress is 0 | ||
@@ -113,2 +135,31 @@ * | ||
/** | ||
* The eventual resolved value | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
*/ | ||
Pledge.setProperty('_resolved_value', null); | ||
/** | ||
* When this pledge has ended | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
*/ | ||
Pledge.setProperty('_ended', null); | ||
/** | ||
* Property that could be a function to cancel the pledge | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @type {Function} | ||
*/ | ||
Pledge.setProperty('cancel', null); | ||
/** | ||
* Calculate the duration | ||
@@ -177,2 +228,76 @@ * | ||
/** | ||
* Determine if an object is a thennable | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @return {Boolean} | ||
*/ | ||
Pledge.setStatic(function isThenable(obj) { | ||
return !!obj && typeof obj.then == 'function'; | ||
}); | ||
/** | ||
* Determine if an object has the promise interface | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @return {Boolean} | ||
*/ | ||
Pledge.setStatic(function hasPromiseInterface(obj) { | ||
return Pledge.isThenable(obj) && typeof obj.catch == 'function'; | ||
}); | ||
/** | ||
* Handle an old-style callback for a pledge or promise | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
*/ | ||
Pledge.setStatic(function done(promise, callback) { | ||
var result = Pledge.prototype.done.call(promise, callback); | ||
if (!result) { | ||
result = Pledge.resolve(); | ||
} | ||
return result; | ||
}); | ||
/** | ||
* Cast the given value to a pledge | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @param {Mixed} value | ||
* | ||
* @return {Pledge} | ||
*/ | ||
Pledge.setStatic(function cast(value) { | ||
var is_primitive = !value || Blast.Bound.Object.isPrimitive(value); | ||
if (!is_primitive) { | ||
if (Pledge.isPledge(value)) { | ||
return value; | ||
} | ||
if (Pledge.hasPromiseInterface(value)) { | ||
let pledge = new Pledge(); | ||
Pledge.done(value, pledge); | ||
return pledge; | ||
} | ||
} | ||
return Pledge.resolve(value); | ||
}); | ||
/** | ||
* Create a new pledge and resolve it with the given value | ||
@@ -329,3 +454,3 @@ * | ||
* @since 0.4.0 | ||
* @version 0.4.0 | ||
* @version 0.7.0 | ||
* | ||
@@ -336,2 +461,7 @@ * @param {Number} value A value between 0-100 | ||
Pledge.setMethod(function addProgress(value, label) { | ||
if (!this.report_progress) { | ||
return; | ||
} | ||
return this.reportProgress((this.progress || 0) + value, label); | ||
@@ -366,2 +496,6 @@ }); | ||
if (!this.report_progress) { | ||
return; | ||
} | ||
if (parts == null) { | ||
@@ -386,2 +520,6 @@ parts = 1; | ||
if (!this.report_progress) { | ||
return; | ||
} | ||
if (parts == null) { | ||
@@ -430,2 +568,6 @@ parts = 1; | ||
if (!this.report_progress) { | ||
return; | ||
} | ||
if (!Pledge.isPledge(pledge)) { | ||
@@ -509,3 +651,5 @@ return false; | ||
this.calculateProgressParts(); | ||
if (this.report_progress) { | ||
this.calculateProgressParts(); | ||
} | ||
}); | ||
@@ -710,3 +854,3 @@ | ||
* @since 0.5.0 | ||
* @version 0.6.5 | ||
* @version 0.7.0 | ||
* | ||
@@ -722,3 +866,15 @@ * @return {Pledge} | ||
if (typeof callback != 'function') { | ||
throw new Error('Unable to handle callback: not a function!'); | ||
if (Pledge.isPledge(callback)) { | ||
let pledge = callback; | ||
callback = function forwardToPledge(err, result) { | ||
if (err) { | ||
pledge.reject(err); | ||
} else { | ||
pledge.resolve(result); | ||
} | ||
} | ||
} else { | ||
throw new Error('Unable to handle callback: not a function or Pledge!'); | ||
} | ||
} | ||
@@ -725,0 +881,0 @@ |
@@ -19,3 +19,3 @@ module.exports = function BlastRegExp(Blast, Collection) { | ||
* @since 0.1.0 | ||
* @version 0.1.0 | ||
* @version 0.7.0 | ||
* | ||
@@ -27,3 +27,3 @@ * @param {String} str | ||
Blast.defineStatic('RegExp', function escape(str) { | ||
return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); | ||
return String(str).replace(/([.\\+*?\[\^\]$(){}=!<>|:\-\/])/g, '\\$1'); | ||
}); | ||
@@ -72,2 +72,26 @@ | ||
/** | ||
* Interpret a wildcard string | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @param {String} str | ||
* @param {String} flags | ||
* | ||
* @return {RegExp} | ||
*/ | ||
Blast.defineStatic('RegExp', function interpretWildcard(str, flags) { | ||
var pattern = Collection.RegExp.escape(str).replace(/\\\*/g, '.*').replace(/\\\?/g, '.'); | ||
if (!flags) { | ||
flags = 'g'; | ||
} else if (flags.indexOf('g') == -1) { | ||
flags += 'g'; | ||
} | ||
return Collection.RegExp.interpret(pattern, flags); | ||
}); | ||
/** | ||
* Combine regular expressions together. | ||
@@ -74,0 +98,0 @@ * |
module.exports = function BlastRequestBrowser(Blast, Collection) { | ||
"use strict"; | ||
var Request = Blast.Classes.Develry.Request, | ||
original_send = XMLHttpRequest.prototype.send, | ||
original_open = XMLHttpRequest.prototype.open; | ||
original_open = XMLHttpRequest.prototype.open, | ||
START_SYMBOL = Symbol('start'); | ||
@@ -12,3 +15,3 @@ /** | ||
* @since 0.6.2 | ||
* @version 0.6.2 | ||
* @version 0.7.0 | ||
* | ||
@@ -22,6 +25,11 @@ * @return {Pledge} | ||
method = this.method_info, | ||
temp_timeout, | ||
response, | ||
finished, | ||
is_form, | ||
timeout, | ||
result, | ||
error, | ||
body = this.body, | ||
bomb, | ||
type, | ||
@@ -36,3 +44,3 @@ key, | ||
if (this.cache === false) { | ||
this.url.param('_', this.request_start); | ||
this.url.param('_', this.time_started); | ||
} else { | ||
@@ -46,2 +54,7 @@ this.url.param('_', null); | ||
// Allow cancelling the request | ||
pledge.cancel = function cancel() { | ||
xhr.abort(); | ||
}; | ||
// DNS failures or no available connection will cause this error | ||
@@ -55,2 +68,4 @@ xhr.addEventListener('error', function onError(event) { | ||
Blast.state.reportError(error); | ||
done(); | ||
@@ -62,2 +77,6 @@ }, false); | ||
if (error) { | ||
return; | ||
} | ||
error = new Error('Transfer aborted'); | ||
@@ -69,3 +88,68 @@ error.status = error.number = 0; | ||
// Set the ajax handler | ||
if (this.timeout) { | ||
timeout = this.timeout; | ||
} else { | ||
timeout = Math.max(Blast.state.rtt_timeout + 500, this.max_timeout); | ||
} | ||
// Create a timeout checker | ||
bomb = Blast.Bound.Function.timebomb(timeout, function onTimeout() { | ||
if (error || finished) { | ||
return; | ||
} | ||
if (!Blast.state.connectionHasTimedOut(timeout)) { | ||
temp_timeout = Blast.state.msUntilTimeout(timeout); | ||
if (!temp_timeout || temp_timeout < 500) { | ||
temp_timeout = 500; | ||
} | ||
console.log('Timeout of', timeout, 'is not yet considered as a connection timeout, waiting another', temp_timeout, 'ms'); | ||
bomb = Blast.Bound.Function.timebomb(temp_timeout, onTimeout); | ||
return; | ||
} | ||
error = new Error('Transfer timeout after ' + timeout + 'ms for ' + that.url); | ||
error.status = error.number = 408; | ||
error.timeout = timeout; | ||
error.request_start = xhr[START_SYMBOL]; | ||
Blast.state.reportError(error); | ||
try { | ||
xhr.abort(); | ||
} catch (err) { | ||
// Ignore | ||
} | ||
done(); | ||
}); | ||
// Event fired when we start UPLOADING data | ||
// This is actually not that useful in retrospect | ||
//xhr.addEventListener('loadstart', function uploadingData(event) {}); | ||
// We are receiving progress update | ||
xhr.addEventListener('progress', function receivingProgress(event) { | ||
if (xhr.readyState < 2) { | ||
return; | ||
} | ||
Blast.state.reportSuccess('progress', event); | ||
}); | ||
// Listen for ready state changes | ||
xhr.addEventListener('readystatechange', function onStateChange(event) { | ||
if (xhr.readyState < 2 || xhr.readyState === 4) { | ||
return; | ||
} | ||
Blast.state.reportSuccess('readystatechange', event); | ||
}); | ||
// Listen for successful load event | ||
xhr.addEventListener('load', function transferComplete(event) { | ||
@@ -83,4 +167,8 @@ | ||
if (type && type.indexOf(';') > -1) { | ||
type = Blast.Bound.String.before(type, ';'); | ||
} | ||
// Intercept file downloads | ||
if (disposition && disposition.search('attachment') !== -1) { | ||
if (disposition && (that.download_if_inline || disposition.search('attachment') !== -1)) { | ||
@@ -96,3 +184,3 @@ // Just browse to it if filereader doesn't exist | ||
if (filename[1]) { | ||
if (filename && filename[1]) { | ||
filename = filename[1]; | ||
@@ -124,3 +212,3 @@ } else { | ||
window.URL.revokeObjectURL(downloadUrl); | ||
window.URL.revokeObjectURL(download_url); | ||
return done(); | ||
@@ -138,3 +226,12 @@ } | ||
result = reader.result; | ||
type = response.type; | ||
if (!type) { | ||
type = response.type; | ||
} else if (type != response.type) { | ||
// Sometimes (on Chrome at least) | ||
// the type is set to "text/xml", even when we're sending | ||
// a JSON string. Seems like onloadend is fired too early? | ||
// Or the data in "response" hasn't been updated yet, at least | ||
} | ||
done(); | ||
@@ -177,2 +274,4 @@ }; | ||
this.serialized_body = body; | ||
xhr.send(body); | ||
@@ -187,2 +286,10 @@ } else { | ||
// If it already finished, do nothing | ||
if (finished) { | ||
return; | ||
} | ||
finished = true; | ||
that.time_ended = Date.now(); | ||
if (!error && xhr.status > 399) { | ||
@@ -193,15 +300,41 @@ error = new Error(xhr.statusText); | ||
if (type && type.indexOf('json') > -1 && result) { | ||
try { | ||
result = Collection.JSON.undry(result); | ||
} catch (err) { | ||
console.error('Error parsing JSON string from "' + that.url.path + '":', result, err); | ||
return pledge.reject(err); | ||
} | ||
} | ||
if (error) { | ||
error.result = result; | ||
pledge.reject(error); | ||
that.error = error; | ||
} else { | ||
if (type && type.indexOf('json') > -1 && result) { | ||
result = Collection.JSON.undry(result); | ||
} | ||
that.result = result; | ||
pledge.resolve(result); | ||
Blast.state.reportSuccess(); | ||
} | ||
} | ||
// Check the connection state | ||
Blast.state.checkConnection().done(function onCheck(err, result) { | ||
// If an error already fired, do nothing | ||
if (error) { | ||
return; | ||
} | ||
if (err) { | ||
error = err; | ||
} else if (!result) { | ||
error = new Error('Connection failed'); | ||
} | ||
if (error) { | ||
done(); | ||
} | ||
}); | ||
return pledge; | ||
@@ -211,2 +344,13 @@ }); | ||
/** | ||
* Get a response header | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
*/ | ||
Request.setMethod(function getResponseHeader(name) { | ||
return this.xhr.getResponseHeader(name); | ||
}); | ||
/** | ||
* Hook into the original open method | ||
@@ -216,3 +360,3 @@ * | ||
* @since 0.6.2 | ||
* @version 0.6.2 | ||
* @version 0.7.0 | ||
* | ||
@@ -225,2 +369,3 @@ * @param {String} method | ||
this.open_url = url; | ||
this[START_SYMBOL] = Date.now(); | ||
Blast.emit('xhr_open', this); | ||
@@ -235,5 +380,6 @@ return original_open.apply(this, arguments); | ||
* @since 0.6.2 | ||
* @version 0.6.2 | ||
* @version 0.7.0 | ||
*/ | ||
XMLHttpRequest.prototype.send = function send() { | ||
this[START_SYMBOL] = Date.now(); | ||
Blast.emit('xhr_send', this); | ||
@@ -240,0 +386,0 @@ return original_send.apply(this, arguments); |
@@ -23,3 +23,3 @@ module.exports = function BlastRequestServer(Blast, Collection) { | ||
* @since 0.2.0 | ||
* @version 0.6.4 | ||
* @version 0.7.0 | ||
*/ | ||
@@ -31,5 +31,6 @@ Request.setMethod(function _make_request(options) { | ||
protocol, | ||
finished, | ||
origin, | ||
config, | ||
pledge = new Pledge(), | ||
pledge = new Blast.Classes.Pledge(), | ||
method = this.method_info, | ||
@@ -78,2 +79,4 @@ body = this.body, | ||
this.serialized_body = body; | ||
// Create the request | ||
@@ -154,2 +157,4 @@ req = protocol.request(config, function gotResponse(res) { | ||
error = new Error(error); | ||
error.request = that; | ||
error.result = body; | ||
} else { | ||
@@ -165,3 +170,3 @@ error = null; | ||
req.on('error', function onRequestError(err) { | ||
callback(err); | ||
done(err); | ||
}); | ||
@@ -177,2 +182,10 @@ | ||
function done(err, response) { | ||
if (finished) { | ||
return; | ||
} | ||
finished = true; | ||
that.time_ended = Date.now(); | ||
if (err) { | ||
@@ -179,0 +192,0 @@ pledge.reject(err); |
@@ -12,3 +12,3 @@ module.exports = function BlastRequest(Blast, Collection) { | ||
* @since 0.2.0 | ||
* @version 0.6.2 | ||
* @version 0.7.0 | ||
* | ||
@@ -19,2 +19,11 @@ * @param {Object|RURL} options | ||
// Set the time it was created | ||
this.time_created = Date.now(); | ||
// And the time it was started will be set later | ||
this.time_started = null; | ||
// And the time the request ended | ||
this.time_ended = null; | ||
// Empty headers object | ||
@@ -29,2 +38,9 @@ this.headers = {}; | ||
// The serialized body that was ultimately sent | ||
this.serialized_body = null; | ||
// Force a download even if it's an inline response? | ||
// (Normally it's only downloaded if it's an attachment) | ||
this.download_if_inline = null; | ||
if (options) { | ||
@@ -116,2 +132,37 @@ this.setOptions(options); | ||
/** | ||
* Default timeout in ms | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @type {Number} | ||
*/ | ||
Request.setProperty('timeout', null); | ||
/** | ||
* Default max timeout in ms | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @type {Number} | ||
*/ | ||
Request.setProperty('max_timeout', 30000); | ||
/** | ||
* Refer to the time_started | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.6.2 | ||
* @version 0.7.0 | ||
* | ||
* @type {Number} | ||
*/ | ||
Request.setProperty(function request_start() { | ||
return this.time_started; | ||
}); | ||
/** | ||
* The HTTP method name to use | ||
@@ -190,4 +241,6 @@ * | ||
if (typeof options == 'string' || RURL.isUrl(options)) { | ||
if (typeof options == 'string' || Blast.Classes.RURL.isUrl(options)) { | ||
return this.setUrl(options); | ||
} else if (options.url || options.href) { | ||
this.setUrl(options.url || options.href); | ||
} | ||
@@ -217,3 +270,7 @@ | ||
} else { | ||
this[key] = options[key]; | ||
if (key == 'headers') { | ||
this.setHeader(options[key]); | ||
} else { | ||
this[key] = options[key]; | ||
} | ||
} | ||
@@ -294,3 +351,3 @@ } | ||
* @since 0.2.0 | ||
* @version 0.6.2 | ||
* @version 0.7.0 | ||
* | ||
@@ -303,2 +360,7 @@ * @return {Pledge} | ||
// Set the time when the request started | ||
if (this.time_started == null) { | ||
this.time_started = Date.now(); | ||
} | ||
// Don't start the same request twice | ||
@@ -313,5 +375,2 @@ if (this.original_url) { | ||
// Date when the request started | ||
this.request_start = Date.now(); | ||
// Do the response, follow redirects | ||
@@ -318,0 +377,0 @@ return this._make_request(); |
@@ -13,3 +13,3 @@ module.exports = function BlastURL(Blast, Collection, Bound, Obj) { | ||
* @since 0.1.3 | ||
* @version 0.5.7 | ||
* @version 0.7.0 | ||
* | ||
@@ -20,4 +20,20 @@ * @param {String} address | ||
RURL = Collection.Function.inherits(function RURL(address, base) { | ||
this._data = {}; | ||
this._data = { | ||
from_base : null, | ||
pathname : null, | ||
segments : null, | ||
host : null, | ||
hostname : null, | ||
port : null, | ||
protocol : null, | ||
search : null, | ||
query : null, | ||
hash : null, | ||
auth : null, | ||
username : null, | ||
password : null, | ||
slashes : null, | ||
}; | ||
if (arguments.length) { | ||
@@ -1538,3 +1554,3 @@ this._parseInput(address, base); | ||
* @since 0.1.4 | ||
* @version 0.5.7 | ||
* @version 0.7.0 | ||
* | ||
@@ -1549,2 +1565,6 @@ * @param {String} name | ||
if (arguments.length == 1) { | ||
if (name && typeof name == 'object') { | ||
return this.addQuery(name); | ||
} | ||
return this.query[name]; | ||
@@ -1551,0 +1571,0 @@ } |
@@ -9,3 +9,6 @@ module.exports = function BlastImmediate(Blast, Collection) { | ||
realNextTick = global.nextTick, | ||
handles = {}; | ||
idle_counter = 0, | ||
last_idle = 0, | ||
handles = {}, | ||
idles = {}; | ||
@@ -16,3 +19,3 @@ // Ensure a nextTick implementation | ||
realNextTick = global.process.nextTick; | ||
} else if (typeof Promise !== 'undefined' && Promise.resolve) { | ||
} else if (typeof Promise !== 'undefined' && Promise !== Blast.Classes.Pledge && Promise.resolve) { | ||
let resolved = Promise.resolve(); | ||
@@ -19,0 +22,0 @@ realNextTick = function nextTick(callback) { |
@@ -145,2 +145,58 @@ module.exports = function BlastSorting(Blast, Collection) { | ||
/** | ||
* Sort the array safely | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @return {Array} | ||
*/ | ||
Blast.definePrototype('Array', function safesort() { | ||
return this.sort(safesortComparer); | ||
}); | ||
/** | ||
* The comparison function used by the safesorter | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @return {Number} | ||
*/ | ||
function safesortComparer(a, b) { | ||
var type_a = typeof a, | ||
type_b = typeof b; | ||
if (type_a == 'object') { | ||
if (a == null) { | ||
type_a = 'null'; | ||
} else if (a.constructor == null) { | ||
type_a = 'nullobject'; | ||
a = null; | ||
} | ||
} | ||
if (type_b == 'object') { | ||
if (b == null) { | ||
type_b = 'null'; | ||
} else if (b.constructor == null) { | ||
type_b = 'nullobject'; | ||
b = null; | ||
} | ||
} | ||
if (a < b) { | ||
return -1; | ||
} | ||
if (a > b) { | ||
return 1; | ||
} | ||
// Equals | ||
return 0; | ||
} | ||
}; |
272
lib/state.js
module.exports = function BlastState(Blast, Collection) { | ||
var online_change_symbol = Symbol('online_change'), | ||
check_count_symbol = Symbol('check_count'), | ||
set_online_symbol = Symbol('set_online'), | ||
@@ -8,3 +9,5 @@ visibility_symbol = Symbol('visibility'), | ||
endpoint_symbol = Symbol('endpoint'), | ||
success_symbol = Symbol('success'), | ||
online_symbol = Symbol('online'), | ||
queue_symbol = Symbol('queue_id'), | ||
busy_symbol = Symbol('busy'), | ||
@@ -30,2 +33,28 @@ same_symbol = Symbol('same'), | ||
/** | ||
* How many endpoint checks were there? | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @type {Number} | ||
*/ | ||
State.setProperty(function check_count() { | ||
return this[check_count_symbol] || 0; | ||
}); | ||
/** | ||
* The last successful request timestamp | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @type {Number} | ||
*/ | ||
State.setProperty(function last_success() { | ||
return this[success_symbol] || 0; | ||
}); | ||
/** | ||
* Should we save data? | ||
@@ -72,2 +101,26 @@ * | ||
/** | ||
* The timeout in ms to use | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @type {Number} | ||
*/ | ||
State.setProperty(function rtt_timeout() { | ||
var timeout = this.rtt; | ||
if (!timeout) { | ||
timeout = 2500; | ||
} else if (timeout < 1000) { | ||
timeout = 1000; | ||
} else if (timeout > 5000) { | ||
timeout = 5000; | ||
} | ||
return timeout; | ||
}); | ||
/** | ||
* The estimated downlink in mbit/s | ||
@@ -125,8 +178,8 @@ * | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 1.2.3 | ||
* @version 1.2.3 | ||
* @since 0.6.2 | ||
* @version 0.7.0 | ||
* | ||
* @type {Number} | ||
*/ | ||
State.setProperty(function online_status_duration() { | ||
State.setProperty(function current_status_duration() { | ||
@@ -184,3 +237,3 @@ if (!this[online_change_symbol]) { | ||
* @since 0.6.2 | ||
* @version 0.6.2 | ||
* @version 0.7.0 | ||
* | ||
@@ -191,7 +244,21 @@ * @param {Boolean} value | ||
var was_online = this[online_symbol]; | ||
var was_online = this[online_symbol], | ||
now = Date.now(); | ||
value = !!value; | ||
this[online_symbol] = value; | ||
// If on-line, set the last successful time | ||
if (value) { | ||
this[success_symbol] = now; | ||
} | ||
if (was_online !== value) { | ||
this[online_change_symbol] = now; | ||
this[same_symbol] = 0; | ||
} else { | ||
this[same_symbol]++; | ||
} | ||
if (value && (!was_online || was_online == null)) { | ||
@@ -205,9 +272,2 @@ this.unsee('offline'); | ||
if (was_online !== value) { | ||
this[online_change_symbol] = Date.now(); | ||
this[same_symbol] = 0; | ||
} else { | ||
this[same_symbol]++; | ||
} | ||
return value; | ||
@@ -273,3 +333,3 @@ }); | ||
window.addEventListener('online', function onOnline() { | ||
that.checkConnection(); | ||
that.checkConnection(true); | ||
}); | ||
@@ -352,3 +412,3 @@ | ||
* @since 0.6.2 | ||
* @version 0.6.2 | ||
* @version 0.7.0 | ||
* | ||
@@ -359,3 +419,3 @@ * @param {Function} callback | ||
*/ | ||
State.setMethod(function checkConnection(callback) { | ||
State.setMethod(function checkConnection(hint, callback) { | ||
@@ -368,2 +428,7 @@ var that = this, | ||
if (typeof hint == 'function') { | ||
callback = hint; | ||
hint = null; | ||
} | ||
pledge.done(callback); | ||
@@ -383,13 +448,5 @@ | ||
if (typeof hawkejs != 'undefined' && this.website_endpoint) { | ||
timeout = this.rtt; | ||
if (this.website_endpoint) { | ||
timeout = this.rtt_timeout; | ||
if (!timeout) { | ||
timeout = 2500; | ||
} else if (timeout < 1000) { | ||
timeout = 1000; | ||
} else if (timeout > 5000) { | ||
timeout = 5000; | ||
} | ||
this[busy_symbol] = pledge; | ||
@@ -413,4 +470,10 @@ | ||
hawkejs.scene.fetch(url, {head: true}, function done(err, body, xhr) { | ||
if (!this[check_count_symbol]) { | ||
this[check_count_symbol] = 0; | ||
} | ||
this[check_count_symbol]++; | ||
Blast.fetch({url: url, head: true}, function done(err, body, xhr) { | ||
that[busy_symbol] = false; | ||
@@ -428,4 +491,18 @@ | ||
} else { | ||
this.online = true; | ||
pledge.resolve(true); | ||
if (hint != null) { | ||
this.online = hint; | ||
} else if (this.current_status_duration < this.rtt_timeout * 2) { | ||
// Queue a new check | ||
this.queueCheck(Math.min(1000, Math.max(this.rtt_timeout, 1000))); | ||
} else { | ||
// When we have no endpoint to check and the online status | ||
// has not yet been set, we'll set it once now. | ||
if (this[online_symbol] == null) { | ||
this.online = true; | ||
} | ||
} | ||
pledge.resolve(!!this.online); | ||
} | ||
@@ -435,2 +512,141 @@ | ||
}); | ||
/** | ||
* Report a connection error | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
*/ | ||
State.setMethod(function reportError(error) { | ||
if (!error) { | ||
return; | ||
} | ||
// Aborts can be ignored | ||
if (error.number === 0) { | ||
return; | ||
} | ||
// Actual dns or other kind of timeout | ||
if (error.number === 408) { | ||
console.log('Reported error, setting to offline:', error) | ||
// If a timeout amount is given, | ||
// see if any other request has successfully finished in that time | ||
if (error.timeout || error.request_start) { | ||
let start = error.request_start || (Date.now() - error.timeout), | ||
diff = this.last_success - start; | ||
if (diff > 0) { | ||
console.log('... Other request finished after this started, not setting offline'); | ||
return; | ||
} | ||
} | ||
this.online = false; | ||
} | ||
}); | ||
/** | ||
* Report a successful connection | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @param {String} type | ||
* @param {Object} event | ||
*/ | ||
State.setMethod(function reportSuccess(type, event) { | ||
this.online = true; | ||
}); | ||
/** | ||
* See if the given ms can be counted as a connection timeout | ||
* (If any other request was successful, then it has not timedout) | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @return {Boolean} | ||
*/ | ||
State.setMethod(function connectionHasTimedOut(ms) { | ||
if (ms == null) { | ||
ms = this.rtt_timeout; | ||
} | ||
return this.msUntilTimeout(ms) <= 0; | ||
}); | ||
/** | ||
* Get the time (in ms) until a timeout could occur | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @param {Number} ms The milliseconds that should count as a timeout | ||
* | ||
* @return {Number} | ||
*/ | ||
State.setMethod(function msUntilTimeout(ms) { | ||
if (!this.online) { | ||
return 0; | ||
} | ||
if (ms == null) { | ||
ms = this.rtt_timeout; | ||
} | ||
// See if any last success has been registered | ||
if (this.last_success) { | ||
let now = Date.now(), | ||
ago = now - this.last_success, | ||
left = ms - ago; | ||
// If a success has happened in less time than the given ms, | ||
// we still have some time left | ||
if (left <= 0) { | ||
return 0; | ||
} else if (left < ms) { | ||
return left; | ||
} | ||
} | ||
// No timeout has been detected, so the given time should be kept | ||
return ms; | ||
}); | ||
/** | ||
* Queue a new check | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @param {Number} ms The milliseconds to wait before checking again | ||
*/ | ||
State.setMethod(function queueCheck(ms) { | ||
if (this[queue_symbol]) { | ||
// @TODO: could starve the checks? | ||
clearTimeout(this[queue_symbol]); | ||
} | ||
if (!ms) { | ||
ms = 200; | ||
} | ||
let that = this; | ||
this[queue_symbol] = setTimeout(function doCheck() { | ||
that.checkConnection(); | ||
that[queue_symbol] = null; | ||
}, ms); | ||
}); | ||
}; |
@@ -44,7 +44,7 @@ module.exports = function BlastString(Blast, Collection, Bound, Obj) { | ||
/** | ||
* Serialize the given parameter to valid HTML attributes | ||
* Decode the given string | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.3.4 | ||
* @version 0.3.4 | ||
* @version 0.7.0 | ||
* | ||
@@ -70,2 +70,32 @@ * @param {Object} value The string to decode | ||
if (separator == null) { | ||
let tokens = Bound.String.tokenizeHTML(value, {state: Blast.HTML_TOKENIZER_STATES.TAG_CONTENT}), | ||
token; | ||
for (i = 0; i < tokens.length; i++) { | ||
token = tokens[i]; | ||
if (token.type == 'attribute') { | ||
if (key) { | ||
result[key] = val; | ||
} | ||
key = token.value; | ||
val = undefined; | ||
} else if (token.type == 'equals') { | ||
continue; | ||
} else if (token.type == 'identifier' || token.type == 'string') { | ||
val = token.value; | ||
} else if (token.type == 'string_open' || token.type == 'string_close') { | ||
continue; | ||
} | ||
} | ||
if (key) { | ||
result[key] = val; | ||
} | ||
return result; | ||
} | ||
if (typeof separator == 'string') { | ||
@@ -81,2 +111,7 @@ separator = Bound.RegExp.interpret(separator); | ||
pair = pieces[i]; | ||
if (!pair) { | ||
continue; | ||
} | ||
index = pair.indexOf('='); | ||
@@ -144,3 +179,3 @@ | ||
* @since 0.1.4 | ||
* @version 0.1.4 | ||
* @version 0.7.0 | ||
* | ||
@@ -153,9 +188,16 @@ * @param {String} value | ||
value = Collection.String.decodeURI(value); | ||
value = Collection.String.decodeURI(value).trim(); | ||
try { | ||
return JSON.parse(value); | ||
} catch (err) { | ||
return value.trim(); | ||
let first = value[0]; | ||
if (first == '[' || first == '{' || first == '"') { | ||
try { | ||
return JSON.parse(value); | ||
} catch (err) { | ||
return value; | ||
} | ||
} | ||
return value; | ||
}); | ||
@@ -624,3 +666,3 @@ | ||
* @since 0.6.5 | ||
* @version 0.6.6 | ||
* @version 0.7.0 | ||
* | ||
@@ -654,28 +696,35 @@ * @param {String source | ||
if (options && options.blocks) { | ||
blocks = options.blocks; | ||
if (options) { | ||
// Get all indexes in advance, so we don't need to check every char | ||
// to see if it matches a custom block | ||
for (key in blocks) { | ||
open = Bound.String.allIndexesOf(source, blocks[key].open); | ||
if (options.state) { | ||
state = options.state; | ||
} | ||
if (open.length) { | ||
do_blocks = true; | ||
if (options.blocks) { | ||
blocks = options.blocks; | ||
if (!block_indexes) { | ||
block_indexes = {}; | ||
block_indexes_arr = []; | ||
} | ||
// Get all indexes in advance, so we don't need to check every char | ||
// to see if it matches a custom block | ||
for (key in blocks) { | ||
open = Bound.String.allIndexesOf(source, blocks[key].open); | ||
for (i = 0; i < open.length; i++) { | ||
block_indexes[open[i]] = key; | ||
block_indexes_arr.push(open[i]); | ||
if (open.length) { | ||
do_blocks = true; | ||
if (!block_indexes) { | ||
block_indexes = {}; | ||
block_indexes_arr = []; | ||
} | ||
for (i = 0; i < open.length; i++) { | ||
block_indexes[open[i]] = key; | ||
block_indexes_arr.push(open[i]); | ||
} | ||
} | ||
} | ||
} | ||
if (do_blocks) { | ||
Bound.Array.flashsort(block_indexes_arr); | ||
block_indexes_arr = Bound.Array.unique(block_indexes_arr); | ||
if (do_blocks) { | ||
Bound.Array.flashsort(block_indexes_arr); | ||
block_indexes_arr = Bound.Array.unique(block_indexes_arr); | ||
} | ||
} | ||
@@ -697,3 +746,2 @@ } | ||
current = null; | ||
end = source.indexOf(block.close, i); | ||
@@ -714,2 +762,10 @@ | ||
state = STATE.TAG_CONTENT; | ||
current = null; | ||
} else if (current) { | ||
current = { | ||
type : current.type, | ||
value : '' | ||
}; | ||
result.push(current); | ||
} | ||
@@ -760,3 +816,10 @@ | ||
if (state === STATE.ATTR_VAL) { | ||
if (!char.trim()) { | ||
// Ignore whitespace right after the equals sign | ||
if (current.type == 'equals') { | ||
continue; | ||
} | ||
state = STATE.TAG_CONTENT; | ||
@@ -766,5 +829,11 @@ current = null; | ||
state = STATE.STRING_D; | ||
result.push({ | ||
type : 'string_open', | ||
value : char | ||
}); | ||
current = { | ||
type : 'string', | ||
value : char | ||
value : '' | ||
}; | ||
@@ -775,5 +844,11 @@ | ||
state = STATE.STRING_S; | ||
result.push({ | ||
type : 'string_open', | ||
value : char | ||
}); | ||
current = { | ||
type : 'string', | ||
value : char | ||
value : '' | ||
}; | ||
@@ -801,9 +876,15 @@ | ||
current.value += char; | ||
if (char === '"') { | ||
result.push({ | ||
type : 'string_close', | ||
value : char | ||
}); | ||
if (char === '"') { | ||
current = null; | ||
state = STATE.TAG_CONTENT; | ||
continue; | ||
} | ||
current.value += char; | ||
continue; | ||
@@ -818,9 +899,15 @@ } | ||
current.value += char; | ||
if (char === "'") { | ||
result.push({ | ||
type : 'string_close', | ||
value : char | ||
}); | ||
if (char === "'") { | ||
current = null; | ||
state = STATE.TAG_CONTENT; | ||
continue; | ||
} | ||
current.value += char; | ||
continue; | ||
@@ -831,3 +918,4 @@ } | ||
if (!char.trim()) { | ||
if (!char.trim() || char == '>' || char == '"' || char == "'" || char == '`' || char == '=' || char == '<') { | ||
// Ignore spaces right after the = | ||
state = STATE.TAG_CONTENT; | ||
@@ -1002,3 +1090,4 @@ current = null; | ||
if (char == '=') { | ||
result.push({type: 'equals', value: '='}); | ||
current = {type: 'equals', value: '='}; | ||
result.push(current); | ||
state = STATE.ATTR_VAL; | ||
@@ -1145,3 +1234,3 @@ continue; | ||
* @since 0.1.3 | ||
* @version 0.1.11 | ||
* @version 0.7.0 | ||
* | ||
@@ -1161,6 +1250,6 @@ * @return {String} The sluggifier string | ||
// Romanize the string (remove diacritics) | ||
result = Bound.String.romanize(result); | ||
result = Collection.String.prototype.romanize.call(result); | ||
// Decode HTML | ||
result = Bound.String.decodeHTML(result); | ||
result = Collection.String.prototype.decodeHTML.call(result); | ||
@@ -1174,3 +1263,3 @@ // Replace non-words with placeholders | ||
// Truncate repeats of the separator | ||
result = result.replace(Bound.RegExp.interpret('/\\' + separator + '+/g'), separator); | ||
result = result.replace(RegExp('\\' + separator + '+', 'g'), separator); | ||
@@ -1980,3 +2069,3 @@ // Make sure the first char isn't the separator | ||
* @since 0.1.4 | ||
* @version 0.5.7 | ||
* @version 0.7.0 | ||
* | ||
@@ -1987,3 +2076,3 @@ * @param {Object} values | ||
*/ | ||
Blast.definePrototype('String', function assign(_values, remove_used) { | ||
Blast.definePrototype('String', function assign(_values, remove_used, cast) { | ||
@@ -2022,2 +2111,7 @@ var pattern, | ||
if (val != null) { | ||
if (cast) { | ||
val = cast(val); | ||
} | ||
result = result.replace(match[0], val); | ||
@@ -2024,0 +2118,0 @@ |
@@ -8,2 +8,16 @@ module.exports = function BlastSymbol(Blast, Collection, Bound, Obj) { | ||
if (typeof Symbol != 'undefined') { | ||
/** | ||
* Is this a symbol? | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @return {Boolean} | ||
*/ | ||
Blast.defineStatic(Symbol, function isSymbol(value) { | ||
return typeof value == 'symbol'; | ||
}); | ||
return; | ||
@@ -19,3 +33,3 @@ } | ||
* @since 0.5.11 | ||
* @version 0.6.0 | ||
* @version 0.7.0 | ||
* | ||
@@ -36,9 +50,44 @@ * @param {String} description | ||
// Make sure the description is a string | ||
this.__description = (description === undefined ? '' : String(description)); | ||
symbol.__description = (description === undefined ? '' : String(description)); | ||
// Create the name to use | ||
this.__name = '@@blastSymbol_' + this.__description + (++postfix); | ||
symbol.__name = '@@blastSymbol_' + symbol.__description + (++postfix); | ||
return symbol; | ||
}, true); | ||
/** | ||
* Simple indicator that this is our polyfill | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @type {Boolean} | ||
*/ | ||
Blast.defineStatic(NewSymbol, 'polyfilled', true); | ||
/** | ||
* Is this a symbol? | ||
* | ||
* @author Jelle De Loecker <jelle@develry.be> | ||
* @since 0.7.0 | ||
* @version 0.7.0 | ||
* | ||
* @return {Boolean} | ||
*/ | ||
Blast.defineStatic(NewSymbol, function isSymbol(value) { | ||
if (!value || typeof value != 'string' || value.length < 14) { | ||
return false; | ||
} | ||
if (value[0] == '@' && value.indexOf('@@blastSymbol_') === 0) { | ||
return true; | ||
} | ||
return false; | ||
}); | ||
/** | ||
* Unexposed constructor, used to foil `instanceof` calls | ||
@@ -65,10 +114,13 @@ * | ||
* @since 0.5.11 | ||
* @version 0.5.11 | ||
* @version 0.7.0 | ||
* | ||
* @return {String} | ||
*/ | ||
Blast.definePrototype('Symbol', function toString() { | ||
Blast.definePrototype(HiddenSymbol, function toString() { | ||
return this.__name; | ||
}, true); | ||
// Add some expected symbols | ||
Symbol.iterator = Symbol('iterator'); | ||
}; |
{ | ||
"name": "protoblast", | ||
"description": "Native object expansion library", | ||
"version": "0.6.7", | ||
"version": "0.7.0", | ||
"author": "Jelle De Loecker <jelle@develry.be>", | ||
@@ -39,4 +39,4 @@ "keywords": [ | ||
"engines": { | ||
"node": ">=4.8" | ||
"node": ">=8.9.0" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
3301383
54
51673
2