diff --git a/index.html b/index.html index 174ef82f..7fea7595 100644 --- a/index.html +++ b/index.html @@ -4,9 +4,8 @@ OxJS - A JavaScript Library for Web Applications - - + - + \ No newline at end of file diff --git a/source/Ox.compat.js b/source/Ox.compat.js deleted file mode 100644 index 7e809841..00000000 --- a/source/Ox.compat.js +++ /dev/null @@ -1,18 +0,0 @@ -const init = [], initLoad = []; -window.Ox = function(value) { - console.log("delay stuff until we are done") - init.push(value) -}; -window.Ox.load = function() { - initLoad.push(arguments) -}; - -(async () => { - const module = await import('./Ox/index.js'); - console.log("Ox was loaded", init); - init.forEach(value => Ox(value)) - delete init - initLoad.forEach(arguments => Ox.load.apply(null, arguments)) - delete initLoad -})(); - diff --git a/source/Ox/index.js b/source/Ox/index.js deleted file mode 100644 index 58e220be..00000000 --- a/source/Ox/index.js +++ /dev/null @@ -1,102 +0,0 @@ -'use strict'; - -/*@ -Ox The `Ox` object - See `Ox.wrap` for details. - (value) -> wrapped value - value <*> Any value -@*/ - -import * as OxCore from './js/Core.js'; -import * as OxFunction from './js/Function.js'; -import * as OxPolyfill from './js/Polyfill.js'; -import * as OxArray from './js/Array.js'; -import * as OxString from './js/String.js'; -import * as OxCollection from './js/Collection.js'; -import * as OxMath from './js/Math.js'; - - -import * as OxAsync from './js/Async.js'; -import * as OxColor from './js/Color.js'; -import * as OxConstants from './js/Constants.js'; -import * as OxDate from './js/Date.js'; -import * as OxDOM from './js/DOM.js'; -import * as OxEncoding from './js/Encoding.js'; -import * as OxFormat from './js/Format.js'; -import * as OxGeo from './js/Geo.js'; -import * as OxHash from './js/Hash.js'; -import * as OxHTML from './js/HTML.js'; -import * as OxJavaScript from './js/JavaScript.js'; -import * as OxLocale from './js/Locale.js'; -import * as OxObject from './js/Object.js'; -import * as OxRegExp from './js/RegExp.js'; -import * as OxRequest from './js/Request.js'; -import * as OxType from './js/Type.js'; -import * as OxVideo from './js/Video.js'; - - -const Ox = function(value) { - return OxCore.wrap(value) -}; - -Object.assign(Ox, - OxCore, - OxFunction, - OxPolyfill, - OxArray, - OxString, - OxCollection, - OxMath, - OxAsync, - OxColor, - OxConstants, - OxDate, - OxDOM, - OxEncoding, - OxFormat, - OxGeo, - OxHash, - OxHTML, - OxJavaScript, - OxLocale, - OxObject, - OxRegExp, - OxRequest, - OxType, - OxVideo, -); - -export default Ox; -export { Ox }; - -// For backward compatibility with global usage -if (typeof window !== 'undefined') { - window.Ox = Ox; - Ox.loadPolyfill(window, Ox) - - /*@ - Ox.documentReady Calls a callback function once the DOM is ready - (callback) -> If true, the document was ready - callback Callback function - @*/ - Ox.documentReady = (function() { - var callbacks = []; - document.onreadystatechange = window.onload = function() { - if (document.readyState == 'complete') { - callbacks.forEach(function(callback) { - callback(); - }); - document.onreadystatechange = window.onload = null; - } - }; - return function(callback) { - if (document.readyState == 'complete') { - callback(); - return true; - } else { - callbacks.push(callback); - return false; - } - }; - }()); -} diff --git a/source/Ox/js/Array.js b/source/Ox/js/Array.js index 380aa357..6beae511 100644 --- a/source/Ox/js/Array.js +++ b/source/Ox/js/Array.js @@ -1,19 +1,5 @@ 'use strict'; -import * as OxCore from './Core.js'; -import * as OxBase from './Base.js'; -import * as OxFunction from './Function.js'; -import * as OxType from './Type.js'; - -const Ox = {}; - -Object.assign(Ox, - OxCore, - OxBase, - OxFunction, - OxType, -); - /*@ Ox.api Turns an array into a list API `Ox.api` takes an array and returns a function that allows you to run @@ -177,7 +163,7 @@ Ox.api Turns an array into a list API > Ox.test.apiResults[9].data {items: [{name: 'John Cale'}, {name: 'Brian Eno'}]} @*/ -export function api(items, options) { +Ox.api = function(items, options) { options = options || {}; @@ -456,7 +442,7 @@ Ox.compact Removes `null` or `undefined` values from an array > Ox.compact([null,,1,,2,,3]) [1, 2, 3] @*/ -export function compact(array) { +Ox.compact = function(array) { return array.filter(function(value) { return value != null; }); @@ -475,7 +461,7 @@ Ox.find Returns array elements that match a string > Ox.find(['Bar', 'Barfoo', 'Foo', 'Foobar'], 'foo', true) ['Foo', 'Foobar'] @*/ -export function find(array, string, leading) { +Ox.find = function(array, string, leading) { var matches = [[], []]; string = string.toLowerCase(); array.forEach(function(value) { @@ -494,7 +480,7 @@ Ox.flatten Flattens an array > Ox.flatten([1, [2, [3], 2], 1]) [1, 2, 3, 2, 1] @*/ -export function flatten(array) { +Ox.flatten = function(array) { var ret = []; array.forEach(function(value) { if (Ox.isArray(value)) { @@ -507,7 +493,7 @@ export function flatten(array) { }; // FIXME: add docs and tests -export function getIndex(array, key, value) { +Ox.getIndex = function(array, key, value) { return Ox.indexOf(array, function(obj) { return obj[key] === value; }); @@ -523,13 +509,13 @@ Ox.getIndexById Returns the first array index of an object with a given id > Ox.getIndexById([{id: 'foo', str: 'Foo'}, {id: 'bar', str: 'Bar'}], 'baz') -1 @*/ -export function getIndexById(array, id) { - return getIndex(array, 'id', id); +Ox.getIndexById = function(array, id) { + return Ox.getIndex(array, 'id', id); }; // FIXME: add docs and tests -export function getObject(array, key, value) { - var index = getIndex(array, key, value); +Ox.getObject = function(array, key, value) { + var index = Ox.getIndex(array, key, value); return index > -1 ? array[index] : null; }; @@ -543,12 +529,12 @@ Ox.getObjectById Returns the first object in an array with a given id > Ox.getObjectById([{id: 'foo', str: 'Foo'}, {id: 'bar', str: 'Bar'}], 'baz') null @*/ -export function getObjectById(array, id) { - return getObject(array, 'id', id); +Ox.getObjectById = function(array, id) { + return Ox.getObject(array, 'id', id); }; /* -export function indexOf(arr) { +Ox.indexOf = function(arr) { // indexOf for primitives, test for function, deep equal for others }; */ @@ -569,7 +555,7 @@ Ox.last Gets or sets the last element of an array > Ox.last('123') '3' @*/ -export function last(array, value) { +Ox.last = function(array, value) { var ret; if (arguments.length == 1) { ret = array[array.length - 1]; @@ -590,7 +576,7 @@ Ox.makeArray Wraps any non-array in an array. ['foo'] @*/ // FIXME: rename to toArray -export function makeArray(value) { +Ox.makeArray = function(value) { var ret, type = Ox.typeOf(value); if (type == 'arguments' || type == 'nodelist') { ret = Ox.slice(value); @@ -613,7 +599,7 @@ Ox.nextValue Next value, given an array of numbers, a number and a direction > Ox.nextValue([], 1, 1) void 0 @*/ -export function nextValue(array, value, direction) { +Ox.nextValue = function(array, value, direction) { var found = false, nextValue; direction = direction || 1; direction == -1 && array.reverse(); @@ -655,7 +641,7 @@ Ox.range Python-style range > Ox.range(-1, -2, -0.5) [-1, -1.5] @*/ -export function range() { +Ox.range = function() { var array = []; Ox.loop.apply(null, Ox.slice(arguments).concat(function(index) { array.push(index); @@ -782,7 +768,7 @@ Ox.unique Removes duplicate values from an array > Ox.unique('foo') 'fo' @*/ -export function unique(array) { +Ox.unique = function(array) { return Ox.filter(array, function(value, index) { return array.indexOf(value) == index; }); @@ -795,7 +781,7 @@ Ox.zip Zips an array of arrays > Ox.zip([0, 1, 2], [3, 4, 5]) [[0, 3], [1, 4], [2, 5]] @*/ -export function zip() { +Ox.zip = function() { var args = arguments.length == 1 ? arguments[0] : Ox.slice(arguments), array = []; args[0].forEach(function(value, index) { diff --git a/source/Ox/js/Async.js b/source/Ox/js/Async.js index c0b6e17d..7607d841 100644 --- a/source/Ox/js/Async.js +++ b/source/Ox/js/Async.js @@ -1,297 +1,287 @@ 'use strict'; -import * as OxObject from './Object.js'; -import * as OxConstants from './Constants.js'; -import * as OxMath from './Math.js'; +(function() { -const Ox = {}; - -Object.assign(Ox, - OxObject, - OxConstants, - OxMath -); - - -function internalAsyncMap(forEach, collection, iterator, that, callback) { - var type = Ox.typeOf(collection), - results = type == 'object' ? {} : []; - callback = Ox.last(arguments); - that = arguments.length == 5 ? that : null; - forEach(collection, function(value, key, collection, callback) { - iterator(value, key, collection, function(value) { - results[key] = value; - callback(); - }); - }, that, function() { - callback(type == 'string' ? results.join('') : results); - }); -} - -export function asyncMap(array, iterator, that, callback) { - array = Ox.makeArray(array); - callback = Ox.last(arguments); - that = arguments.length == 4 ? that : null; - if (array.some(Ox.isArray)) { - Ox.serialMap(array, function(value, key, array, callback) { - Ox.parallelMap(Ox.makeArray(value), iterator, callback); - }, callback); - } else { - Ox.parallelMap(array, iterator, callback); - } -}; - -/*@ -Ox.nonblockingForEach Non-blocking `forEach` with synchronous iterator - (col, iterator[, that], callback[, ms]) -> undefined - collection Collection - iterator Iterator function - value <*> Value - key Key - collection The collection - that The iterator's `this` binding - callback Callback function - ms Number of milliseconds after which to insert a `setTimeout` call -@*/ -export function nonblockingForEach(collection, iterator, that, callback, ms) { - var i = 0, keys, last = Ox.last(arguments), - n, time, type = Ox.typeOf(collection); - callback = Ox.isFunction(last) ? last : arguments[arguments.length - 2]; - collection = type == 'array' || type == 'object' - ? collection : Ox.slice(collection); - keys = type == 'object' - ? Object.keys(collection) : Ox.range(collection.length); - ms = ms || 1000; - n = Ox.len(collection); - that = arguments.length == 5 || ( - arguments.length == 4 && Ox.isFunction(last) - ) ? that : null; - time = +new Date(); - iterate(); - function iterate() { - Ox.forEach(keys.slice(i), function(key) { - if (key in collection) { - if (iterator.call( - that, collection[key], key, collection - ) === false) { - i = n; - return false; - } - } - i++; - if (+new Date() >= time + ms) { - return false; // break - } - }); - if (i < n) { - setTimeout(function() { - time = +new Date(); - iterate(); - }, 1); - } else { - callback(); - } - } -}; - -/*@ -Ox.nonblockingMap Non-blocking `map` with synchronous iterator - (collection, iterator[, that], callback[, ms]) -> undefined - collection Collection - iterator Iterator function - that The iterator's `this` binding - callback Callback function - ms Number of milliseconds after which to insert a `setTimeout` call - - > Ox.nonblockingMap(Ox.range(100000), Ox.identity, function(r) { Ox.test(r.length, 100000); }) - undefined -@*/ -export function nonblockingMap(collection, iterator, that, callback, ms) { - var last = Ox.last(arguments), - type = Ox.typeOf(collection), - results = type == 'object' ? {} : []; - callback = Ox.isFunction(last) ? last : arguments[arguments.length - 2]; - that = arguments.length == 5 || ( - arguments.length == 4 && Ox.isFunction(last) - ) ? that : null; - Ox.nonblockingForEach(collection, function(value, key, collection) { - results[key] = iterator.call(that, value, key, collection); - }, function() { - callback(type == 'string' ? results.join('') : results); - }, ms); -}; - -/*@ -Ox.parallelForEach `forEach` with asynchronous iterator, running in parallel - (collection, iterator[, that], callback) -> undefined - collection Collection - iterator Iterator function - value <*> Value - key Key - collection The collection - callback Callback function - that The iterator's this binding - callback Callback function - - > Ox.parallelForEach(Ox.range(10), Ox.test.pfeIterator, function() { Ox.test(Ox.test.pfeNumber, 5); }) - undefined -@*/ -export function parallelForEach(collection, iterator, that, callback) { - var i = 0, n, type = Ox.typeOf(collection); - callback = callback || (arguments.length == 3 ? arguments[2] : Ox.noop); - collection = type == 'array' || type == 'object' - ? collection : Ox.slice(collection); - n = Ox.len(collection); - that = arguments.length == 4 ? that : null; - Ox.forEach(collection, function(value, key, collection) { - iterator.call(that, value, key, collection, function() { - ++i == n && callback(); - }); - }); -}; - -/*@ -Ox.parallelMap Parallel `map` with asynchronous iterator - (collection, iterator[, that], callback) -> undefined - collection Collection - iterator Iterator function - value <*> Value - key Key - collection The collection - callback Callback function - that The iterator's this binding - callback Callback function - results Results - - > Ox.parallelMap(Ox.range(10), Ox.test.pmIterator, function(r) { Ox.test(Ox.sum(r), 0); }) - undefined -@*/ -export function parallelMap() { - internalAsyncMap.apply(null, [Ox.parallelForEach].concat(Ox.slice(arguments))); -}; - -/*@ -Ox.serialForEach `forEach` with asynchronous iterator, run serially - (collection, iterator[, that], callback) -> undefined - collection Collection - iterator Iterator function - value <*> Value - key Key - collection The collection - callback Callback function - that The iterator's this binding - callback Callback function - - > Ox.serialForEach(Ox.range(10), Ox.test.sfeIterator, function() { Ox.test(Ox.test.sfeNumber, 5); }) - undefined -@*/ -export function serialForEach(collection, iterator, that, callback) { - var i = 0, keys, n, type = Ox.typeOf(collection); - callback = callback || (arguments.length == 3 ? arguments[2] : Ox.noop); - collection = type == 'array' || type == 'object' - ? collection : Ox.slice(collection); - keys = type == 'object' - ? Object.keys(collection) : Ox.range(collection.length); - n = Ox.len(collection); - that = arguments.length == 4 ? that : null; - iterate(); - function iterate(value) { - if (value !== false) { - if (keys[i] in collection) { - iterator.call( - that, - collection[keys[i]], - keys[i], - collection, - ++i < n ? iterate : callback - ); - } else { - ++i < n ? iterate() : callback(); - } - } else { - callback(); - } + }, that, function() { + callback(type == 'string' ? results.join('') : results); + }); } -}; -/*@ -Ox.serialMap Serial `map` with asynchronous iterator - (collection, iterator[, that], callback) -> undefined - collection Collection - iterator Iterator function - value <*> Value - key Key - collection The collection + Ox.asyncMap = function(array, iterator, that, callback) { + array = Ox.makeArray(array); + callback = Ox.last(arguments); + that = arguments.length == 4 ? that : null; + if (array.some(Ox.isArray)) { + Ox.serialMap(array, function(value, key, array, callback) { + Ox.parallelMap(Ox.makeArray(value), iterator, callback); + }, callback); + } else { + Ox.parallelMap(array, iterator, callback); + } + }; + + /*@ + Ox.nonblockingForEach Non-blocking `forEach` with synchronous iterator + (col, iterator[, that], callback[, ms]) -> undefined + collection Collection + iterator Iterator function + value <*> Value + key Key + collection The collection + that The iterator's `this` binding callback Callback function - that The iterator's this binding - callback Callback function - results Results - - > Ox.serialMap(Ox.range(10), Ox.test.smIterator, function(r) { Ox.test(Ox.sum(r), 0); }) - undefined -@*/ -export function serialMap(collection, iterator, that, callback) { - internalAsyncMap.apply(null, [Ox.serialForEach].concat(Ox.slice(arguments))); -}; -// FIXME: The above test with 10000 iterations blows the stack + ms Number of milliseconds after which to insert a `setTimeout` call + @*/ + Ox.nonblockingForEach = function(collection, iterator, that, callback, ms) { + var i = 0, keys, last = Ox.last(arguments), + n, time, type = Ox.typeOf(collection); + callback = Ox.isFunction(last) ? last : arguments[arguments.length - 2]; + collection = type == 'array' || type == 'object' + ? collection : Ox.slice(collection); + keys = type == 'object' + ? Object.keys(collection) : Ox.range(collection.length); + ms = ms || 1000; + n = Ox.len(collection); + that = arguments.length == 5 || ( + arguments.length == 4 && Ox.isFunction(last) + ) ? that : null; + time = +new Date(); + iterate(); + function iterate() { + Ox.forEach(keys.slice(i), function(key) { + if (key in collection) { + if (iterator.call( + that, collection[key], key, collection + ) === false) { + i = n; + return false; + } + } + i++; + if (+new Date() >= time + ms) { + return false; // break + } + }); + if (i < n) { + setTimeout(function() { + time = +new Date(); + iterate(); + }, 1); + } else { + callback(); + } + } + }; + /*@ + Ox.nonblockingMap Non-blocking `map` with synchronous iterator + (collection, iterator[, that], callback[, ms]) -> undefined + collection Collection + iterator Iterator function + that The iterator's `this` binding + callback Callback function + ms Number of milliseconds after which to insert a `setTimeout` call + + > Ox.nonblockingMap(Ox.range(100000), Ox.identity, function(r) { Ox.test(r.length, 100000); }) + undefined + @*/ + Ox.nonblockingMap = function(collection, iterator, that, callback, ms) { + var last = Ox.last(arguments), + type = Ox.typeOf(collection), + results = type == 'object' ? {} : []; + callback = Ox.isFunction(last) ? last : arguments[arguments.length - 2]; + that = arguments.length == 5 || ( + arguments.length == 4 && Ox.isFunction(last) + ) ? that : null; + Ox.nonblockingForEach(collection, function(value, key, collection) { + results[key] = iterator.call(that, value, key, collection); + }, function() { + callback(type == 'string' ? results.join('') : results); + }, ms); + }; + + /*@ + Ox.parallelForEach `forEach` with asynchronous iterator, running in parallel + (collection, iterator[, that], callback) -> undefined + collection Collection + iterator Iterator function + value <*> Value + key Key + collection The collection + callback Callback function + that The iterator's this binding + callback Callback function + + > Ox.parallelForEach(Ox.range(10), Ox.test.pfeIterator, function() { Ox.test(Ox.test.pfeNumber, 5); }) + undefined + @*/ + Ox.parallelForEach = function(collection, iterator, that, callback) { + var i = 0, n, type = Ox.typeOf(collection); + callback = callback || (arguments.length == 3 ? arguments[2] : Ox.noop); + collection = type == 'array' || type == 'object' + ? collection : Ox.slice(collection); + n = Ox.len(collection); + that = arguments.length == 4 ? that : null; + Ox.forEach(collection, function(value, key, collection) { + iterator.call(that, value, key, collection, function() { + ++i == n && callback(); + }); + }); + }; + + /*@ + Ox.parallelMap Parallel `map` with asynchronous iterator + (collection, iterator[, that], callback) -> undefined + collection Collection + iterator Iterator function + value <*> Value + key Key + collection The collection + callback Callback function + that The iterator's this binding + callback Callback function + results Results + + > Ox.parallelMap(Ox.range(10), Ox.test.pmIterator, function(r) { Ox.test(Ox.sum(r), 0); }) + undefined + @*/ + Ox.parallelMap = function() { + asyncMap.apply(null, [Ox.parallelForEach].concat(Ox.slice(arguments))); + }; + + /*@ + Ox.serialForEach `forEach` with asynchronous iterator, run serially + (collection, iterator[, that], callback) -> undefined + collection Collection + iterator Iterator function + value <*> Value + key Key + collection The collection + callback Callback function + that The iterator's this binding + callback Callback function + + > Ox.serialForEach(Ox.range(10), Ox.test.sfeIterator, function() { Ox.test(Ox.test.sfeNumber, 5); }) + undefined + @*/ + Ox.serialForEach = function(collection, iterator, that, callback) { + var i = 0, keys, n, type = Ox.typeOf(collection); + callback = callback || (arguments.length == 3 ? arguments[2] : Ox.noop); + collection = type == 'array' || type == 'object' + ? collection : Ox.slice(collection); + keys = type == 'object' + ? Object.keys(collection) : Ox.range(collection.length); + n = Ox.len(collection); + that = arguments.length == 4 ? that : null; + iterate(); + function iterate(value) { + if (value !== false) { + if (keys[i] in collection) { + iterator.call( + that, + collection[keys[i]], + keys[i], + collection, + ++i < n ? iterate : callback + ); + } else { + ++i < n ? iterate() : callback(); + } + } else { + callback(); + } + } + }; + + /*@ + Ox.serialMap Serial `map` with asynchronous iterator + (collection, iterator[, that], callback) -> undefined + collection Collection + iterator Iterator function + value <*> Value + key Key + collection The collection + callback Callback function + that The iterator's this binding + callback Callback function + results Results + + > Ox.serialMap(Ox.range(10), Ox.test.smIterator, function(r) { Ox.test(Ox.sum(r), 0); }) + undefined + @*/ + Ox.serialMap = function(collection, iterator, that, callback) { + asyncMap.apply(null, [Ox.serialForEach].concat(Ox.slice(arguments))); + }; + // FIXME: The above test with 10000 iterations blows the stack + +}()); diff --git a/source/Ox/js/Base.js b/source/Ox/js/Base.js deleted file mode 100644 index d0a315df..00000000 --- a/source/Ox/js/Base.js +++ /dev/null @@ -1,49 +0,0 @@ -/*@ -Ox.filter Filters a collection by a given condition - Unlike `Array.prototype.filter`, `Ox.filter` works for arrays, objects and - strings. - > Ox.filter([2, 1, 0], function(v, i) { return v == i; }) - [1] - > Ox.filter({a: 'c', b: 'b', c: 'a'}, function(v, k) { return v == k; }) - {b: 'b'} - > Ox.filter(' foo bar ', function(v) { return v != ' '; }) - 'foobar' -@*/ -export function filter(collection, iterator, that) { - var ret, type = Ox.typeOf(collection); - iterator = iterator || Ox.identity; - if (type == 'object' || type == 'storage') { - ret = {}; - Ox.forEach(collection, function(value, key) { - if (iterator.call(that, value, key, collection)) { - ret[key] = value; - } - }); - } else { - ret = Ox.slice(collection).filter(iterator, that); - if (type == 'string') { - ret = ret.join(''); - } - } - return ret; -}; - -/*@ -Ox.indexOf Returns the first index of a collection element that passes a test - > Ox.indexOf([1, 2, 3], function(val) { return val % 2 == 0; }) - 1 - > Ox.indexOf({a: 1, b: 2, c: 3}, function(val) { return val % 2 == 0; }) - 'b' - > Ox.indexOf('FooBar', function(val) { return val == val.toUpperCase(); }) - 0 - > Ox.indexOf([1, 2, 3], function(val) { return val == 0; }) - -1 -@*/ -export function indexOf(collection, test) { - var index = Ox.forEach(collection, function(value) { - return !test(value); // break if test succeeds - }); - return Ox.isObject(collection) ? Object.keys(collection)[index] || null - : index == collection.length ? -1 : index; -}; - diff --git a/source/Ox/js/Collection.js b/source/Ox/js/Collection.js index 01b4bf82..efcb41ac 100644 --- a/source/Ox/js/Collection.js +++ b/source/Ox/js/Collection.js @@ -1,22 +1,5 @@ 'use strict'; -import * as OxCore from './Core.js'; -import * as OxFunction from './Function.js'; -import * as OxConstants from './Constants.js'; -import * as OxType from './Type.js'; -import * as OxObject from './Object.js'; - -const Ox = {}; - -Object.assign(Ox, - OxCore, - OxConstants, - OxType, - OxObject, - OxFunction, -); - - /*@ Ox.avg Returns the average of an array's values, or an object's properties (collection) -> Average value @@ -28,8 +11,8 @@ Ox.avg Returns the average of an array's values, or an object's properties > Ox.avg('avg is 0.1') 0.1 @*/ -export function avg(collection) { - return Ox.sum(collection) / len(collection); +Ox.avg = function(collection) { + return Ox.sum(collection) / Ox.len(collection); }; /*@ @@ -45,13 +28,13 @@ Ox.clone Returns a (shallow or deep) copy of an array or object > (function() { var a = [[0, 1]], b = Ox.clone(a, true); a[0][0] = null; return b[0]; }()) [0, 1] @*/ -export function clone(collection, deep) { +Ox.clone = function(collection, deep) { var ret, type = Ox.typeOf(collection); if (type != 'array' && type != 'object') { ret = collection; } else if (deep) { ret = type == 'array' ? [] : {}; - forEach(collection, function(value, key) { + Ox.forEach(collection, function(value, key) { type = Ox.typeOf(value); ret[key] = type == 'array' || type == 'object' ? Ox.clone(value, true) : value; @@ -76,7 +59,7 @@ Ox.contains Tests if a collection contains a value > Ox.contains('foobar', 'bar') true @*/ -export function contains(collection, value) { +Ox.contains = function(collection, value) { var type = Ox.typeOf(collection); return ( type == 'nodelist' || type == 'object' @@ -101,9 +84,9 @@ Ox.count Counts the occurences of values in a collection > Ox.count('foo', 'x') 0 @*/ -export function count(collection, value) { +Ox.count = function(collection, value) { var count = {}; - forEach(collection, function(value) { + Ox.forEach(collection, function(value) { count[value] = (count[value] || 0) + 1; }); return value ? count[value] || 0 : count; @@ -128,13 +111,42 @@ Ox.every Tests if every element of a collection satisfies a given condition > Ox.every([true, true, true]) true @*/ -export function every(collection, iterator, that) { +Ox.every = function(collection, iterator, that) { iterator = iterator || Ox.identity; - return forEach(collection, function(value, key, collection) { + return Ox.forEach(collection, function(value, key, collection) { return !!iterator.call(that, value, key, collection); - }) == len(collection); + }) == Ox.len(collection); }; +/*@ +Ox.filter Filters a collection by a given condition + Unlike `Array.prototype.filter`, `Ox.filter` works for arrays, objects and + strings. + > Ox.filter([2, 1, 0], function(v, i) { return v == i; }) + [1] + > Ox.filter({a: 'c', b: 'b', c: 'a'}, function(v, k) { return v == k; }) + {b: 'b'} + > Ox.filter(' foo bar ', function(v) { return v != ' '; }) + 'foobar' +@*/ +Ox.filter = function(collection, iterator, that) { + var ret, type = Ox.typeOf(collection); + iterator = iterator || Ox.identity; + if (type == 'object' || type == 'storage') { + ret = {}; + Ox.forEach(collection, function(value, key) { + if (iterator.call(that, value, key, collection)) { + ret[key] = value; + } + }); + } else { + ret = Ox.slice(collection).filter(iterator, that); + if (type == 'string') { + ret = ret.join(''); + } + } + return ret; +}; /*@ Ox.forEach forEach loop @@ -159,7 +171,7 @@ Ox.forEach forEach loop > Ox.forEach({a: 'f', b: 'o', c: 'o'}, function(v, k) { return v != 'o' }); 1 @*/ -export function forEach(collection, iterator, that) { +Ox.forEach = function(collection, iterator, that) { var i = 0, key, type = Ox.typeOf(collection); if (type == 'object' || type == 'storage') { for (key in collection) { @@ -185,6 +197,25 @@ export function forEach(collection, iterator, that) { return i; }; +/*@ +Ox.indexOf Returns the first index of a collection element that passes a test + > Ox.indexOf([1, 2, 3], function(val) { return val % 2 == 0; }) + 1 + > Ox.indexOf({a: 1, b: 2, c: 3}, function(val) { return val % 2 == 0; }) + 'b' + > Ox.indexOf('FooBar', function(val) { return val == val.toUpperCase(); }) + 0 + > Ox.indexOf([1, 2, 3], function(val) { return val == 0; }) + -1 +@*/ +Ox.indexOf = function(collection, test) { + var index = Ox.forEach(collection, function(value) { + return !test(value); // break if test succeeds + }); + return Ox.isObject(collection) ? Object.keys(collection)[index] || null + : index == collection.length ? -1 : index; +}; + /*@ Ox.indicesOf Returns all indices of collection elements that pass a test > Ox.indicesOf([1, 2, 3], function(val) { return val % 2 == 1; }) @@ -196,9 +227,9 @@ Ox.indicesOf Returns all indices of collection elements that pass a test > Ox.indicesOf([1, 2, 3], function(val) { return val == 0; }) [] @*/ -export function indicesOf(collection, test) { +Ox.indicesOf = function(collection, test) { var ret = []; - forEach(collection, function(value, index) { + Ox.forEach(collection, function(value, index) { test(value) && ret.push(index); }); return ret; @@ -225,7 +256,7 @@ Ox.len Returns the length of an array, nodelist, object, storage or string > Ox.len(function(a, b, c) {}) undefined @*/ -export function len(collection) { +Ox.len = function(collection) { var ret, type = Ox.typeOf(collection); if ( type == 'arguments' || type == 'array' @@ -251,11 +282,11 @@ Ox.map Transforms the values of an array, object or string > Ox.map([,], function(v, i) { return i; }) [,] @*/ -export function map(collection, iterator, that) { +Ox.map = function(collection, iterator, that) { var ret, type = Ox.typeOf(collection); if (type == 'object' || type == 'storage') { ret = {}; - forEach(collection, function(value, key) { + Ox.forEach(collection, function(value, key) { ret[key] = iterator.call(that, value, key, collection); }); } else { @@ -278,7 +309,7 @@ Ox.max Returns the maximum value of a collection > Ox.max([]) -Infinity @*/ -export function max(collection) { +Ox.max = function(collection) { var ret, values = Ox.values(collection); if (values.length < Ox.STACK_LENGTH) { ret = Math.max.apply(null, values); @@ -301,7 +332,7 @@ Ox.min Returns the minimum value of a collection > Ox.min([]) Infinity @*/ -export function min(collection) { +Ox.min = function(collection) { var ret, values = Ox.values(collection); if (values.length < Ox.STACK_LENGTH) { ret = Math.min.apply(null, values); @@ -328,8 +359,8 @@ Ox.numberOf Returns the number of elements in a collection that pass a test > Ox.numberOf('foo', function(v, k, c) { return v == c[k - 1]; }) 1 @*/ -export function numberOf(collection, test) { - return len(Ox.filter(collection, test)); +Ox.numberOf = function(collection, test) { + return Ox.len(Ox.filter(collection, test)); }; /*@ @@ -350,7 +381,7 @@ Ox.remove Removes an element from an array or object and returns it > Ox.test.collection [['a', 'c'], {a: 0, c: 2}] @*/ -export function remove(collection, element) { +Ox.remove = function(collection, element) { var ret, key; if (Ox.isArray(collection)) { key = collection.indexOf(element); @@ -374,7 +405,7 @@ Ox.reverse Reverses an array or string > Ox.reverse('foobar') 'raboof' @*/ -export function reverse(collection) { +Ox.reverse = function(collection) { return Ox.isArray(collection) ? Ox.clone(collection).reverse() : collection.toString().split('').reverse().join(''); @@ -389,7 +420,7 @@ Ox.shuffle Randomizes the order of values within a collection > Ox.shuffle('123').split('').sort().join('') '123' @*/ -export function shuffle(collection) { +Ox.shuffle = function(collection) { var keys, ret, type = Ox.typeOf(collection), values; if (type == 'object' || type == 'storage') { keys = Object.keys(collection); @@ -412,6 +443,57 @@ export function shuffle(collection) { return ret; }; +/*@ +Ox.slice Alias for `Array.prototype.slice.call` + (collection[, start[, stop]]) -> Array + collection Array-like + start Start position + stop Stop position + > (function() { return Ox.slice(arguments); }(1, 2, 3)) + [1, 2, 3] + > Ox.slice('foo', 0, 1); + ['f'] + > Ox.slice({0: 'f', 1: 'o', 2: 'o', length: 3}, -2) + ['o', 'o'] +@*/ +// FIXME: remove toArray alias +Ox.slice = Ox.toArray = function(collection, start, stop) { + return Array.prototype.slice.call(collection, start, stop); +}; +// IE8 can't apply slice to NodeLists, returns an empty array if undefined is +// passed as stop and returns an array of null values if a string is passed as +// value. Firefox 3.6 returns an array of undefined values if a string is passed +// as value. +if ( + Ox.slice([0]).length == 0 + || Ox.slice('0')[0] === null + || Ox.slice('0')[0] === void 0 + || !(function() { + try { + return Ox.slice(document.getElementsByTagName('a')); + } catch (error) {} + }()) +) { + // FIXME: remove toArray alias + Ox.slice = Ox.toArray = function(collection, start, stop) { + var args = stop === void 0 ? [start] : [start, stop], + array = [], index, length, ret; + if (Ox.typeOf(collection) == 'string') { + collection = collection.split(''); + } + try { + ret = Array.prototype.slice.apply(collection, args); + } catch (error) { + length = collection.length; + for (index = 0; index < length; index++) { + array[index] = collection[index]; + } + ret = Array.prototype.slice.apply(array, args); + } + return ret; + }; +} + /*@ Ox.some Tests if one or more elements of a collection meet a given condition Unlike `Array.prototype.some`, `Ox.some` works for arrays, objects and @@ -425,11 +507,11 @@ Ox.some Tests if one or more elements of a collection meet a given condition > Ox.some([false, null, 0, '', void 0]) false @*/ -export function some(collection, iterator, that) { +Ox.some = function(collection, iterator, that) { iterator = iterator || Ox.identity; - return forEach(collection, function(value, key, collection) { + return Ox.forEach(collection, function(value, key, collection) { return !iterator.call(that, value, key, collection); - }) < len(collection); + }) < Ox.len(collection); }; /*@ @@ -447,10 +529,10 @@ Ox.sum Returns the sum of the values of a collection > Ox.sum('08', -2, 'foo') 6 @*/ -export function sum(collection) { +Ox.sum = function(collection) { var ret = 0; collection = arguments.length > 1 ? Ox.slice(arguments) : collection; - forEach(collection, function(value) { + Ox.forEach(collection, function(value) { value = +value; ret += isFinite(value) ? value : 0; }); @@ -459,7 +541,7 @@ export function sum(collection) { /* FIXME: do we need this kind of zip functionality? -export function arrayToObject(array, key) { +Ox.arrayToObject = function(array, key) { var ret = {}; array.forEach(function(v) { ret[v[key]] = v; @@ -467,9 +549,9 @@ export function arrayToObject(array, key) { return ret; }; -export function objectToArray(object, key) { +Ox.objectToArray = function(object, key) { var ret = []; - forEach(object, function(v, k) { + Ox.forEach(object, function(v, k) { ret.push(Ox.extend(v, key, k)); }); return ret; @@ -492,13 +574,13 @@ Ox.values Returns the values of a collection > Ox.values([1,,3]) [1,,3] @*/ -export function values(collection) { +Ox.values = function(collection) { var ret, type = Ox.typeOf(collection); if (type == 'array' || type == 'nodelist') { ret = Ox.slice(collection); } else if (type == 'object' || type == 'storage') { ret = []; - forEach(collection, function(value) { + Ox.forEach(collection, function(value) { ret.push(value); }); } else if (type == 'string') { @@ -531,9 +613,9 @@ Ox.walk Iterates over a nested data structure > Ox.test.array [['a'], ['b', 'c'], ['b', 'd']] @*/ -export function walk(collection, iterator, that, keys) { +Ox.walk = function(collection, iterator, that, keys) { keys = keys || []; - forEach(collection, function(value, key) { + Ox.forEach(collection, function(value, key) { var keys_ = keys.concat(key); iterator.call(that, value, keys_, collection); Ox.walk(collection[key], iterator, that, keys_); diff --git a/source/Ox/js/Color.js b/source/Ox/js/Color.js index c58b30cf..ce01d81d 100644 --- a/source/Ox/js/Color.js +++ b/source/Ox/js/Color.js @@ -1,17 +1,5 @@ 'use strict'; -import * as OxObject from './Object.js'; -import * as OxConstants from './Constants.js'; -import * as OxMath from './Math.js'; - -const Ox = {}; - -Object.assign(Ox, - OxObject, - OxConstants, - OxMath -); - /*@ Ox.hsl Takes RGB values and returns HSL values (rgb) <[n]> HSL values @@ -27,7 +15,7 @@ Ox.hsl Takes RGB values and returns HSL values > Ox.hsl(0, 255, 0) [120, 1, 0.5] @*/ -export function hsl(rgb) { +Ox.hsl = function(rgb) { var hsl = [0, 0, 0], max, min; if (arguments.length == 3) { rgb = Ox.slice(arguments); @@ -74,7 +62,7 @@ Ox.rgb Takes HSL values and returns RGB values [0, 255, 0] @*/ -export function rgb(hsl) { +Ox.rgb = function(hsl) { var rgb = [0, 0, 0], v1, v2, v3; if (arguments.length == 3) { hsl = Ox.slice(arguments); @@ -118,7 +106,7 @@ Ox.toHex Format RGB array as hex value > Ox.toHex([192, 128, 64]) 'C08040' @*/ -export function toHex(rgb) { +Ox.toHex = function(rgb) { return rgb.map(function(value) { return Ox.pad(value.toString(16).toUpperCase(), 'left', 2, '0'); }).join(''); @@ -129,7 +117,7 @@ Ox.toRGB Format hex value as RGB array > Ox.toRGB('C08040') [192, 128, 64] @*/ -export function toRGB(hex) { +Ox.toRGB = function(hex) { return Ox.range(3).map(function(index) { return parseInt(hex.substr(index * 2, 2), 16); }); diff --git a/source/Ox/js/Constants.js b/source/Ox/js/Constants.js index a0e0004b..1fe2d45b 100644 --- a/source/Ox/js/Constants.js +++ b/source/Ox/js/Constants.js @@ -1,38 +1,30 @@ 'use strict'; -import * as OxMath from './Math.js'; - -const Ox = {}; - -Object.assign(Ox, - OxMath -); - //@ Ox.AMPM <[s]> ['AM', 'PM'] -export const AMPM = ['AM', 'PM']; +Ox.AMPM = ['AM', 'PM']; //@ Ox.BASE_32_ALIASES Base 32 aliases -export const BASE_32_ALIASES = {'I': '1', 'L': '1', 'O': '0', 'U': 'V'}; +Ox.BASE_32_ALIASES = {'I': '1', 'L': '1', 'O': '0', 'U': 'V'}, //@ Ox.BASE_32_DIGITS Base 32 digits -export const BASE_32_DIGITS = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; +Ox.BASE_32_DIGITS = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; //@ Ox.BCAD <[s]> ['BC', 'AD'] -export const BCAD = ['BC', 'AD']; +Ox.BCAD = ['BC', 'AD']; /*@ Ox.EARTH_RADIUS Radius of the earth in meters See http://en.wikipedia.org/wiki/WGS-84 */ -export const EARTH_RADIUS = 6378137; +Ox.EARTH_RADIUS = 6378137; //@ Ox.EARTH_CIRCUMFERENCE Circumference of the earth in meters -export const EARTH_CIRCUMFERENCE = 2 * Math.PI * EARTH_RADIUS; +Ox.EARTH_CIRCUMFERENCE = 2 * Math.PI * Ox.EARTH_RADIUS; //@ Ox.EARTH_SURFACE Surface of the earth in square meters -export const EARTH_SURFACE = 4 * Math.PI * Math.pow(EARTH_RADIUS, 2); +Ox.EARTH_SURFACE = 4 * Math.PI * Math.pow(Ox.EARTH_RADIUS, 2); //@ Ox.HTML_ENTITIES HTML entities for ... (FIXME) -export const HTML_ENTITIES = { +Ox.HTML_ENTITIES = { '"': '"', '&': '&', "'": ''', '<': '<', '>': '>' }; //@ Ox.KEYS Names for key codes // The dot notation ('0.numpad') allows for namespaced events ('key_0.numpad'), // so that binding to 'key_0' will catch both 'key_0' and 'key_0.numpad'. -export const KEYS = { +Ox.KEYS = { 0: 'section', 8: 'backspace', 9: 'tab', 12: 'clear', 13: 'enter', 16: 'shift', 17: 'control', 18: 'alt', 20: 'capslock', 27: 'escape', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', @@ -63,9 +55,9 @@ export const KEYS = { // see dojo, for ex. }; //@ Ox.LOCALE Default locale -export const LOCALE = 'en'; +Ox.LOCALE = 'en'; //@ Ox.LOCALE_NAMES Locale names -export const LOCALE_NAMES = { +Ox.LOCALE_NAMES = { 'ar': 'العربية', 'de': 'Deutsch', 'el': 'Ελληνικά', @@ -75,56 +67,51 @@ export const LOCALE_NAMES = { 'tr': 'Türkçe' }; //@ Ox.LOCALES Locales per module -export const LOCALES = {}; +Ox.LOCALES = {}; //@ Ox.MAX_LATITUDE Maximum latitude of a Mercator projection -export const MAX_LATITUDE = Ox.deg(Math.atan(Ox.sinh(Math.PI))); +Ox.MAX_LATITUDE = Ox.deg(Math.atan(Ox.sinh(Math.PI))); //@ Ox.MIN_LATITUDE Minimum latitude of a Mercator projection -export const MIN_LATITUDE = -Ox.MAX_LATITUDE; +Ox.MIN_LATITUDE = -Ox.MAX_LATITUDE; //@ Ox.MODIFIER_KEYS Names for modifier keys // meta comes last so that one can differentiate between // alt_control_shift_meta.left and alt_control_shift_meta.right -export const MODIFIER_KEYS = { +Ox.MODIFIER_KEYS = { altKey: 'alt', // Mac: option ctrlKey: 'control', shiftKey: 'shift', metaKey: 'meta' // Mac: command }; //@ Ox.MONTHS <[s]> Names of months -export const MONTHS = [ +Ox.MONTHS = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ]; //@ Ox.SHORT_MONTHS <[s]> Short names of months -export const SHORT_MONTHS = MONTHS.map(function(val) { +Ox.SHORT_MONTHS = Ox.MONTHS.map(function(val) { return val.slice(0, 3); }); //@ Ox.PATH Path of Ox.js -export const PATH = (function() { +Ox.PATH = (function() { // IE8 can't apply slice to NodeLists, see Ox.slice - var index, regexp = /Ox\.js(\?.+|)$/, scripts, src; - try { - scripts = document.getElementsByTagName('script') - } catch(e) { - scripts = '' - } + var index, regexp = /Ox\.js(\?.+|)$/, + scripts = document.getElementsByTagName('script'), src; for (index = scripts.length - 1; index >= 0; index--) { src = scripts[index].src; if (regexp.test(src)) { return src.replace(regexp, ''); } } - return '' }()); //@ Ox.MODE Mode ('dev' or 'min') -export const MODE = PATH.slice(0, -1).split('/').pop(); +Ox.MODE = Ox.PATH.slice(0, -1).split('/').pop(); //@ Ox.PREFIXES <[str]> `['', 'K', 'M', 'G', 'T', 'P']` -export const PREFIXES = ['', 'K', 'M', 'G', 'T', 'P']; +Ox.PREFIXES = ['', 'K', 'M', 'G', 'T', 'P']; //@ Ox.SEASONS <[s]> Names of the seasons of the year -export const SEASONS = ['Winter', 'Spring', 'Summer', 'Fall']; +Ox.SEASONS = ['Winter', 'Spring', 'Summer', 'Fall']; //@ Ox.STACK_SIZE Maximum number of arguments -export const STACK_SIZE = 65536; +Ox.STACK_SIZE = 65536; //@ Ox.SYMBOLS Unicode characters for symbols -export const SYMBOLS = { +Ox.SYMBOLS = { dollar: '\u0024', cent: '\u00A2', pound: '\u00A3', currency: '\u00A4', yen: '\u00A5', bullet: '\u2022', ellipsis: '\u2026', permille: '\u2030', colon: '\u20A1', cruzeiro: '\u20A2', franc: '\u20A3', lira: '\u20A4', @@ -151,12 +138,12 @@ export const SYMBOLS = { click: '\uF803', apple: '\uF8FF' }; //@ Ox.VERSION OxJS version number -export const VERSION = '0.1'; +Ox.VERSION = '0.1'; //@ Ox.WEEKDAYS <[s]> Names of weekdays -export const WEEKDAYS = [ +Ox.WEEKDAYS = [ 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' ]; //@ Ox.SHORT_WEEKDAYS <[s]> Short names of weekdays -export const SHORT_WEEKDAYS = WEEKDAYS.map(function(val) { +Ox.SHORT_WEEKDAYS = Ox.WEEKDAYS.map(function(val) { return val.slice(0, 3); }); diff --git a/source/Ox/js/Core.js b/source/Ox/js/Core.js index d278ae49..8a8e402c 100644 --- a/source/Ox/js/Core.js +++ b/source/Ox/js/Core.js @@ -2,23 +2,15 @@ 'use strict'; -import * as OxType from './Type.js'; -import * as OxCollection from './Collection.js'; -import * as OxObject from './Object.js'; -import * as OxDOM from './DOM.js'; -import * as OxString from './String.js'; -import * as OxRequest from './Request.js'; - -const Ox = {}; - -Object.assign(Ox, - OxType, - OxCollection, - OxObject, - OxDOM, - OxString, - OxRequest, -); +/*@ +Ox The `Ox` object + See `Ox.wrap` for details. + (value) -> wrapped value + value <*> Any value +@*/ +this.Ox = function(value) { + return Ox.wrap(value); +}; /*@ Ox.load Loads OxJS and, optionally, one or more modules @@ -60,7 +52,7 @@ Ox.load Loads OxJS and, optionally, one or more modules callback Callback function success If true, all modules have been loaded successfully @*/ -export function load() { +Ox.load = function() { var callback = arguments[arguments.length - 1], length, loaded = 0, localeFiles = [], modules = {}, succeeded = 0, type = Ox.typeOf(arguments[0]); @@ -84,18 +76,20 @@ export function load() { if (!length) { callback(true); } else { - Ox.forEach(modules, async function(options, module) { - console.log("load module!", module, options) - // Ox.PATH + module + '/index.js?' + Ox.VERSION, - const obj = await import('../../' + module + '/index.js?' + Ox.VERSION); - Ox.load[module](options, function(success) { - succeeded += success; - if (++loaded == length) { - Ox.setLocale(Ox.LOCALE, function() { - callback(succeeded == length); + Ox.forEach(modules, function(options, module) { + Ox.getFile( + Ox.PATH + module + '/' + module + '.js?' + Ox.VERSION, + function() { + Ox.load[module](options, function(success) { + succeeded += success; + if (++loaded == length) { + Ox.setLocale(Ox.LOCALE, function() { + callback(succeeded == length); + }); + } }); } - }); + ); }); } }); @@ -116,7 +110,7 @@ Ox.localStorage localStorage wrapper > Ox.test.localStorage.delete('foo')() {} @*/ -export function localStorage(namespace) { +Ox.localStorage = function(namespace) { var localStorage; try { // this will fail if third party cookies/storage is not allowed @@ -169,7 +163,7 @@ export function localStorage(namespace) { Ox.Log Logging module @*/ Ox.Log = (function() { - var storage = localStorage('Ox'), + var storage = Ox.localStorage('Ox'), log = storage('log') || {filter: [], filterEnabled: true}, that = function() { var ret; @@ -245,7 +239,7 @@ Ox.loop For-loop, functional-style > Ox.loop(0, 3, 2, function() {}) 4 @*/ -export function loop() { +Ox.loop = function() { var length = arguments.length, start = length > 2 ? arguments[0] : 0, stop = arguments[length > 2 ? 1 : 0], @@ -269,7 +263,7 @@ Ox.print Prints its arguments to the console > Ox.print('foo', 'bar').split(' ').slice(1).join(' ') 'foo bar' @*/ -export function print() { +Ox.print = function() { var args = Ox.slice(arguments), date = new Date(); args.unshift( date.toString().split(' ')[4] + '.' + (+date).toString().slice(-3) @@ -282,7 +276,7 @@ export function print() { Ox.trace Prints its arguments to the console, followed by a stack trace (arg, ...) -> String @*/ -export function trace() { +Ox.trace = function() { var args = Ox.slice(arguments); try { throw new Error(); @@ -302,10 +296,12 @@ Ox.uid Returns a unique id > Ox.uid() != Ox.uid() true @*/ -var _uid = 0; -export function uid() { - return ++_uid; -} +Ox.uid = (function() { + var uid = 0; + return function() { + return ++uid; + }; +}()); /*@ Ox.wrap Wraps a value so that one can directly call any Ox function on it @@ -321,7 +317,7 @@ Ox.wrap Wraps a value so that one can directly call any Ox function on it > Ox.wrap('foobar').value() 'foobar' @*/ -export function wrap(value, chained) { +Ox.wrap = function(value, chained) { // somewhat inspired by underscore.js var wrapper = { chain: function() { @@ -345,55 +341,3 @@ export function wrap(value, chained) { }); return wrapper; }; - -/*@ -Ox.slice Alias for `Array.prototype.slice.call` - (collection[, start[, stop]]) -> Array - collection Array-like - start Start position - stop Stop position - > (function() { return Ox.slice(arguments); }(1, 2, 3)) - [1, 2, 3] - > Ox.slice('foo', 0, 1); - ['f'] - > Ox.slice({0: 'f', 1: 'o', 2: 'o', length: 3}, -2) - ['o', 'o'] -@*/ -// FIXME: remove toArray alias -export function slice(collection, start, stop) { - return Array.prototype.slice.call(collection, start, stop); -}; -// IE8 can't apply slice to NodeLists, returns an empty array if undefined is -// passed as stop and returns an array of null values if a string is passed as -// value. Firefox 3.6 returns an array of undefined values if a string is passed -// as value. -if ( - slice([0]).length == 0 - || slice('0')[0] === null - || slice('0')[0] === void 0 - || !(function() { - try { - return Ox.slice(document.getElementsByTagName('a')); - } catch (error) {} - }()) -) { - // FIXME: remove toArray alias - slice = function(collection, start, stop) { - var args = stop === void 0 ? [start] : [start, stop], - array = [], index, length, ret; - if (Ox.typeOf(collection) == 'string') { - collection = collection.split(''); - } - try { - ret = Array.prototype.slice.apply(collection, args); - } catch (error) { - length = collection.length; - for (index = 0; index < length; index++) { - array[index] = collection[index]; - } - ret = Array.prototype.slice.apply(array, args); - } - return ret; - }; -} - diff --git a/source/Ox/js/DOM.js b/source/Ox/js/DOM.js index ed270c20..71ef21cc 100644 --- a/source/Ox/js/DOM.js +++ b/source/Ox/js/DOM.js @@ -1,27 +1,5 @@ 'use strict'; -import * as OxCore from './Core.js'; -import * as OxObject from './Object.js'; -import * as OxConstants from './Constants.js'; -import * as OxMath from './Math.js'; -import * as OxType from './Type.js'; -import * as OxArray from './Array.js'; -import * as OxCollection from './Collection.js'; -import * as OxString from './String.js'; - -const Ox = {}; - -Object.assign(Ox, - OxCore, - OxObject, - OxConstants, - OxMath, - OxType, - OxArray, - OxCollection, - OxString, -); - /*@ Ox.$ Generic HTML element, mimics jQuery value tagname, selector, html element, `window`, or `document` @@ -53,7 +31,7 @@ Ox.$ Generic HTML element, mimics jQuery > Ox.$('').val('red').val() 'red' @*/ -export function element(value) { +Ox.$ = Ox.element = function $(value) { var elements = Ox.isArray(value) ? value // array of elements : Ox.isNodeList(value) ? Ox.slice(value) // nodelist @@ -818,8 +796,6 @@ export function element(value) { }; -export const $ = element; - /*@ Ox.canvas Generic canvas object Returns an object with the properties: `canvas`, `context`, `data` and @@ -830,7 +806,7 @@ Ox.canvas Generic canvas object height Height in px image Image object @*/ -export function canvas() { +Ox.canvas = function() { var c = {}, isImage = arguments.length == 1, image = isImage ? arguments[0] : { width: arguments[0], height: arguments[1] @@ -845,24 +821,13 @@ export function canvas() { return c; }; - -var callbacks = []; /*@ Ox.documentReady Calls a callback function once the DOM is ready (callback) -> If true, the document was ready callback Callback function @*/ -export function documentReady(callback) { - if (document.readyState == 'complete') { - callback(); - return true; - } else { - callbacks.push(callback); - return false; - } -} - -if (typeof document !== 'undefined') { +Ox.documentReady = (function() { + var callbacks = []; document.onreadystatechange = window.onload = function() { if (document.readyState == 'complete') { callbacks.forEach(function(callback) { @@ -871,4 +836,13 @@ if (typeof document !== 'undefined') { document.onreadystatechange = window.onload = null; } }; -} + return function(callback) { + if (document.readyState == 'complete') { + callback(); + return true; + } else { + callbacks.push(callback); + return false; + } + }; +}()); diff --git a/source/Ox/js/Date.js b/source/Ox/js/Date.js index 62c5511b..74668b2e 100644 --- a/source/Ox/js/Date.js +++ b/source/Ox/js/Date.js @@ -1,17 +1,5 @@ 'use strict'; -import * as OxObject from './Object.js'; -import * as OxConstants from './Constants.js'; -import * as OxMath from './Math.js'; - -const Ox = {}; - -Object.assign(Ox, - OxObject, - OxConstants, - OxMath -); - //@ Ox.getDate Get the day of a date, optionally UTC // see Ox.setSeconds for source code @@ -33,7 +21,7 @@ Ox.getDateInWeek Get the date that falls on a given weekday in the same week "Monday, December 27, 1999" @*/ // fixme: why is this Monday first? shouldn't it then be "getDateInISOWeek"?? -export function getDateInWeek(date, weekday, utc) { +Ox.getDateInWeek = function(date, weekday, utc) { date = Ox.makeDate(date); var sourceWeekday = Ox.getISODay(date, utc), targetWeekday = Ox.isNumber(weekday) ? weekday @@ -62,7 +50,7 @@ Ox.getDayOfTheYear Get the day of the year for a given date > Ox.getDayOfTheYear(new Date("12/31/2004")) 366 @*/ -export function getDayOfTheYear(date, utc) { +Ox.getDayOfTheYear = function(date, utc) { date = Ox.makeDate(date); var month = Ox.getMonth(date, utc), year = Ox.getFullYear(date, utc); @@ -80,7 +68,7 @@ Ox.getDaysInMonth Get the number of days in a given month > Ox.getDaysInMonth(new Date('01/01/2004'), "February") 29 @*/ -export function getDaysInMonth(year, month) { +Ox.getDaysInMonth = function(year, month) { year = Ox.makeYear(year); month = Ox.isNumber(month) ? month : Ox.indexOf(Ox.MONTHS, function(v) { @@ -99,7 +87,7 @@ Ox.getDaysInYear Get the number of days in a given year > Ox.getDaysInYear(new Date('01/01/2004')) 366 @*/ -export function getDaysInYear(year, utc) { +Ox.getDaysInYear = function(year, utc) { return 365 + Ox.isLeapYear(Ox.makeYear(year, utc)); }; @@ -109,7 +97,7 @@ Ox.getFirstDayOfTheYear Get the weekday of the first day of a given year > Ox.getFirstDayOfTheYear(new Date('01/01/2000')) 6 @*/ -export function getFirstDayOfTheYear(date, utc) { +Ox.getFirstDayOfTheYear = function(date, utc) { date = Ox.makeDate(date); date = Ox.setMonth(date, 0, utc); date = Ox.setDate(date, 1, utc); @@ -126,7 +114,7 @@ Ox.getISODate Get the ISO date string for a given date > Ox.getISODate(new Date('01/01/2000')) '2000-01-01T00:00:00Z' @*/ -export function getISODate(date, utc) { +Ox.getISODate = function(date, utc) { return Ox.formatDate(Ox.makeDate(date), '%FT%TZ', utc); }; @@ -140,7 +128,7 @@ Ox.getISODay Get the ISO weekday of a given date > Ox.getISODay(new Date('01/03/2000')) 1 @*/ -export function getISODay(date, utc) { +Ox.getISODay = function(date, utc) { return Ox.getDay(Ox.makeDate(date), utc) || 7; }; @@ -155,7 +143,7 @@ Ox.getISOWeek Get the ISO week of a given date 1 @*/ -export function getISOWeek(date, utc) { +Ox.getISOWeek = function(date, utc) { date = Ox.makeDate(date); // set date to Thursday of the same week return Math.floor((Ox.getDayOfTheYear(Ox.setDate( @@ -174,7 +162,7 @@ Ox.getISOYear Get the ISO year of a given date 2000 @*/ -export function getISOYear(date, utc) { +Ox.getISOYear = function(date, utc) { date = Ox.makeDate(date); // set date to Thursday of the same week return Ox.getFullYear(Ox.setDate( @@ -192,7 +180,7 @@ export function getISOYear(date, utc) { // see Ox.setSeconds for source code //@ Ox.getTime Alias for `+new Date()` -export function getTime(utc) { +Ox.getTime = function(utc) { return +new Date() - (utc ? Ox.getTimezoneOffset() : 0); }; @@ -201,7 +189,7 @@ Ox.getTimezoneOffset Get the local time zone offset in milliseconds ([date]) -> Offset in milliseconds date Return offset at this date (if undefined, return current offset) @*/ -export function getTimezoneOffset(date) { +Ox.getTimezoneOffset = function(date) { return Ox.makeDate(date).getTimezoneOffset() * 60000; }; @@ -213,7 +201,7 @@ Ox.getTimezoneOffsetString Get the local time zone offset as a string > Ox.getTimezoneOffsetString(new Date('01/01/2000')).length 5 @*/ -export function getTimezoneOffsetString(date) { +Ox.getTimezoneOffsetString = function(date) { var offset = Ox.makeDate(date).getTimezoneOffset(); return (offset <= 0 ? '+' : '-') + Ox.pad(Math.floor(Math.abs(offset) / 60), 2) @@ -230,7 +218,7 @@ Ox.getWeek Get the week of a given day > Ox.getWeek(new Date('01/03/2000')) 1 @*/ -export function getWeek(date, utc) { +Ox.getWeek = function(date, utc) { date = Ox.makeDate(date); return Math.floor((Ox.getDayOfTheYear(date, utc) + Ox.getFirstDayOfTheYear(date, utc) - 1) / 7); @@ -245,7 +233,7 @@ Ox.isLeapYear Returns true if a given year is a leap year > Ox.isLeapYear(new Date('01/01/2004')) true @*/ -export function isLeapYear(year, utc) { +Ox.isLeapYear = function(year, utc) { year = Ox.makeYear(year, utc); return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); }; @@ -267,7 +255,7 @@ Ox.makeDate Takes a date, number or string, returns a date > Ox.formatDate(Ox.makeDate(Ox.parseDate('-50')), '%Y') '-50' @*/ -export function makeDate(date) { +Ox.makeDate = function(date) { // Safari 4/5 (<= 534.59.10) doesn't parse YYYY, YYYY-MM or YYYY-MM-DD if (Ox.isString(date) && Ox.isInvalidDate(new Date(date))) { if (/^\d{4}$/.test(date)) { @@ -292,7 +280,7 @@ Ox.makeYear Takes a date, number or string, returns a year > Ox.makeYear('1970') 1970 @*/ -export function makeYear(date, utc) { +Ox.makeYear = function(date, utc) { return Ox.isDate(date) ? Ox.getFullYear(date, utc) : parseInt(date, 10); }; @@ -307,7 +295,7 @@ Ox.parseDate Takes a string ('YYYY-MM-DD HH:MM:SS.MMM') and returns a date > Ox.parseDate('50', true).getUTCFullYear() 50 @*/ -export function parseDate(string, utc) { +Ox.parseDate = function(string, utc) { var date, defaults = [, 1, 1, 0, 0, 0, 0], values = /(-?\d+)-?(\d+)?-?(\d+)? ?(\d+)?:?(\d+)?:?(\d+)?\.?(\d+)?/ @@ -332,7 +320,7 @@ export function parseDate(string, utc) { }; /* -export function parseDateRange(start, end, utc) { +Ox.parseDateRange = function(start, end, utc) { var dates = [ Ox.parseDate(start, utc), Ox.parseDate(end, utc) diff --git a/source/Ox/js/Encoding.js b/source/Ox/js/Encoding.js index 26afd836..974108ac 100644 --- a/source/Ox/js/Encoding.js +++ b/source/Ox/js/Encoding.js @@ -1,17 +1,5 @@ 'use strict'; -import * as OxObject from './Object.js'; -import * as OxConstants from './Constants.js'; -import * as OxMath from './Math.js'; - -const Ox = {}; - -Object.assign(Ox, - OxObject, - OxConstants, - OxMath -); - /*@ Ox.encodeBase26 Encode a number as bijective base26 See @@ -27,7 +15,7 @@ Ox.encodeBase26 Encode a number as bijective base26 > Ox.encodeBase26(4461) 'FOO' @*/ -export function encodeBase26(number) { +Ox.encodeBase26 = function(number) { var string = ''; while (number) { string = String.fromCharCode(65 + (number - 1) % 26) + string; @@ -43,7 +31,7 @@ Ox.decodeBase26 Decodes a bijective base26-encoded number > Ox.decodeBase26('foo') 4461 @*/ -export function decodeBase26(string) { +Ox.decodeBase26 = function(string) { return string.toUpperCase().split('').reverse().reduce(function(p, c, i) { return p + (c.charCodeAt(0) - 64) * Math.pow(26, i); }, 0); @@ -57,7 +45,7 @@ Ox.encodeBase32 Encode a number as base32 > Ox.encodeBase32(33819) '110V' @*/ -export function encodeBase32(number) { +Ox.encodeBase32 = function(number) { return Ox.map(number.toString(32), function(char) { return Ox.BASE_32_DIGITS[parseInt(char, 32)]; }); @@ -73,7 +61,7 @@ Ox.decodeBase32 Decodes a base32-encoded number > Ox.decodeBase32('?').toString() 'NaN' @*/ -export function decodeBase32(string) { +Ox.decodeBase32 = function(string) { return parseInt(Ox.map(string.toUpperCase(), function(char) { var index = Ox.BASE_32_DIGITS.indexOf( Ox.BASE_32_ALIASES[char] || char @@ -87,7 +75,7 @@ Ox.encodeBase64 Encode a number as base64 > Ox.encodeBase64(32394) 'foo' @*/ -export function encodeBase64(number) { +Ox.encodeBase64 = function(number) { return btoa(Ox.encodeBase256(number)).replace(/=/g, ''); }; @@ -96,7 +84,7 @@ Ox.decodeBase64 Decodes a base64-encoded number > Ox.decodeBase64('foo') 32394 @*/ -export function decodeBase64(string) { +Ox.decodeBase64 = function(string) { return Ox.decodeBase256(atob(string)); }; @@ -105,7 +93,7 @@ Ox.encodeBase128 Encode a number as base128 > Ox.encodeBase128(1685487) 'foo' @*/ -export function encodeBase128(number) { +Ox.encodeBase128 = function(number) { var string = ''; while (number) { string = Ox.char(number & 127) + string; @@ -119,7 +107,7 @@ Ox.decodeBase128 Decode a base128-encoded number > Ox.decodeBase128('foo') 1685487 @*/ -export function decodeBase128(string) { +Ox.decodeBase128 = function(string) { return string.split('').reverse().reduce(function(p, c, i) { return p + (c.charCodeAt(0) << i * 7); }, 0); @@ -130,7 +118,7 @@ Ox.encodeBase256 Encode a number as base256 > Ox.encodeBase256(6713199) 'foo' @*/ -export function encodeBase256(number) { +Ox.encodeBase256 = function(number) { var string = ''; while (number) { string = Ox.char(number & 255) + string; @@ -144,7 +132,7 @@ Ox.decodeBase256 Decode a base256-encoded number > Ox.decodeBase256('foo') 6713199 @*/ -export function decodeBase256(string) { +Ox.decodeBase256 = function(string) { return string.split('').reverse().reduce(function(p, c, i) { return p + (c.charCodeAt(0) << i * 8); }, 0); @@ -161,7 +149,7 @@ Ox.encodeDeflate Encodes a string, using deflate > Ox.decodeDeflate(Ox.encodeDeflate('foo'), function(str) { Ox.test(str, 'foo'); }) undefined @*/ -export function encodeDeflate(string, callback) { +Ox.encodeDeflate = function(string, callback) { // Make sure we can encode the full unicode range of characters. string = Ox.encodeUTF8(string); // We can only safely write to RGB, so we need 1 pixel for 3 bytes. @@ -210,7 +198,7 @@ Ox.decodeDeflate Decodes an deflate-encoded string str The decoded string @*/ -export function decodeDeflate(string, callback) { +Ox.decodeDeflate = function(string, callback) { var image = new Image(), // PNG file signature and IHDR chunk data = '\u0089PNG\r\n\u001A\n\u0000\u0000\u0000\u000DIHDR' @@ -304,7 +292,7 @@ Ox.encodeUTF8 Encodes a string as UTF-8 > Ox.encodeUTF8("¥€$") "\u00C2\u00A5\u00E2\u0082\u00AC\u0024" @*/ -export function encodeUTF8(string) { +Ox.encodeUTF8 = function(string) { return Ox.map(string, function(char) { var code = char.charCodeAt(0), string = ''; @@ -332,7 +320,7 @@ Ox.decodeUTF8 Decodes an UTF-8-encoded string > Ox.decodeUTF8('\u00C2\u00A5\u00E2\u0082\u00AC\u0024') '¥€$' @*/ -export function decodeUTF8(string) { +Ox.decodeUTF8 = function(string) { var code, i = 0, length = string.length, ret = ''; function error(byte, position) { throw new RangeError( diff --git a/source/Ox/js/Format.js b/source/Ox/js/Format.js index 82c2292d..6870c75b 100644 --- a/source/Ox/js/Format.js +++ b/source/Ox/js/Format.js @@ -1,17 +1,5 @@ 'use strict'; -import * as OxObject from './Object.js'; -import * as OxConstants from './Constants.js'; -import * as OxMath from './Math.js'; - -const Ox = {}; - -Object.assign(Ox, - OxObject, - OxConstants, - OxMath -); - /*@ Ox.formatArea Formats a number of meters as square meters or kilometers > Ox.formatArea(1000) @@ -20,7 +8,7 @@ Ox.formatArea Formats a number of meters as square meters or kilometers '1 km\u00B2' @*/ -export function formatArea(number, decimals) { +Ox.formatArea = function(number, decimals) { var k = number >= 1000000 ? 'k' : ''; decimals = Ox.isUndefined(decimals) ? 8 : decimals; return Ox.formatNumber( @@ -37,7 +25,7 @@ Ox.formatCount Returns a string like "2 items", "1 item" or "no items". > Ox.formatCount(1000, 'city', 'cities') '1,000 cities' @*/ -export function formatCount(number, singular, plural) { +Ox.formatCount = function(number, singular, plural) { plural = (plural || singular + 's') + (number === 2 ? '{2}' : ''); return (number === 0 ? Ox._('no') : Ox.formatNumber(number)) + ' ' + Ox._(number === 1 ? singular : plural); @@ -48,7 +36,7 @@ Ox.formatCurrency Formats a number with a currency symbol > Ox.formatCurrency(1000, '$', 2) '$1,000.00' @*/ -export function formatCurrency(number, string, decimals) { +Ox.formatCurrency = function(number, string, decimals) { return string + Ox.formatNumber(number, decimals); }; @@ -409,7 +397,7 @@ Ox.formatDateRange Formats a date range as a string > Ox.formatDateRange('-50-01-01 00:00:00', '-50-01-01 23:59:59') 'Sun, Jan 1, 50 BC, 00:00:00 - 23:59:59' @*/ -export function formatDateRange(start, end, utc) { +Ox.formatDateRange = function(start, end, utc) { end = end || Ox.formatDate(new Date(), '%Y-%m-%d'); var isOneUnit = false, range = [start, end], @@ -499,7 +487,7 @@ Ox.formatDateRangeDuration Formats the duration of a date range as a string > Ox.formatDateRangeDuration('2000-02', '2000-03', true) '1 month' @*/ -export function formatDateRangeDuration(start, end, utc) { +Ox.formatDateRangeDuration = function(start, end, utc) { end = end || Ox.formatDate(new Date(), '%Y-%m-%d'); var date = Ox.parseDate(start, utc), dates = [start, end].map(function(string) { @@ -547,7 +535,7 @@ Ox.formatDegrees Formats degrees as D°MM'SS" > Ox.formatDegrees(-111.11, 'lng') "111°06'36\"W" @*/ -export function formatDegrees(degrees, mode) { +Ox.formatDegrees = function(degrees, mode) { var days = 0, seconds = Math.round(Math.abs(degrees) * 3600), sign = degrees < 0 ? '-' : '', @@ -570,13 +558,11 @@ Ox.formatDimensions Formats valus as dimension > Ox.formatDimensions([1920, 1080], 'px') "1,920 × 1,080 px" @*/ -export function formatDimensions(array, string) { +Ox.formatDimensions = Ox.formatResolution = function(array, string) { return array.map(function(value) { return Ox.formatNumber(value); }).join(' × ') + (string ? ' ' + string : ''); }; -export const formatResolution = formatDimensions; - /*@ Ox.formatDuration Formats a duration as a string @@ -609,7 +595,7 @@ Ox.formatDuration Formats a duration as a string > Ox.formatDuration(0, 'long') '' @*/ -export function formatDuration(seconds/*, decimals, format*/) { +Ox.formatDuration = function(seconds/*, decimals, format*/) { var last = Ox.last(arguments), format = last == 'short' || last == 'long' ? last : 'none', decimals = Ox.isNumber(arguments[1]) ? arguments[1] : 0, @@ -663,7 +649,7 @@ Ox.formatISBN Formats a string as an ISBN of a given length (10 or 13) > Ox.formatISBN('978-0-306-40615-7', 10) '0306406152' @*/ -export function formatISBN(isbn, length, dashes) { +Ox.formatISBN = function(isbn, length, dashes) { var ret = ''; function getCheckDigit(isbn) { var mod = isbn.length == 10 ? 11 : 10 @@ -711,7 +697,7 @@ Ox.formatNumber Formats a number with thousands separators > Ox.formatNumber(666666.666) "666,667" @*/ -export function formatNumber(number, decimals) { +Ox.formatNumber = function(number, decimals) { var array = [], abs = Math.abs(number), split = abs.toFixed(decimals).split('.'); @@ -740,7 +726,7 @@ Ox.formatOrdinal Formats a number as an ordinal > Ox.formatOrdinal(13) "13th" @*/ -export function formatOrdinal(number) { +Ox.formatOrdinal = function(number) { var string = Ox.formatNumber(number), length = string.length, last = string[length - 1], @@ -765,7 +751,7 @@ Ox.formatPercent Formats the relation of two numbers as a percentage > Ox.formatPercent(1, 1000, 2) "0.10%" @*/ -export function formatPercent(number, total, decimals) { +Ox.formatPercent = function(number, total, decimals) { return Ox.formatNumber(number / total * 100, decimals) + Ox._('%'); }; @@ -786,7 +772,7 @@ Ox.formatRoman Formats a number as a roman numeral > Ox.formatRoman(10000) 'MMMMMMMMMM' @*/ -export function formatRoman(number) { +Ox.formatRoman = function(number) { var string = ''; Ox.forEach({ M: 1000, CM: 900, D: 500, CD: 400, C: 100, XC: 90, @@ -803,7 +789,7 @@ export function formatRoman(number) { /*@ Ox.formatSRT Formats subtitles as SRT @*/ -export function formatSRT(subtitles) { +Ox.formatSRT = function(subtitles) { return '\ufeff' + Ox.sortBy(subtitles, ['in', 'out']).map(function(subtitle, index) { return [ index + 1, @@ -830,7 +816,7 @@ Ox.formatString Basic string formatting > Ox.formatString('{b}', {a: 'foobar'}, true) '{b}' @*/ -export function formatString(string, collection, keepUnmatched) { +Ox.formatString = function(string, collection, keepUnmatched) { return string.replace(/\{([^}]+)\}/g, function(string, match) { // make sure to not split at escaped dots ('\.') var key, @@ -858,7 +844,7 @@ Ox.formatUnit Formats a number with a unit > Ox.formatUnit(100/3, '%') '33%' @*/ -export function formatUnit(number, string, decimals) { +Ox.formatUnit = function(number, string, decimals) { return Ox.formatNumber(number, decimals) + (/^[:%]/.test(string) ? '' : ' ') + string; }; @@ -873,7 +859,7 @@ Ox.formatValue Formats a numerical value "1.15 GiB" @*/ // fixme: is this the best name? -export function formatValue(number, string, bin) { +Ox.formatValue = function(number, string, bin) { var base = bin ? 1024 : 1000, length = Ox.PREFIXES.length, ret; diff --git a/source/Ox/js/Function.js b/source/Ox/js/Function.js index 5c70bb4c..5d2a41d1 100644 --- a/source/Ox/js/Function.js +++ b/source/Ox/js/Function.js @@ -1,17 +1,5 @@ 'use strict'; -import * as OxObject from './Object.js'; -import * as OxConstants from './Constants.js'; -import * as OxMath from './Math.js'; - -const Ox = {}; - -Object.assign(Ox, - OxObject, - OxConstants, - OxMath -); - /*@ Ox.cache Memoize a function fn function @@ -27,7 +15,7 @@ Ox.cache Memoize a function false @*/ // TODO: add async test -export function cache(fn, options) { +Ox.cache = function(fn, options) { var cache = {}, ret; options = options || {}; options.async = options.async || false; @@ -77,7 +65,7 @@ Ox.debounce Runs a function once it stops being called for a given interval ms Interval in milliseconds immediate If true, function is called once immediately @*/ -export function debounce(fn/*, ms, immediate*/) { +Ox.debounce = function(fn/*, ms, immediate*/) { var args, immediate = Ox.last(arguments) === true, ms = Ox.isNumber(arguments[1]) ? arguments[1] : 250, @@ -107,7 +95,7 @@ Ox.identity Returns its first argument > Ox.identity(Infinity) Infinity @*/ -export function identity(value) { +Ox.identity = function(value) { return value; }; @@ -120,7 +108,7 @@ Ox.noop Returns undefined and calls optional callback without arguments > Ox.noop(1, 2, 3, function() { Ox.test(arguments.length, 0); }) undefined @*/ -export function noop() { +Ox.noop = function() { var callback = Ox.last(arguments); Ox.isFunction(callback) && callback(); }; @@ -130,7 +118,7 @@ Ox.once Runs a function once, and then never again (fn) -> Function that will run only once fn Function to run once @*/ -export function once(fn) { +Ox.once = function(fn) { var once = false; return function() { if (!once) { @@ -151,11 +139,11 @@ Ox.queue Queue of asynchronous function calls with cached results fn Queued function maxThreads Number of parallel function calls @*/ -export function queue(fn, maxThreads) { +Ox.queue = function(fn, maxThreads) { maxThreads = maxThreads || 10; var processing = [], queued = [], - ret = cache(function() { + ret = Ox.cache(function() { var args = Ox.slice(arguments); queued.push({args: args, key: getKey(args)}); process(); @@ -211,7 +199,7 @@ Ox.throttle Runs a function at most once per given interval fn Function to throttle ms Interval in milliseconds @*/ -export function throttle(fn, ms) { +Ox.throttle = function(fn, ms) { var args, timeout; ms = arguments.length == 1 ? 250 : ms; @@ -234,7 +222,7 @@ export function throttle(fn, ms) { Ox.time Returns the time it takes to execute a given function (fn) -> Time in milliseconds @*/ -export function time(fn) { +Ox.time = function(fn) { var time = new Date(); fn(); return new Date() - time; diff --git a/source/Ox/js/Geo.js b/source/Ox/js/Geo.js index b635932d..aa341cf1 100644 --- a/source/Ox/js/Geo.js +++ b/source/Ox/js/Geo.js @@ -1,488 +1,480 @@ 'use strict'; -import * as OxObject from './Object.js'; -import * as OxConstants from './Constants.js'; -import * as OxMath from './Math.js'; +(function() { -const Ox = {}; + // fixme: make all this work with different types of "points" + // i.e. {lat, lng}, [lat, lng] -Object.assign(Ox, - OxObject, - OxConstants, - OxMath -); - -// fixme: make all this work with different types of "points" -// i.e. {lat, lng}, [lat, lng] - -function deg(point) { - return Ox.map(point, function(val) { - return Ox.mod(Ox.deg(val) + 180, 360) - 180; - }); -} - -function rad(point) { - return Ox.map(point, function(val) { - return Ox.rad(val); - }); -} - -function splitArea(area) { - return Ox.crossesDateline(area.sw, area.ne) ? [ - {sw: area.sw, ne: {lat: area.ne.lat, lng: 180}}, - {sw: {lat: area.sw.lat, lng: -180}, ne: area.ne} - ] : [area]; -} - -/*@ -Ox.crossesDateline Returns true if a given line crosses the dateline - > Ox.crossesDateline({lat: 0, lng: -90}, {lat: 0, lng: 90}) - false - > Ox.crossesDateline({lat: 0, lng: 90}, {lat: 0, lng: -90}) - true -@*/ -// FIXME: argument should be {w: ..., e: ...} -export function crossesDateline(pointA, pointB) { - return pointA.lng > pointB.lng; -}; - -/*@ -Ox.getArea Returns the area in square meters of a given rectancle -@*/ -// FIXME: argument should be {sw: ..., ne: ...} -export function getArea(pointA, pointB) { - /* - area of a ring between two latitudes: - 2 * PI * r^2 * abs(sin(latA) - sin(latB)) - see http://mathforum.org/library/drmath/view/63767.html - => - 2 * Math.PI - * Math.pow(Ox.EARTH_RADIUS, 2) - * Math.abs(Math.sin(Ox.rad(latA)) - Math.sin(Ox.rad(latB))) - * Math.abs(Ox.rad(lngA) - Ox.rad(lngB)) - / (2 * Math.PI) - */ - if (Ox.crossesDateline(pointA, pointB)) { - pointB.lng += 360; - } - pointA = rad(pointA); - pointB = rad(pointB); - return Math.pow(Ox.EARTH_RADIUS, 2) - * Math.abs(Math.sin(pointA.lat) - Math.sin(pointB.lat)) - * Math.abs(pointA.lng - pointB.lng); -}; - -/*@ -Ox.getAverageBearing Returns the average of two bearings - > Ox.getAverageBearing(0, 90) - 45 - > Ox.getAverageBearing(10, 350) - 0 -@*/ -// FIXME: find the proper name of this operation -// FIMXE: use in manhattan grid example -export function getAverageBearing(bearingA, bearingB) { - return Ox.mod((bearingA + bearingB) / 2 + ( - Math.abs(bearingA - bearingB) > 180 ? 180 : 0 - ), 360); -}; - -/*@ -Ox.getBearing Returns the bearing from one point to another - > Ox.getBearing({lat: -45, lng: 0}, {lat: 45, lng: 0}) - 0 - > Ox.getBearing({lat: 0, lng: -90}, {lat: 0, lng: 90}) - 90 -@*/ -export function getBearing(pointA, pointB) { - pointA = rad(pointA); - pointB = rad(pointB); - var x = Math.cos(pointA.lat) * Math.sin(pointB.lat) - - Math.sin(pointA.lat) * Math.cos(pointB.lat) - * Math.cos(pointB.lng - pointA.lng), - y = Math.sin(pointB.lng - pointA.lng) - * Math.cos(pointB.lat); - return (Ox.deg(Math.atan2(y, x)) + 360) % 360; -}; - -// FIXME: name, docs -export function getBearingDifference(bearingA, bearingB) { - var difference = Math.abs(bearingA - bearingB); - return difference > 180 ? 360 - difference : difference; -}; - -/*@ -Ox.getCenter Returns the center of a recangle on a spehre - > Ox.getCenter({lat: -45, lng: -90}, {lat: 45, lng: 90}) - {lat: 0, lng: 0} -@*/ -export function getCenter(pointA, pointB) { - pointA = rad(pointA); - pointB = rad(pointB); - var x = Math.cos(pointB.lat) - * Math.cos(pointB.lng - pointA.lng), - y = Math.cos(pointB.lat) - * Math.sin(pointB.lng - pointA.lng), - d = Math.sqrt( - Math.pow(Math.cos(pointA.lat) + x, 2) + Math.pow(y, 2) - ), - lat = Math.atan2(Math.sin(pointA.lat) + Math.sin(pointB.lat), d), - lng = pointA.lng + Math.atan2(y, Math.cos(pointA.lat) + x); - return deg({lat: lat, lng: lng}); -}; - -/*@ -Ox.getCircle Returns points on a circle around a given point - (center, radius, precision) -> Points - center Center point ({lat, lng}) - radius Radius in meters - precision Precision (the circle will have 2^precision segments) -@*/ -export function getCircle(center, radius, precision) { - return Ox.range( - 0, 360, 360 / Math.pow(2, precision) - ).map(function(bearing) { - return Ox.getPoint(center, radius, bearing); - }); -}; - -// FIXME: name, docs -export function getClosestBearing(bearing, bearings) { - var differences = bearings.map(function(bearing_) { - return getBearingDifference(bearing, bearing_); - }); - return bearings[differences.indexOf(Ox.min(differences))]; -}; - -/*@ -Ox.getDegreesPerMeter Returns degrees per meter at a given latitude - > 360 / Ox.getDegreesPerMeter(0) - Ox.EARTH_CIRCUMFERENCE -@*/ -export function getDegreesPerMeter(lat) { - return 360 / Ox.EARTH_CIRCUMFERENCE / Math.cos(lat * Math.PI / 180); -}; - -/*@ -Ox.getDistance Returns the distance in meters between two points - > Ox.getDistance({lat: -45, lng: -90}, {lat: 45, lng: 90}) * 2 - Ox.EARTH_CIRCUMFERENCE -@*/ -export function getDistance(pointA, pointB) { - pointA = rad(pointA); - pointB = rad(pointB); - return Math.acos( - Math.sin(pointA.lat) * Math.sin(pointB.lat) - + Math.cos(pointA.lat) * Math.cos(pointB.lat) - * Math.cos(pointB.lng - pointA.lng) - ) * Ox.EARTH_RADIUS; -}; - -/*@ -Ox.getLatLngByXY Returns lat/lng for a given x/y on a 1x1 mercator projection - > Ox.getLatLngByXY({x: 0.5, y: 0.5}) - {lat: -0, lng: 0} -@*/ -export function getLatLngByXY(xy) { - function getValue(value) { - return (value - 0.5) * 2 * Math.PI; - } - return { - lat: -Ox.deg(Math.atan(Ox.sinh(getValue(xy.y)))), - lng: Ox.deg(getValue(xy.x)) - }; -}; - -/*@ -Ox.getLine Returns points on a line between two points - (pointA, pointB, precision) -> Points - pointA Start point ({lat, lng}) - pointB End point ({lat, lng}) - precision Precision (the line will have 2^precision segments) -@*/ -export function getLine(pointA, pointB, precision) { - var line = [pointA, pointB], points; - while (precision > 0) { - points = [line[0]]; - Ox.loop(line.length - 1, function(i) { - points.push( - Ox.getCenter(line[i], line[i + 1]), - line[i + 1] - ); + function deg(point) { + return Ox.map(point, function(val) { + return Ox.mod(Ox.deg(val) + 180, 360) - 180; }); - line = points; - precision--; } - return line; -}; -/*@ -Ox.getMetersPerDegree Returns meters per degree at a given latitude - > Ox.getMetersPerDegree(0) * 360 - Ox.EARTH_CIRCUMFERENCE -@*/ -export function getMetersPerDegree(lat) { - return Math.cos(lat * Math.PI / 180) * Ox.EARTH_CIRCUMFERENCE / 360; -}; - -/*@ -Ox.getPoint Returns a point at a given distance/bearing from a given point - > Ox.getPoint({lat: -45, lng: 0}, Ox.EARTH_CIRCUMFERENCE / 4, 0) - {lat: 45, lng: 0} -@*/ -export function getPoint(point, distance, bearing) { - var pointB = {}; - point = rad(point); - distance /= Ox.EARTH_RADIUS; - bearing = Ox.rad(bearing); - pointB.lat = Math.asin( - Math.sin(point.lat) * Math.cos(distance) - + Math.cos(point.lat) * Math.sin(distance) * Math.cos(bearing) - ); - pointB.lng = point.lng + Math.atan2( - Math.sin(bearing) * Math.sin(distance) * Math.cos(point.lat), - Math.cos(distance) - Math.sin(point.lat) * Math.sin(pointB.lat) - ); - return deg(pointB); -}; - -/*@ -Ox.getXYByLatLng Returns x/y on a 1x1 mercator projection for a given lat/lng - > Ox.getXYByLatLng({lat: 0, lng: 0}) - {x: 0.5, y: 0.5} -@*/ -export function getXYByLatLng(latlng) { - function getValue(value) { - return value / (2 * Math.PI) + 0.5; - } - return { - x: getValue(Ox.rad(latlng.lng)), - y: getValue(Ox.asinh(Math.tan(Ox.rad(-latlng.lat)))) - }; -}; - -/*@ -Ox.isPolar Returns true if a given point is outside the bounds of a mercator projection - > Ox.isPolar({lat: 90, lng: 0}) - true -@*/ -export function isPolar(point) { - return point.lat < Ox.MIN_LATITUDE || point.lat > Ox.MAX_LATITUDE; -}; - -/*@ -Ox.containsArea Returns true if an area contains another area - - > Ox.containsArea(Ox.test.areas[0], Ox.test.areas[1]) - false - > Ox.containsArea(Ox.test.areas[2], Ox.test.areas[3]) - true -@*/ -// FIXME: Shouldn't this be rewritten as a test -// if the intersection is equal to the inner area? -export function containsArea(areaA, areaB) { - // If an area crosses the dateline, - // we split it into two parts, - // west and east of the dateline - var areas = [areaA, areaB].map(splitArea), ret; - function contains(areaA, areaB) { - return areaA.sw.lat <= areaB.sw.lat - && areaA.sw.lng <= areaB.sw.lng - && areaA.ne.lat >= areaB.ne.lat - && areaA.ne.lng >= areaB.ne.lng; - } - // For each part of the inner area, test if it - // is contained in any part of the outer area - Ox.forEach(areas[1], function(area1) { - Ox.forEach(areas[0], function(area0) { - ret = contains(area0, area1); - // Break if the outer part contains the inner part - return !ret; + function rad(point) { + return Ox.map(point, function(val) { + return Ox.rad(val); }); - // Break if no outer part contains the inner part - return ret; - }); - return ret; -}; + } -/*@ -Ox.intersectAreas Returns the intersection of two areas, or null - - > Ox.intersectAreas([Ox.test.areas[0], Ox.test.areas[1]]) - {sw: {lat: 0, lng: 0}, ne: {lat: 0, lng: 0}} - > Ox.intersectAreas([Ox.test.areas[2], Ox.test.areas[3]]) - {sw: {lat: 25, lng: -155}, ne: {lat: 30, lng: -150}} -@*/ -// FIXME: handle the a corner case where -// two areas have two intersections -export function intersectAreas(areas) { - var intersections, ret; - // If an area crosses the dateline, - // we split it into two parts, - // west and east of the dateline - areas = areas.map(splitArea); - ret = areas[0]; - function intersect(areaA, areaB) { - return areaA.sw.lat > areaB.ne.lat - || areaA.sw.lng > areaB.ne.lng - || areaA.ne.lat < areaB.sw.lat - || areaA.ne.lng < areaB.sw.lng - ? null : { - sw: { - lat: Math.max(areaA.sw.lat, areaB.sw.lat), - lng: Math.max(areaA.sw.lng, areaB.sw.lng) - }, - ne: { - lat: Math.min(areaA.ne.lat, areaB.ne.lat), - lng: Math.min(areaA.ne.lng, areaB.ne.lng) - } + function splitArea(area) { + return Ox.crossesDateline(area.sw, area.ne) ? [ + {sw: area.sw, ne: {lat: area.ne.lat, lng: 180}}, + {sw: {lat: area.sw.lat, lng: -180}, ne: area.ne} + ] : [area]; + } + + /*@ + Ox.crossesDateline Returns true if a given line crosses the dateline + > Ox.crossesDateline({lat: 0, lng: -90}, {lat: 0, lng: 90}) + false + > Ox.crossesDateline({lat: 0, lng: 90}, {lat: 0, lng: -90}) + true + @*/ + // FIXME: argument should be {w: ..., e: ...} + Ox.crossesDateline = function(pointA, pointB) { + return pointA.lng > pointB.lng; + }; + + /*@ + Ox.getArea Returns the area in square meters of a given rectancle + @*/ + // FIXME: argument should be {sw: ..., ne: ...} + Ox.getArea = function(pointA, pointB) { + /* + area of a ring between two latitudes: + 2 * PI * r^2 * abs(sin(latA) - sin(latB)) + see http://mathforum.org/library/drmath/view/63767.html + => + 2 * Math.PI + * Math.pow(Ox.EARTH_RADIUS, 2) + * Math.abs(Math.sin(Ox.rad(latA)) - Math.sin(Ox.rad(latB))) + * Math.abs(Ox.rad(lngA) - Ox.rad(lngB)) + / (2 * Math.PI) + */ + if (Ox.crossesDateline(pointA, pointB)) { + pointB.lng += 360; + } + pointA = rad(pointA); + pointB = rad(pointB); + return Math.pow(Ox.EARTH_RADIUS, 2) + * Math.abs(Math.sin(pointA.lat) - Math.sin(pointB.lat)) + * Math.abs(pointA.lng - pointB.lng); + }; + + /*@ + Ox.getAverageBearing Returns the average of two bearings + > Ox.getAverageBearing(0, 90) + 45 + > Ox.getAverageBearing(10, 350) + 0 + @*/ + // FIXME: find the proper name of this operation + // FIMXE: use in manhattan grid example + Ox.getAverageBearing = function(bearingA, bearingB) { + return Ox.mod((bearingA + bearingB) / 2 + ( + Math.abs(bearingA - bearingB) > 180 ? 180 : 0 + ), 360); + }; + + /*@ + Ox.getBearing Returns the bearing from one point to another + > Ox.getBearing({lat: -45, lng: 0}, {lat: 45, lng: 0}) + 0 + > Ox.getBearing({lat: 0, lng: -90}, {lat: 0, lng: 90}) + 90 + @*/ + Ox.getBearing = function(pointA, pointB) { + pointA = rad(pointA); + pointB = rad(pointB); + var x = Math.cos(pointA.lat) * Math.sin(pointB.lat) + - Math.sin(pointA.lat) * Math.cos(pointB.lat) + * Math.cos(pointB.lng - pointA.lng), + y = Math.sin(pointB.lng - pointA.lng) + * Math.cos(pointB.lat); + return (Ox.deg(Math.atan2(y, x)) + 360) % 360; + }; + + // FIXME: name, docs + Ox.getBearingDifference = function(bearingA, bearingB) { + var difference = Math.abs(bearingA - bearingB); + return difference > 180 ? 360 - difference : difference; + }; + + /*@ + Ox.getCenter Returns the center of a recangle on a spehre + > Ox.getCenter({lat: -45, lng: -90}, {lat: 45, lng: 90}) + {lat: 0, lng: 0} + @*/ + Ox.getCenter = function(pointA, pointB) { + pointA = rad(pointA); + pointB = rad(pointB); + var x = Math.cos(pointB.lat) + * Math.cos(pointB.lng - pointA.lng), + y = Math.cos(pointB.lat) + * Math.sin(pointB.lng - pointA.lng), + d = Math.sqrt( + Math.pow(Math.cos(pointA.lat) + x, 2) + Math.pow(y, 2) + ), + lat = Math.atan2(Math.sin(pointA.lat) + Math.sin(pointB.lat), d), + lng = pointA.lng + Math.atan2(y, Math.cos(pointA.lat) + x); + return deg({lat: lat, lng: lng}); + }; + + /*@ + Ox.getCircle Returns points on a circle around a given point + (center, radius, precision) -> Points + center Center point ({lat, lng}) + radius Radius in meters + precision Precision (the circle will have 2^precision segments) + @*/ + Ox.getCircle = function(center, radius, precision) { + return Ox.range( + 0, 360, 360 / Math.pow(2, precision) + ).map(function(bearing) { + return Ox.getPoint(center, radius, bearing); + }); + }; + + // FIXME: name, docs + Ox.getClosestBearing = function(bearing, bearings) { + var differences = bearings.map(function(bearing_) { + return getBearingDifference(bearing, bearing_); + }); + return bearings[differences.indexOf(Ox.min(differences))]; + }; + + /*@ + Ox.getDegreesPerMeter Returns degrees per meter at a given latitude + > 360 / Ox.getDegreesPerMeter(0) + Ox.EARTH_CIRCUMFERENCE + @*/ + Ox.getDegreesPerMeter = function(lat) { + return 360 / Ox.EARTH_CIRCUMFERENCE / Math.cos(lat * Math.PI / 180); + }; + + /*@ + Ox.getDistance Returns the distance in meters between two points + > Ox.getDistance({lat: -45, lng: -90}, {lat: 45, lng: 90}) * 2 + Ox.EARTH_CIRCUMFERENCE + @*/ + Ox.getDistance = function(pointA, pointB) { + pointA = rad(pointA); + pointB = rad(pointB); + return Math.acos( + Math.sin(pointA.lat) * Math.sin(pointB.lat) + + Math.cos(pointA.lat) * Math.cos(pointB.lat) + * Math.cos(pointB.lng - pointA.lng) + ) * Ox.EARTH_RADIUS; + }; + + /*@ + Ox.getLatLngByXY Returns lat/lng for a given x/y on a 1x1 mercator projection + > Ox.getLatLngByXY({x: 0.5, y: 0.5}) + {lat: -0, lng: 0} + @*/ + Ox.getLatLngByXY = function(xy) { + function getValue(value) { + return (value - 0.5) * 2 * Math.PI; + } + return { + lat: -Ox.deg(Math.atan(Ox.sinh(getValue(xy.y)))), + lng: Ox.deg(getValue(xy.x)) }; - } - Ox.forEach(areas.slice(1), function(parts) { - if (ret.length == 1 && parts.length == 1) { - ret = intersect(ret[0], parts[0]); - } else { - // intersect each part of the intersection - // with all parts of the next area - intersections = Ox.compact(ret.map(function(part) { - return Ox.intersectAreas(parts.concat(part)); - })); - ret = intersections.length == 0 ? null - : Ox.joinAreas(intersections); - } - if (ret === null) { - return false; // break - } else { - ret = splitArea(ret); - } - }); - return ret ? Ox.joinAreas(ret) : null; -}; + }; -/*@ -Ox.joinAreas Joins an array of areas - - > Ox.joinAreas(Ox.test.areas) - {sw: {lat: -30, lng: 150}, ne: {lat: 30, lng: -150}} -@*/ -export function joinAreas(areas) { - // While the combined latitude is trivial (min to max), the combined longitude - // spans from the eastern to the western edge of the largest gap between areas - var ret = areas[0], - gaps = [{ - sw: {lat: -90, lng: ret.ne.lng}, - ne: {lat: 90, lng: ret.sw.lng} - }]; - function containsGaps(area) { - return Ox.getIndices(gaps, function(gap) { - return Ox.containsArea({ - sw: {lat: -90, lng: area.sw.lng}, - ne: {lat: 90, lng: area.ne.lng} - }, gap); - }); - } - function intersectsWithGaps(area) { - var ret = {}; - gaps.forEach(function(gap, i) { - var intersection = Ox.intersectAreas([area, gap]); - if (intersection) { - ret[i] = intersection; - } + /*@ + Ox.getLine Returns points on a line between two points + (pointA, pointB, precision) -> Points + pointA Start point ({lat, lng}) + pointB End point ({lat, lng}) + precision Precision (the line will have 2^precision segments) + @*/ + Ox.getLine = function(pointA, pointB, precision) { + var line = [pointA, pointB], points; + while (precision > 0) { + points = [line[0]]; + Ox.loop(line.length - 1, function(i) { + points.push( + Ox.getCenter(line[i], line[i + 1]), + line[i + 1] + ); + }); + line = points; + precision--; + } + return line; + }; + + /*@ + Ox.getMetersPerDegree Returns meters per degree at a given latitude + > Ox.getMetersPerDegree(0) * 360 + Ox.EARTH_CIRCUMFERENCE + @*/ + Ox.getMetersPerDegree = function(lat) { + return Math.cos(lat * Math.PI / 180) * Ox.EARTH_CIRCUMFERENCE / 360; + }; + + /*@ + Ox.getPoint Returns a point at a given distance/bearing from a given point + > Ox.getPoint({lat: -45, lng: 0}, Ox.EARTH_CIRCUMFERENCE / 4, 0) + {lat: 45, lng: 0} + @*/ + Ox.getPoint = function(point, distance, bearing) { + var pointB = {}; + point = rad(point); + distance /= Ox.EARTH_RADIUS; + bearing = Ox.rad(bearing); + pointB.lat = Math.asin( + Math.sin(point.lat) * Math.cos(distance) + + Math.cos(point.lat) * Math.sin(distance) * Math.cos(bearing) + ); + pointB.lng = point.lng + Math.atan2( + Math.sin(bearing) * Math.sin(distance) * Math.cos(point.lat), + Math.cos(distance) - Math.sin(point.lat) * Math.sin(pointB.lat) + ); + return deg(pointB); + }; + + /*@ + Ox.getXYByLatLng Returns x/y on a 1x1 mercator projection for a given lat/lng + > Ox.getXYByLatLng({lat: 0, lng: 0}) + {x: 0.5, y: 0.5} + @*/ + Ox.getXYByLatLng = function(latlng) { + function getValue(value) { + return value / (2 * Math.PI) + 0.5; + } + return { + x: getValue(Ox.rad(latlng.lng)), + y: getValue(Ox.asinh(Math.tan(Ox.rad(-latlng.lat)))) + }; + }; + + /*@ + Ox.isPolar Returns true if a given point is outside the bounds of a mercator projection + > Ox.isPolar({lat: 90, lng: 0}) + true + @*/ + Ox.isPolar = function(point) { + return point.lat < Ox.MIN_LATITUDE || point.lat > Ox.MAX_LATITUDE; + }; + + /*@ + Ox.containsArea Returns true if an area contains another area + + > Ox.containsArea(Ox.test.areas[0], Ox.test.areas[1]) + false + > Ox.containsArea(Ox.test.areas[2], Ox.test.areas[3]) + true + @*/ + // FIXME: Shouldn't this be rewritten as a test + // if the intersection is equal to the inner area? + Ox.containsArea = function(areaA, areaB) { + // If an area crosses the dateline, + // we split it into two parts, + // west and east of the dateline + var areas = [areaA, areaB].map(splitArea), ret; + function contains(areaA, areaB) { + return areaA.sw.lat <= areaB.sw.lat + && areaA.sw.lng <= areaB.sw.lng + && areaA.ne.lat >= areaB.ne.lat + && areaA.ne.lng >= areaB.ne.lng; + } + // For each part of the inner area, test if it + // is contained in any part of the outer area + Ox.forEach(areas[1], function(area1) { + Ox.forEach(areas[0], function(area0) { + ret = contains(area0, area1); + // Break if the outer part contains the inner part + return !ret; + }); + // Break if no outer part contains the inner part + return ret; }); return ret; - } - function isContainedInGap(area) { - var ret = -1; - Ox.forEach(gaps, function(gap, i) { - if (Ox.containsArea(gap, area)) { - ret = i; + }; + + /*@ + Ox.intersectAreas Returns the intersection of two areas, or null + + > Ox.intersectAreas([Ox.test.areas[0], Ox.test.areas[1]]) + {sw: {lat: 0, lng: 0}, ne: {lat: 0, lng: 0}} + > Ox.intersectAreas([Ox.test.areas[2], Ox.test.areas[3]]) + {sw: {lat: 25, lng: -155}, ne: {lat: 30, lng: -150}} + @*/ + // FIXME: handle the a corner case where + // two areas have two intersections + Ox.intersectAreas = function(areas) { + var intersections, ret; + // If an area crosses the dateline, + // we split it into two parts, + // west and east of the dateline + areas = areas.map(splitArea); + ret = areas[0]; + function intersect(areaA, areaB) { + return areaA.sw.lat > areaB.ne.lat + || areaA.sw.lng > areaB.ne.lng + || areaA.ne.lat < areaB.sw.lat + || areaA.ne.lng < areaB.sw.lng + ? null : { + sw: { + lat: Math.max(areaA.sw.lat, areaB.sw.lat), + lng: Math.max(areaA.sw.lng, areaB.sw.lng) + }, + ne: { + lat: Math.min(areaA.ne.lat, areaB.ne.lat), + lng: Math.min(areaA.ne.lng, areaB.ne.lng) + } + }; + } + Ox.forEach(areas.slice(1), function(parts) { + if (ret.length == 1 && parts.length == 1) { + ret = intersect(ret[0], parts[0]); + } else { + // intersect each part of the intersection + // with all parts of the next area + intersections = Ox.compact(ret.map(function(part) { + return Ox.intersectAreas(parts.concat(part)); + })); + ret = intersections.length == 0 ? null + : Ox.joinAreas(intersections); + } + if (ret === null) { return false; // break + } else { + ret = splitArea(ret); } }); - return ret; - } - areas.slice(1).forEach(function(area) { - var index, indices, intersections; - if (area.sw.lat < ret.sw.lat) { - ret.sw.lat = area.sw.lat; - } - if (area.ne.lat > ret.ne.lat) { - ret.ne.lat = area.ne.lat; - } - // If the area is contained in a gap, split the gap in two - index = isContainedInGap(area); - if (index > -1) { - gaps.push({ - sw: gaps[index].sw, - ne: {lat: 90, lng: area.sw.lng} - }); - gaps.push({ - sw: {lat: -90, lng: area.ne.lng}, - ne: gaps[index].ne - }); - gaps.splice(index, 1); - } else { - // If the area contains gaps, remove them - indices = containsGaps(area); - Ox.reverse(indices).forEach(function(index) { - gaps.splice(index, 1); - }); - // If the area intersects with gaps, shrink them - intersections = intersectsWithGaps(area); - Ox.forEach(intersections, function(intersection, index) { - gaps[index] = { - sw: { - lat: -90, - lng: gaps[index].sw.lng == intersection.sw.lng - ? intersection.ne.lng : gaps[index].sw.lng - }, - ne: { - lat: 90, - lng: gaps[index].ne.lng == intersection.ne.lng - ? intersection.sw.lng : gaps[index].ne.lng - } - }; - }); - } - }); - if (gaps.length == 0) { - ret.sw.lng = -180; - ret.ne.lng = 180; - } else { - gaps.sort(function(a, b) { - return ( - b.ne.lng - + (Ox.crossesDateline(b.sw, b.ne) ? 360 : 0) - - b.sw.lng - ) - ( - a.ne.lng - + (Ox.crossesDateline(a.sw, a.ne) ? 360 : 0) - - a.sw.lng - ); - }); - ret.sw.lng = gaps[0].ne.lng; - ret.ne.lng = gaps[0].sw.lng; - } - return ret; -}; + return ret ? Ox.joinAreas(ret) : null; + }; + + /*@ + Ox.joinAreas Joins an array of areas + + > Ox.joinAreas(Ox.test.areas) + {sw: {lat: -30, lng: 150}, ne: {lat: 30, lng: -150}} + @*/ + Ox.joinAreas = function(areas) { + // While the combined latitude is trivial (min to max), the combined longitude + // spans from the eastern to the western edge of the largest gap between areas + var ret = areas[0], + gaps = [{ + sw: {lat: -90, lng: ret.ne.lng}, + ne: {lat: 90, lng: ret.sw.lng} + }]; + function containsGaps(area) { + return Ox.getIndices(gaps, function(gap) { + return Ox.containsArea({ + sw: {lat: -90, lng: area.sw.lng}, + ne: {lat: 90, lng: area.ne.lng} + }, gap); + }); + } + function intersectsWithGaps(area) { + var ret = {}; + gaps.forEach(function(gap, i) { + var intersection = Ox.intersectAreas([area, gap]); + if (intersection) { + ret[i] = intersection; + } + }); + return ret; + } + function isContainedInGap(area) { + var ret = -1; + Ox.forEach(gaps, function(gap, i) { + if (Ox.containsArea(gap, area)) { + ret = i; + return false; // break + } + }); + return ret; + } + areas.slice(1).forEach(function(area) { + var index, indices, intersections; + if (area.sw.lat < ret.sw.lat) { + ret.sw.lat = area.sw.lat; + } + if (area.ne.lat > ret.ne.lat) { + ret.ne.lat = area.ne.lat; + } + // If the area is contained in a gap, split the gap in two + index = isContainedInGap(area); + if (index > -1) { + gaps.push({ + sw: gaps[index].sw, + ne: {lat: 90, lng: area.sw.lng} + }); + gaps.push({ + sw: {lat: -90, lng: area.ne.lng}, + ne: gaps[index].ne + }); + gaps.splice(index, 1); + } else { + // If the area contains gaps, remove them + indices = containsGaps(area); + Ox.reverse(indices).forEach(function(index) { + gaps.splice(index, 1); + }); + // If the area intersects with gaps, shrink them + intersections = intersectsWithGaps(area); + Ox.forEach(intersections, function(intersection, index) { + gaps[index] = { + sw: { + lat: -90, + lng: gaps[index].sw.lng == intersection.sw.lng + ? intersection.ne.lng : gaps[index].sw.lng + }, + ne: { + lat: 90, + lng: gaps[index].ne.lng == intersection.ne.lng + ? intersection.sw.lng : gaps[index].ne.lng + } + }; + }); + } + }); + if (gaps.length == 0) { + ret.sw.lng = -180; + ret.ne.lng = 180; + } else { + gaps.sort(function(a, b) { + return ( + b.ne.lng + + (Ox.crossesDateline(b.sw, b.ne) ? 360 : 0) + - b.sw.lng + ) - ( + a.ne.lng + + (Ox.crossesDateline(a.sw, a.ne) ? 360 : 0) + - a.sw.lng + ); + }); + ret.sw.lng = gaps[0].ne.lng; + ret.ne.lng = gaps[0].sw.lng; + } + return ret; + }; + +}()); diff --git a/source/Ox/js/HTML.js b/source/Ox/js/HTML.js index ca1da093..b5cf839b 100644 --- a/source/Ox/js/HTML.js +++ b/source/Ox/js/HTML.js @@ -1,682 +1,668 @@ 'use strict'; -import * as OxArray from './Array.js'; -import * as OxObject from './Object.js'; -import * as OxConstants from './Constants.js'; -import * as OxMath from './Math.js'; -import * as OxString from './String.js'; +(function() { -const Ox = {}; - -Object.assign(Ox, - OxArray, - OxObject, - OxConstants, - OxMath, - OxString, -); - - -var defaultTags = [ - // inline formatting - {'name': 'b'}, - {'name': 'bdi'}, - {'name': 'code'}, - {'name': 'em'}, - {'name': 'i'}, - {'name': 'q'}, - {'name': 's'}, - {'name': 'span'}, - {'name': 'strong'}, - {'name': 'sub'}, - {'name': 'sup'}, - {'name': 'u'}, - // block formatting - {'name': 'blockquote'}, - {'name': 'cite'}, - { - 'name': 'div', - 'optional': ['style'], - 'validate': { - 'style': /^direction: rtl$/ - } - }, - {'name': 'h1'}, - {'name': 'h2'}, - {'name': 'h3'}, - {'name': 'h4'}, - {'name': 'h5'}, - {'name': 'h6'}, - {'name': 'p'}, - {'name': 'pre'}, - // lists - {'name': 'li'}, - {'name': 'ol'}, - {'name': 'ul'}, - // definition lists - {'name': 'dl'}, - {'name': 'dt'}, - {'name': 'dd'}, - // tables - {'name': 'table'}, - {'name': 'tbody'}, - {'name': 'td'}, - {'name': 'tfoot'}, - {'name': 'th'}, - {'name': 'thead'}, - {'name': 'tr'}, - // other - {'name': '[]'}, - { - 'name': 'a', - 'required': ['href'], - 'optional': ['target'], - 'validate': { - 'href': /^((https?:\/\/|\/|mailto:).*?)/, - 'target': /^_blank$/ - } - }, - {'name': 'br'}, - { - 'name': 'iframe', - 'optional': ['width', 'height'], - 'required': ['src'], - 'validate': { - 'width': /^\d+$/, - 'height': /^\d+$/, - 'src': /^((https?:\/\/|\/).*?)/ - } - }, - { - 'name': 'img', - 'optional': ['width', 'height'], - 'required': ['src'], - 'validate': { - 'width': /^\d+$/, - 'height': /^\d+$/, - 'src': /^((https?:\/\/|\/).*?)/ - }, - }, - {'name': 'figure'}, - {'name': 'figcaption'} - ], - htmlEntities = { - '"': '"', '&': '&', "'": ''', '<': '<', '>': '>' - }, - regexp = { - entity: /&[^\s]+?;/g, - html: /[<&]/, - tag: new RegExp('<\\/?(' + [ - 'a', 'b', 'br', 'code', 'i', 's', 'span', 'u' - ].join('|') + ')\\/?>', 'gi') - }, - salt = Ox.range(2).map(function(){ - return Ox.range(16).map(function() { - return Ox.char(65 + Ox.random(26)); - }).join(''); - }); - -function _addLinks(string, obfuscate) { - return string - .replace( - /\b((https?:\/\/|www\.).+?)([.,:;!?)\]]*?(\s|$))/gi, - function(match, url, prefix, end) { - prefix = prefix.toLowerCase() == 'www.' ? 'http://' : ''; - return Ox.formatString( - '{url}{end}', - {end: end, prefix: prefix, url: url} - ); - } - ) - .replace( - /\b([0-9A-Z.+\-_]+@(?:[0-9A-Z\-]+\.)+[A-Z]{2,6})\b/gi, - obfuscate ? function(match, mail) { - return Ox.encodeEmailAddress(mail); - } : '$1' - ); -} - -function _decodeHTMLEntities(string) { - return string - .replace( - new RegExp('(' + Ox.values(htmlEntities).join('|') + ')', 'g'), - function(match) { - return Ox.keyOf(htmlEntities, match); - } - ) - .replace( - /&#([0-9A-FX]+);/gi, - function(match, code) { - return Ox.char( - /^X/i.test(code) - ? parseInt(code.slice(1), 16) - : parseInt(code, 10) - ); - } - ); -} - -// Splits a string into text (even indices) and tags (odd indices), ignoring -// tags with starting positions that are included in the ignore array -function splitHTMLTags(string, ignore) { - var isTag = false, ret = ['']; - ignore = ignore || []; - Ox.forEach(string, function(char, i) { - if (!isTag && char == '<' && ignore.indexOf(i) == -1) { - isTag = true; - ret.push(''); - } - ret[ret.length - 1] += char; - if (isTag && char == '>') { - isTag = false; - ret.push(''); - } - }); - return ret; -} - -/*@ -Ox.addLinks Takes a string and adds links for e-mail addresses and URLs - (string[, isHTML]) -> Formatted string - string String - isHTML If true, ignore matches in tags or enclosed by links - > Ox.addLinks('foo bar ') - 'foo bar <foo@bar.com>' - > Ox.addLinks('www.foo.com/bar#baz, etc.') - 'www.foo.com/bar#baz, etc.' - > Ox.addLinks('www.foo.com', true) - 'www.foo.com' -@*/ -export function addLinks(string, isHTML) { - var isLink = false; - return isHTML - ? splitHTMLTags(string).map(function(string, i) { - var isTag = i % 2; - if (isTag) { - if (/^ Returns obfuscated mailto: link - > Ox.encodeEmailAddress('mailto:foo@bar.com').indexOf(':') > -1 - true -@*/ -export function encodeEmailAddress(address, text) { - var parts = ['mailto:' + address, text || address].map(function(part) { - return Ox.map(part, function(char) { - var code = char.charCodeAt(0); - return char == ':' ? ':' - : '&#' - + (Math.random() < 0.5 ? code : 'x' + code.toString(16)) - + ';'; + }, + {'name': 'h1'}, + {'name': 'h2'}, + {'name': 'h3'}, + {'name': 'h4'}, + {'name': 'h5'}, + {'name': 'h6'}, + {'name': 'p'}, + {'name': 'pre'}, + // lists + {'name': 'li'}, + {'name': 'ol'}, + {'name': 'ul'}, + // definition lists + {'name': 'dl'}, + {'name': 'dt'}, + {'name': 'dd'}, + // tables + {'name': 'table'}, + {'name': 'tbody'}, + {'name': 'td'}, + {'name': 'tfoot'}, + {'name': 'th'}, + {'name': 'thead'}, + {'name': 'tr'}, + // other + {'name': '[]'}, + { + 'name': 'a', + 'required': ['href'], + 'optional': ['target'], + 'validate': { + 'href': /^((https?:\/\/|\/|mailto:).*?)/, + 'target': /^_blank$/ + } + }, + {'name': 'br'}, + { + 'name': 'iframe', + 'optional': ['width', 'height'], + 'required': ['src'], + 'validate': { + 'width': /^\d+$/, + 'height': /^\d+$/, + 'src': /^((https?:\/\/|\/).*?)/ + } + }, + { + 'name': 'img', + 'optional': ['width', 'height'], + 'required': ['src'], + 'validate': { + 'width': /^\d+$/, + 'height': /^\d+$/, + 'src': /^((https?:\/\/|\/).*?)/ + }, + }, + {'name': 'figure'}, + {'name': 'figcaption'} + ], + htmlEntities = { + '"': '"', '&': '&', "'": ''', '<': '<', '>': '>' + }, + regexp = { + entity: /&[^\s]+?;/g, + html: /[<&]/, + tag: new RegExp('<\\/?(' + [ + 'a', 'b', 'br', 'code', 'i', 's', 'span', 'u' + ].join('|') + ')\\/?>', 'gi') + }, + salt = Ox.range(2).map(function(){ + return Ox.range(16).map(function() { + return Ox.char(65 + Ox.random(26)); + }).join(''); }); - }); - return '' + parts[1] + ''; -}; -/*@ -Ox.encodeHTMLEntities Encodes HTML entities - (string[, encodeAll]) -> String - string String - encodeAll If true, encode characters > 127 as numeric entities - > Ox.encodeHTMLEntities('<\'&"> äbçdê') - '<'&"> äbçdê' - > Ox.encodeHTMLEntities('<\'&"> äbçdê', true) - '<'&"> äbçdê' -@*/ -export function encodeHTMLEntities(string, encodeAll) { - return Ox.map(String(string), function(char) { - var code = char.charCodeAt(0); - if (code < 128) { - char = char in htmlEntities ? htmlEntities[char] : char; - } else if (encodeAll) { - char = '&#x' - + Ox.pad(code.toString(16).toUpperCase(), 'left', 4, '0') - + ';'; - } - return char; - }); -}; - -/*@ -Ox.decodeHTMLEntities Decodes HTML entities - (string[, decodeAll]) -> String - string String - decodeAll If true, decode named entities for characters > 127 - Note that `decodeAll` relies on `Ox.normalizeHTML`, which uses the - DOM and may transform the string - > Ox.decodeHTMLEntities('<'&">') - '<\'&">' - > Ox.decodeHTMLEntities('<'&">') - '<\'&">' - > Ox.decodeHTMLEntities('äbçdê') - 'äbçdê' - > Ox.decodeHTMLEntities('äbçdê') - 'äbçdê' - > Ox.decodeHTMLEntities('äbçdê', true) - 'äbçdê' - > Ox.decodeHTMLEntities('β') - 'β' - > Ox.decodeHTMLEntities('β', true) - 'β' - > Ox.decodeHTMLEntities('<b>') - '' -@*/ -export function decodeHTMLEntities(string, decodeAll) { - return decodeAll - ? decodeHTMLEntities(normalizeHTML(string)) - : _decodeHTMLEntities(string); -}; - -/*@ -Ox.highlight Highlight matches in string - (string, query, classname[, isHTML]) -> Output string - string Input string - query Case-insentitive query string, or regular expression - classname Class name for matches - isHTML If true, the input string is treated as HTML - > Ox.highlight('', 'foo', 'c') - '<foo><bar>' - > Ox.highlight('&', '&', 'c') - '&amp;' - > Ox.highlight('&', '&', 'c') - '&' - > Ox.highlight('<foo> <foo>', '', 'c', true) - '<foo> <foo>' - > Ox.highlight('name', 'name', 'c', true) - 'name' - > Ox.highlight('amp & amp', 'amp', 'c', true) - 'amp & amp' - > Ox.highlight('amp & amp', 'amp & amp', 'c', true) - 'amp & amp' - > Ox.highlight('<b>', '', 'c', true) - '<b>' - > Ox.highlight('<b>', '<b>', 'c', true) - '<b>' - > Ox.highlight('foobarbaz', 'foobar', 'c', true) - 'foobarbaz' - > Ox.highlight('foo

bar

baz', 'foobar', 'c', true) - 'foo

bar

baz' - > Ox.highlight('foo
bar baz', 'foo bar', 'c', true) - 'foo
bar
baz' -@*/ -export function highlight(string, query, classname, isHTML) { - if (!query) { - return string; - } - var cursor = 0, - entities = [], - matches = [], - offset = 0, - re = Ox.isRegExp(query) ? query - : new RegExp(Ox.escapeRegExp(query), 'gi'), - span = ['', ''], - tags = []; - function insert(array) { - // for each replacement - array.forEach(function(v) { - // replace the modified value with the original value - string = Ox.splice(string, v.position, v.length, v.value); - // for each match - matches.forEach(function(match) { - if (v.position < match.position) { - // replacement is before match, update match position - match.position += v.value.length - v.length; - } else if ( - v.position < match.position + match.value.length - ) { - // replacement is inside match, update match value - match.value = Ox.splice( - match.value, v.position - match.position, v.length, - v.value + function addLinks(string, obfuscate) { + return string + .replace( + /\b((https?:\/\/|www\.).+?)([.,:;!?)\]]*?(\s|$))/gi, + function(match, url, prefix, end) { + prefix = prefix.toLowerCase() == 'www.' ? 'http://' : ''; + return Ox.formatString( + '{url}{end}', + {end: end, prefix: prefix, url: url} ); } - }); - }); - } - if (isHTML && regexp.html.test(string)) { - string = string // Ox.normalizeHTML(string) - // remove inline tags - .replace(regexp.tag, function(value, tag, position) { - tags.push({ - length: 0, position: position, value: value - }); - return ''; - }) - // decode html entities - .replace(regexp.entity, function(value, position) { - var ret = Ox.decodeHTMLEntities(value, true); - entities.push({ - length: ret.length, position: position, value: value - }); - return ret; - }); - // if decoding entities has created new tags, ignore them - splitHTMLTags(string, entities.map(function(entity) { - var ret = entity.position + offset; - offset += entity.length - entity.value.length; - return ret; - })).forEach(function(v, i) { - if (i % 2 == 0) { - // outside tags, find matches and save position and value - v.replace(re, function(value, position) { - matches.push( - {position: cursor + position, value: value} - ); - }); - } - cursor += v.length; - }); - insert(entities); - insert(tags); - // for each match (in reverse order, so that positions are correct) - matches.reverse().forEach(function(match) { - // wrap it in a span - string = Ox.splice( - string, match.position, match.value.length, - span.join(match.value) + ) + .replace( + /\b([0-9A-Z.+\-_]+@(?:[0-9A-Z\-]+\.)+[A-Z]{2,6})\b/gi, + obfuscate ? function(match, mail) { + return Ox.encodeEmailAddress(mail); + } : '$1' ); - }); - // we may have enclosed single opening or closing tags in a span - if (matches.length && tags.length) { - string = normalizeHTML(string); - } - } else { - string = Ox.encodeHTMLEntities( - string.replace(re, function(value) { - matches.push(span.join(Ox.encodeHTMLEntities(value))); - return salt.join(matches.length - 1); - }) - ); - matches.forEach(function(match, i) { - string = string.replace(new RegExp(salt.join(i)), match); - }); - } - return string; -}; - -/*@ -Ox.normalizeHTML Normalize HTML (using the DOM) - > Ox.normalizeHTML('foo') - 'foo' - > Ox.normalizeHTML('foo') - 'foo' - > Ox.normalizeHTML('<'&"> äbçdê') - '<\'&"> äbçdê' -@*/ -export function normalizeHTML(html) { - return regexp.html.test(html) ? Ox.$('
').html(html).html() : html; -}; - -/*@ -Ox.parseMarkdown Parses (a tiny subset of) Markdown. - Supports `*emphasis*`, `_emphasis_`, `**strong**`, `__strong__`, - `` `code` ``, ``` ``code with backtick (`)`` ```, - ```` ```classname\ngithub-style\ncode blocks\n``` ````, - ``, `` and - `[text](http://example.com "title")`. - > Ox.parseMarkdown('*foo* **bar** `baz` ``back`tick``') - 'foo bar baz back`tick' - > Ox.parseMarkdown('foo\n\nbar\n\nbaz') - 'foo

bar

baz' - > Ox.parseMarkdown('```foo\n\nbar\n\nbaz\n```') - '
bar\n\nbaz\n
' - > Ox.parseMarkdown('') - 'http://example.com' - > Ox.parseMarkdown('``') - '<http://example.com>' - > Ox.parseMarkdown('[example](http://example.com "example.com")') - 'example' - > Ox.parseMarkdown('[example](http://example.com?foo=bar&bar=baz)') - 'example' - > Ox(Ox.parseMarkdown('')).startsWith('mail@example.com' -*/ -export function parseMarkdown(string) { - // see https://github.com/coreyti/showdown/blob/master/src/showdown.js - var array = []; - return string.replace(/\r\n/g, '\n').replace(/\r/g, '\n') - .replace( - /(?:^|\n)```(.*)\n([^`]+)\n```/g, - function(match, classname, code) { - array.push( - '
'
-                    + code.trim().replace(/
' - ); - return salt.join(array.length - 1); - } - ) - .replace( - /(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm, - function(match, prev, backticks, code, next) { - array.push( - prev + '' - + code.trim().replace(/' - ); - return salt.join(array.length - 1); - } - ) - .replace( - /(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, - '$2' - ) - .replace( - /(\*|_)(?=\S)([^\r]*?\S)\1/g, - '$2' - ) - .replace( - /(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, - function(match, all, text, id, url, rest, quote, title) { - return '' + text + ''; - } - ) - .replace( - /<((https?|ftp|dict):[^'">\s]+)>/gi, - '$1' - ) - .replace( - /<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi, - function(match, mail) { - return Ox.encodeEmailAddress(mail); - } - ) - .replace(/\n\n/g, '

') - .replace( - new RegExp(salt.join('(\\d+)'), 'g'), - function(match, index) { - return array[parseInt(index)]; - } - ); -}; - -/*@ -Ox.sanitizeHTML Takes untrusted HTML and returns something trustworthy - > Ox.sanitizeHTML('http://foo.com, ...') - 'http://foo.com, ...' - > Ox.sanitizeHTML('http://foo.com/foo?bar&baz, ...') - 'http://foo.com/foo?bar&baz, ...' - > Ox.sanitizeHTML('(see: www.foo.com)') - '(see: www.foo.com)' - > Ox.sanitizeHTML('foo@bar.com') - 'foo@bar.com' - > Ox.sanitizeHTML('foo') - 'foo' - > Ox.sanitizeHTML('foo') - 'foo' - > Ox.sanitizeHTML('http://www.foo.com/') - 'http://www.foo.com/' - > Ox.sanitizeHTML('foo') - 'foo' - > Ox.sanitizeHTML('foo') - 'foo' - > Ox.sanitizeHTML('foo') - '<a href="javascript:alert()">foo</a>' - > Ox.sanitizeHTML('foo') - '<a href="foo">foo</a>' - > Ox.sanitizeHTML('foo') - 'foo' - > Ox.sanitizeHTML('foo') - 'foo' - > Ox.sanitizeHTML('[http://foo.com foo]') - 'foo' - > Ox.sanitizeHTML('
foo
') - '
foo
' - > Ox.sanitizeHTML('') - '<script>alert()</script>' - > Ox.sanitizeHTML('\'foo\' < \'bar\' && "foo" > "bar"') - '\'foo\' < \'bar\' && "foo" > "bar"' - > Ox.sanitizeHTML('foo') - 'foo' - > Ox.sanitizeHTML('foo') - 'foo' - > Ox.sanitizeHTML('&&') - '&&' - > Ox.sanitizeHTML('') - '<http://foo.com>' - > Ox.sanitizeHTML('') - '"<foo value="http://foo.com"></foo>"' -@*/ -export function sanitizeHTML(html, tags, globalAttributes) { - - tags = tags || defaultTags; - globalAttributes = globalAttributes || []; - - var escaped = {}, - level = 0, - matches = [], - selfClosingTags = ['img', 'br'], - validAttributes = {}, requiredAttributes = {}, validate = {}, - validTags = tags.map(function(tag) { - validAttributes[tag.name] = globalAttributes - .concat(tag.required || []) - .concat(tag.optional || []); - requiredAttributes[tag.name] = tag.required || []; - validate[tag.name] = tag.validate || {}; - return tag.name; - }); - - // html = Ox.clean(html); fixme: can this be a parameter? - if (Ox.contains(validTags, '[]')) { - html = html.replace( - /\[((\/|https?:\/\/|mailto:).+?) (.+?)\]/gi, - '$3' - ); - validTags = validTags.filter(function(tag) { - return tag != '[]'; - }); } - html = splitHTMLTags(html).map(function(string, i) { + function decodeHTMLEntities(string) { + return string + .replace( + new RegExp('(' + Ox.values(htmlEntities).join('|') + ')', 'g'), + function(match) { + return Ox.keyOf(htmlEntities, match); + } + ) + .replace( + /&#([0-9A-FX]+);/gi, + function(match, code) { + return Ox.char( + /^X/i.test(code) + ? parseInt(code.slice(1), 16) + : parseInt(code, 10) + ); + } + ); + } - var attrs = {}, - attrMatch, - attrRegexp = /([^=\ ]+)="([^"]+)"/g, - attrString, - isClosing, - isTag = i % 2, - isValid = true, - tag, - tagMatch, - tagRegexp = /<(\/)?([^\ \/]+)(.*?)(\/)?>/g; + // Splits a string into text (even indices) and tags (odd indices), ignoring + // tags with starting positions that are included in the ignore array + function splitHTMLTags(string, ignore) { + var isTag = false, ret = ['']; + ignore = ignore || []; + Ox.forEach(string, function(char, i) { + if (!isTag && char == '<' && ignore.indexOf(i) == -1) { + isTag = true; + ret.push(''); + } + ret[ret.length - 1] += char; + if (isTag && char == '>') { + isTag = false; + ret.push(''); + } + }); + return ret; + } - if (isTag) { - tagMatch = tagRegexp.exec(string); - if (tagMatch) { - isClosing = !Ox.isUndefined(tagMatch[1]); - tag = tagMatch[2]; - attrString = tagMatch[3].trim(); - while (attrMatch = attrRegexp.exec(attrString)) { - if ( - validAttributes[tag] - && Ox.contains(validAttributes[tag], attrMatch[1]) - ) { - attrs[attrMatch[1]] = attrMatch[2]; + /*@ + Ox.addLinks Takes a string and adds links for e-mail addresses and URLs + (string[, isHTML]) -> Formatted string + string String + isHTML If true, ignore matches in tags or enclosed by links + > Ox.addLinks('foo bar ') + 'foo bar <foo@bar.com>' + > Ox.addLinks('www.foo.com/bar#baz, etc.') + 'www.foo.com/bar#baz, etc.' + > Ox.addLinks('www.foo.com', true) + 'www.foo.com' + @*/ + Ox.addLinks = function(string, isHTML) { + var isLink = false; + return isHTML + ? splitHTMLTags(string).map(function(string, i) { + var isTag = i % 2; + if (isTag) { + if (/^'; - } + return isTag || isLink ? string : addLinks(string); + }).join('') + : Ox.normalizeHTML(addLinks(string)); + }; + + /*@ + Ox.encodeEmailAddress Returns obfuscated mailto: link + > Ox.encodeEmailAddress('mailto:foo@bar.com').indexOf(':') > -1 + true + @*/ + Ox.encodeEmailAddress = function(address, text) { + var parts = ['mailto:' + address, text || address].map(function(part) { + return Ox.map(part, function(char) { + var code = char.charCodeAt(0); + return char == ':' ? ':' + : '&#' + + (Math.random() < 0.5 ? code : 'x' + code.toString(16)) + + ';'; + }); + }); + return '' + parts[1] + ''; + }; + + /*@ + Ox.encodeHTMLEntities Encodes HTML entities + (string[, encodeAll]) -> String + string String + encodeAll If true, encode characters > 127 as numeric entities + > Ox.encodeHTMLEntities('<\'&"> äbçdê') + '<'&"> äbçdê' + > Ox.encodeHTMLEntities('<\'&"> äbçdê', true) + '<'&"> äbçdê' + @*/ + Ox.encodeHTMLEntities = function(string, encodeAll) { + return Ox.map(String(string), function(char) { + var code = char.charCodeAt(0); + if (code < 128) { + char = char in htmlEntities ? htmlEntities[char] : char; + } else if (encodeAll) { + char = '&#x' + + Ox.pad(code.toString(16).toUpperCase(), 'left', 4, '0') + + ';'; } + return char; + }); + }; + + /*@ + Ox.decodeHTMLEntities Decodes HTML entities + (string[, decodeAll]) -> String + string String + decodeAll If true, decode named entities for characters > 127 + Note that `decodeAll` relies on `Ox.normalizeHTML`, which uses the + DOM and may transform the string + > Ox.decodeHTMLEntities('<'&">') + '<\'&">' + > Ox.decodeHTMLEntities('<'&">') + '<\'&">' + > Ox.decodeHTMLEntities('äbçdê') + 'äbçdê' + > Ox.decodeHTMLEntities('äbçdê') + 'äbçdê' + > Ox.decodeHTMLEntities('äbçdê', true) + 'äbçdê' + > Ox.decodeHTMLEntities('β') + 'β' + > Ox.decodeHTMLEntities('β', true) + 'β' + > Ox.decodeHTMLEntities('<b>') + '' + @*/ + Ox.decodeHTMLEntities = function(string, decodeAll) { + return decodeAll + ? Ox.decodeHTMLEntities(Ox.normalizeHTML(string)) + : decodeHTMLEntities(string); + }; + + /*@ + Ox.highlight Highlight matches in string + (string, query, classname[, isHTML]) -> Output string + string Input string + query Case-insentitive query string, or regular expression + classname Class name for matches + isHTML If true, the input string is treated as HTML + > Ox.highlight('', 'foo', 'c') + '<foo><bar>' + > Ox.highlight('&', '&', 'c') + '&amp;' + > Ox.highlight('&', '&', 'c') + '&' + > Ox.highlight('<foo> <foo>', '', 'c', true) + '<foo> <foo>' + > Ox.highlight('name', 'name', 'c', true) + 'name' + > Ox.highlight('amp & amp', 'amp', 'c', true) + 'amp & amp' + > Ox.highlight('amp & amp', 'amp & amp', 'c', true) + 'amp & amp' + > Ox.highlight('<b>', '', 'c', true) + '<b>' + > Ox.highlight('<b>', '<b>', 'c', true) + '<b>' + > Ox.highlight('foobarbaz', 'foobar', 'c', true) + 'foobarbaz' + > Ox.highlight('foo

bar

baz', 'foobar', 'c', true) + 'foo

bar

baz' + > Ox.highlight('foo
bar baz', 'foo bar', 'c', true) + 'foo
bar
baz' + @*/ + Ox.highlight = function(string, query, classname, isHTML) { + if (!query) { + return string; + } + var cursor = 0, + entities = [], + matches = [], + offset = 0, + re = Ox.isRegExp(query) ? query + : new RegExp(Ox.escapeRegExp(query), 'gi'), + span = ['', ''], + tags = []; + function insert(array) { + // for each replacement + array.forEach(function(v) { + // replace the modified value with the original value + string = Ox.splice(string, v.position, v.length, v.value); + // for each match + matches.forEach(function(match) { + if (v.position < match.position) { + // replacement is before match, update match position + match.position += v.value.length - v.length; + } else if ( + v.position < match.position + match.value.length + ) { + // replacement is inside match, update match value + match.value = Ox.splice( + match.value, v.position - match.position, v.length, + v.value + ); + } + }); + }); + } + if (isHTML && regexp.html.test(string)) { + string = string // Ox.normalizeHTML(string) + // remove inline tags + .replace(regexp.tag, function(value, tag, position) { + tags.push({ + length: 0, position: position, value: value + }); + return ''; + }) + // decode html entities + .replace(regexp.entity, function(value, position) { + var ret = Ox.decodeHTMLEntities(value, true); + entities.push({ + length: ret.length, position: position, value: value + }); + return ret; + }); + // if decoding entities has created new tags, ignore them + splitHTMLTags(string, entities.map(function(entity) { + var ret = entity.position + offset; + offset += entity.length - entity.value.length; + return ret; + })).forEach(function(v, i) { + if (i % 2 == 0) { + // outside tags, find matches and save position and value + v.replace(re, function(value, position) { + matches.push( + {position: cursor + position, value: value} + ); + }); + } + cursor += v.length; + }); + insert(entities); + insert(tags); + // for each match (in reverse order, so that positions are correct) + matches.reverse().forEach(function(match) { + // wrap it in a span + string = Ox.splice( + string, match.position, match.value.length, + span.join(match.value) + ); + }); + // we may have enclosed single opening or closing tags in a span + if (matches.length && tags.length) { + string = Ox.normalizeHTML(string); + } + } else { + string = Ox.encodeHTMLEntities( + string.replace(re, function(value) { + matches.push(span.join(Ox.encodeHTMLEntities(value))); + return salt.join(matches.length - 1); + }) + ); + matches.forEach(function(match, i) { + string = string.replace(new RegExp(salt.join(i)), match); + }); + } + return string; + }; + + /*@ + Ox.normalizeHTML Normalize HTML (using the DOM) + > Ox.normalizeHTML('foo') + 'foo' + > Ox.normalizeHTML('foo') + 'foo' + > Ox.normalizeHTML('<'&"> äbçdê') + '<\'&"> äbçdê' + @*/ + Ox.normalizeHTML = function(html) { + return regexp.html.test(html) ? Ox.$('
').html(html).html() : html; + }; + + /*@ + Ox.parseMarkdown Parses (a tiny subset of) Markdown. + Supports `*emphasis*`, `_emphasis_`, `**strong**`, `__strong__`, + `` `code` ``, ``` ``code with backtick (`)`` ```, + ```` ```classname\ngithub-style\ncode blocks\n``` ````, + ``, `` and + `[text](http://example.com "title")`. + > Ox.parseMarkdown('*foo* **bar** `baz` ``back`tick``') + 'foo bar baz back`tick' + > Ox.parseMarkdown('foo\n\nbar\n\nbaz') + 'foo

bar

baz' + > Ox.parseMarkdown('```foo\n\nbar\n\nbaz\n```') + '
bar\n\nbaz\n
' + > Ox.parseMarkdown('') + 'http://example.com' + > Ox.parseMarkdown('``') + '<http://example.com>' + > Ox.parseMarkdown('[example](http://example.com "example.com")') + 'example' + > Ox.parseMarkdown('[example](http://example.com?foo=bar&bar=baz)') + 'example' + > Ox(Ox.parseMarkdown('')).startsWith('mail@example.com' + */ + Ox.parseMarkdown = function(string) { + // see https://github.com/coreyti/showdown/blob/master/src/showdown.js + var array = []; + return string.replace(/\r\n/g, '\n').replace(/\r/g, '\n') + .replace( + /(?:^|\n)```(.*)\n([^`]+)\n```/g, + function(match, classname, code) { + array.push( + '
'
+                        + code.trim().replace(/
' + ); + return salt.join(array.length - 1); + } + ) + .replace( + /(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm, + function(match, prev, backticks, code, next) { + array.push( + prev + '' + + code.trim().replace(/' + ); + return salt.join(array.length - 1); + } + ) + .replace( + /(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, + '$2' + ) + .replace( + /(\*|_)(?=\S)([^\r]*?\S)\1/g, + '$2' + ) + .replace( + /(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, + function(match, all, text, id, url, rest, quote, title) { + return '' + text + ''; + } + ) + .replace( + /<((https?|ftp|dict):[^'">\s]+)>/gi, + '$1' + ) + .replace( + /<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi, + function(match, mail) { + return Ox.encodeEmailAddress(mail); + } + ) + .replace(/\n\n/g, '

') + .replace( + new RegExp(salt.join('(\\d+)'), 'g'), + function(match, index) { + return array[parseInt(index)]; + } + ); + }; + + /*@ + Ox.sanitizeHTML Takes untrusted HTML and returns something trustworthy + > Ox.sanitizeHTML('http://foo.com, ...') + 'http://foo.com, ...' + > Ox.sanitizeHTML('http://foo.com/foo?bar&baz, ...') + 'http://foo.com/foo?bar&baz, ...' + > Ox.sanitizeHTML('(see: www.foo.com)') + '(see: www.foo.com)' + > Ox.sanitizeHTML('foo@bar.com') + 'foo@bar.com' + > Ox.sanitizeHTML('foo') + 'foo' + > Ox.sanitizeHTML('foo') + 'foo' + > Ox.sanitizeHTML('http://www.foo.com/') + 'http://www.foo.com/' + > Ox.sanitizeHTML('foo') + 'foo' + > Ox.sanitizeHTML('foo') + 'foo' + > Ox.sanitizeHTML('foo') + '<a href="javascript:alert()">foo</a>' + > Ox.sanitizeHTML('foo') + '<a href="foo">foo</a>' + > Ox.sanitizeHTML('foo') + 'foo' + > Ox.sanitizeHTML('foo') + 'foo' + > Ox.sanitizeHTML('[http://foo.com foo]') + 'foo' + > Ox.sanitizeHTML('
foo
') + '
foo
' + > Ox.sanitizeHTML('') + '<script>alert()</script>' + > Ox.sanitizeHTML('\'foo\' < \'bar\' && "foo" > "bar"') + '\'foo\' < \'bar\' && "foo" > "bar"' + > Ox.sanitizeHTML('foo') + 'foo' + > Ox.sanitizeHTML('foo') + 'foo' + > Ox.sanitizeHTML('&&') + '&&' + > Ox.sanitizeHTML('') + '<http://foo.com>' + > Ox.sanitizeHTML('') + '"<foo value="http://foo.com"></foo>"' + @*/ + Ox.sanitizeHTML = function(html, tags, globalAttributes) { + + tags = tags || defaultTags; + globalAttributes = globalAttributes || []; + + var escaped = {}, + level = 0, + matches = [], + selfClosingTags = ['img', 'br'], + validAttributes = {}, requiredAttributes = {}, validate = {}, + validTags = tags.map(function(tag) { + validAttributes[tag.name] = globalAttributes + .concat(tag.required || []) + .concat(tag.optional || []); + requiredAttributes[tag.name] = tag.required || []; + validate[tag.name] = tag.validate || {}; + return tag.name; + }); + + // html = Ox.clean(html); fixme: can this be a parameter? + if (Ox.contains(validTags, '[]')) { + html = html.replace( + /\[((\/|https?:\/\/|mailto:).+?) (.+?)\]/gi, + '$3' + ); + validTags = validTags.filter(function(tag) { + return tag != '[]'; + }); } - return Ox.encodeHTMLEntities(Ox.decodeHTMLEntities(string)); + html = splitHTMLTags(html).map(function(string, i) { - }).join(''); + var attrs = {}, + attrMatch, + attrRegexp = /([^=\ ]+)="([^"]+)"/g, + attrString, + isClosing, + isTag = i % 2, + isValid = true, + tag, + tagMatch, + tagRegexp = /<(\/)?([^\ \/]+)(.*?)(\/)?>/g; - //FIXME: dont add links to urls inside of escaped tags - html = addLinks(html, true); - html = html.replace(/\n\n/g, '

'); - // Close extra opening and remove extra closing tags. - // Note: this converts ''' to "'" and '"' to '"' - return normalizeHTML(html); + if (isTag) { + tagMatch = tagRegexp.exec(string); + if (tagMatch) { + isClosing = !Ox.isUndefined(tagMatch[1]); + tag = tagMatch[2]; + attrString = tagMatch[3].trim(); + while (attrMatch = attrRegexp.exec(attrString)) { + if ( + validAttributes[tag] + && Ox.contains(validAttributes[tag], attrMatch[1]) + ) { + attrs[attrMatch[1]] = attrMatch[2]; + } + } + if (!isClosing && !Ox.contains(selfClosingTags, tag)) { + level++; + } + if ( + !Ox.contains(validTags, tag) + || (attrString.length && Ox.isEmpty(attrs)) + ) { + isValid = false; + } else if (!isClosing && requiredAttributes[tag]) { + requiredAttributes[tag].forEach(function(attr) { + if (Ox.isUndefined(attrs[attr])) { + isValid = false; + } + }); + } + if (isValid && !Ox.isEmpty(attrs)) { + Ox.forEach(attrs, function(value, key) { + if ( + !Ox.isUndefined(validate[tag][key]) + && !validate[tag][key].exec(value) + ) { + isValid = false; + return false; // break + } + }); + } + if (isValid && isClosing) { + isValid = !escaped[level]; + } else { + escaped[level] = !isValid; + } + if (isClosing) { + level--; + } + if (isValid) { + return '<' + + (isClosing ? '/' : '') + + tag + + (!isClosing && !Ox.isEmpty(attrs) + ? ' ' + Ox.values(Ox.map(attrs, function(value, key) { + return key + '="' + value + '"'; + })).join(' ') + : '') + + '>'; + } + } + } -}; + return Ox.encodeHTMLEntities(Ox.decodeHTMLEntities(string)); -/*@ -Ox.stripTags Strips HTML tags from a string - > Ox.stripTags('foo') - 'foo' -@*/ -export function stripTags(string) { - return string.replace(/<.*?>/g, ''); -}; + }).join(''); + //FIXME: dont add links to urls inside of escaped tags + html = Ox.addLinks(html, true); + html = html.replace(/\n\n/g, '

'); + // Close extra opening and remove extra closing tags. + // Note: this converts ''' to "'" and '"' to '"' + return Ox.normalizeHTML(html); + + }; + + /*@ + Ox.stripTags Strips HTML tags from a string + > Ox.stripTags('foo') + 'foo' + @*/ + Ox.stripTags = function(string) { + return string.replace(/<.*?>/g, ''); + }; + +}()); diff --git a/source/Ox/js/Hash.js b/source/Ox/js/Hash.js index 11f5f41b..7471604d 100644 --- a/source/Ox/js/Hash.js +++ b/source/Ox/js/Hash.js @@ -1,21 +1,9 @@ 'use strict'; -import * as OxObject from './Object.js'; -import * as OxConstants from './Constants.js'; -import * as OxMath from './Math.js'; - -const Ox = {}; - -Object.assign(Ox, - OxObject, - OxConstants, - OxMath -); - /*@ Ox.oshash Calculates oshash for a given file or blob object. Async. @*/ -export function oshash(file, callback) { +Ox.oshash = function(file, callback) { // Needs to go via string to work for files > 2GB var hash = fromString(file.size.toString()); @@ -112,7 +100,7 @@ export function oshash(file, callback) { /*@ Ox.SHA1 Calculates SHA1 hash of the given string @*/ -export function SHA1(msg) { +Ox.SHA1 = function(msg) { function rotate_left(n,s) { var t4 = ( n<>>(32-s)); diff --git a/source/Ox/js/JavaScript.js b/source/Ox/js/JavaScript.js index 2a6fea52..56514b14 100644 --- a/source/Ox/js/JavaScript.js +++ b/source/Ox/js/JavaScript.js @@ -1,21 +1,5 @@ 'use strict'; -import * as OxArray from './Array.js'; -import * as OxObject from './Object.js'; -import * as OxConstants from './Constants.js'; -import * as OxMath from './Math.js'; -import * as OxString from './String.js'; - -const Ox = {}; - -Object.assign(Ox, - OxArray, - OxObject, - OxConstants, - OxMath, - OxString, -); - /*@ Ox.doc Generates documentation for annotated JavaScript (source) -> <[o]> Array of doc objects diff --git a/source/Ox/js/Locale.js b/source/Ox/js/Locale.js index 76c4e4b4..324f8646 100644 --- a/source/Ox/js/Locale.js +++ b/source/Ox/js/Locale.js @@ -1,103 +1,90 @@ 'use strict'; -import * as OxArray from './Array.js'; -import * as OxObject from './Object.js'; -import * as OxConstants from './Constants.js'; -import * as OxMath from './Math.js'; -import * as OxString from './String.js'; -const Ox = {}; +(function() { -Object.assign(Ox, - OxArray, - OxObject, - OxConstants, - OxMath, - OxString, -); + var log, translations = {}; + /*@ + Ox.getLocale Returns locale + () -> Locale (like 'de' or 'fr') + @*/ + Ox.getLocale = function() { + return Ox.LOCALE; + }; -var log, translations = {}; - -/*@ -Ox.getLocale Returns locale - () -> Locale (like 'de' or 'fr') -@*/ -export function getLocale() { - return Ox.LOCALE; -}; - -/*@ -Ox.setLocale Sets locale - (locale[, url], callback) - locale Locale (like 'de' or 'fr') - url one or more URLs of JSON file with additional translations - callback Callback function - success If true, locale has been set -@*/ -export function setLocale(locale, url, callback) { - var isValidLocale = Ox.contains(Object.keys(Ox.LOCALE_NAMES), locale), - urls = []; - if (arguments.length == 2) { - callback = arguments[1]; - url = null; - } - if (isValidLocale) { - Ox.LOCALE = locale; - if (locale == 'en') { - translations = {}; - } else { - translations = {}; - Ox.forEach(Ox.LOCALES, function(locales, module) { - if ( - (module == 'Ox' || Ox.load[module]) - && Ox.contains(locales, locale) - ) { - urls.push([ - Ox.PATH + module + '/json/locale.' - + locale + '.json' - ]); - } - }); + /*@ + Ox.setLocale Sets locale + (locale[, url], callback) + locale Locale (like 'de' or 'fr') + url one or more URLs of JSON file with additional translations + callback Callback function + success If true, locale has been set + @*/ + Ox.setLocale = function(locale, url, callback) { + var isValidLocale = Ox.contains(Object.keys(Ox.LOCALE_NAMES), locale), + urls = []; + if (arguments.length == 2) { + callback = arguments[1]; + url = null; } - url && Ox.makeArray(url).forEach(function(value) { - urls.push(Ox.makeArray(value)); - }); - if (urls.length) { - Ox.getJSON(urls, function(data) { - Ox.forEach(data, function(values, url) { - Ox.extend(translations, values); + if (isValidLocale) { + Ox.LOCALE = locale; + if (locale == 'en') { + translations = {}; + } else { + translations = {}; + Ox.forEach(Ox.LOCALES, function(locales, module) { + if ( + (module == 'Ox' || Ox.load[module]) + && Ox.contains(locales, locale) + ) { + urls.push([ + Ox.PATH + module + '/json/locale.' + + locale + '.json' + ]); + } }); + } + url && Ox.makeArray(url).forEach(function(value) { + urls.push(Ox.makeArray(value)); + }); + if (urls.length) { + Ox.getJSON(urls, function(data) { + Ox.forEach(data, function(values, url) { + Ox.extend(translations, values); + }); + callback(true); + }) + } else { callback(true); - }) + } } else { - callback(true); + callback(false); } - } else { - callback(false); - } -}; + }; -/*@ -Ox._ Localizes a string - (string[, options]) -> Localized string - string English string - options Options passed to Ox.formatString -@*/ -export function _(value, options) { - var translation = translations[value]; - log && log(value, translation); - translation = translation || value || ''; - return Ox.formatString(translation, options); -}; + /*@ + Ox._ Localizes a string + (string[, options]) -> Localized string + string English string + options Options passed to Ox.formatString + @*/ + Ox._ = function(value, options) { + var translation = translations[value]; + log && log(value, translation); + translation = translation || value || ''; + return Ox.formatString(translation, options); + }; -/*@ -Ox._.log Registers a logging function - (callback) -> undefined - callback Callback function - english English string - translation Translated string -@*/ -_.log = function(callback) { - log = callback; -}; + /*@ + Ox._.log Registers a logging function + (callback) -> undefined + callback Callback function + english English string + translation Translated string + @*/ + Ox._.log = function(callback) { + log = callback; + }; +})(); diff --git a/source/Ox/js/Math.js b/source/Ox/js/Math.js index cb7fde2b..9e933c04 100644 --- a/source/Ox/js/Math.js +++ b/source/Ox/js/Math.js @@ -1,20 +1,12 @@ 'use strict'; -import * as OxArray from './Array.js'; - -const Ox = {}; - -Object.assign(Ox, - OxArray, -); - /*@ Ox.acosh Inverse hyperbolic cosine Missing from `Math`. > Ox.acosh(1) 0 @*/ -export function acosh(x) { +Ox.acosh = function(x) { return Math.log(x + Math.sqrt(x * x - 1)); }; @@ -24,7 +16,7 @@ Ox.asinh Inverse hyperbolic sine > Ox.asinh(0) 0 @*/ -export function asinh(x) { +Ox.asinh = function(x) { return Math.log(x + Math.sqrt(x * x + 1)); }; @@ -34,7 +26,7 @@ Ox.atanh Inverse hyperbolic tangent > Ox.atanh(0) 0 @*/ -export function atanh(x) { +Ox.atanh = function(x) { return 0.5 * Math.log((1 + x) / (1 - x)); }; @@ -44,7 +36,7 @@ Ox.cosh Hyperbolic cosine > Ox.cosh(0) 1 @*/ -export function cosh(x) { +Ox.cosh = function(x) { return (Math.exp(x) + Math.exp(-x)) / 2; }; @@ -54,7 +46,7 @@ Ox.deg Takes radians, returns degrees > Ox.deg(2 * Math.PI) 360 @*/ -export function deg(rad) { +Ox.deg = function(rad) { return rad * 180 / Math.PI; }; @@ -66,7 +58,7 @@ Ox.hypot Returns the square root of the sum of the squares of its arguments > Ox.hypot(1, 1, 1) Math.sqrt(3) @*/ -export function hypot() { +Ox.hypot = function() { return Math.sqrt(Ox.slice(arguments).reduce(function(sum, number) { return sum + number * number; }, 0)); @@ -91,7 +83,7 @@ Ox.limit Limits a number by a given mininum and maximum > Ox.limit(-1, -2) -2 @*/ -export function limit(/*number[[, min], max]*/) { +Ox.limit = function(/*number[[, min], max]*/) { var number = arguments[0], min = arguments.length == 3 ? arguments[1] : -Infinity, max = arguments[arguments.length - 1]; @@ -106,7 +98,7 @@ Ox.log Returns the logarithm of a given number to a given base > Ox.log(Math.E) 1 @*/ -export function log(number, base) { +Ox.log = function(number, base) { return Math.log(number) / Math.log(base || Math.E); }; @@ -118,7 +110,7 @@ Ox.mod Modulo function > Ox.mod(-11, 10) 9 @*/ -export function mod(number, by) { +Ox.mod = function(number, by) { return (number % by + by) % by; }; @@ -128,7 +120,7 @@ Ox.rad Takes degrees, returns radians > Ox.rad(360) 2 * Math.PI @*/ -export function rad(deg) { +Ox.rad = function(deg) { return deg * Math.PI / 180; }; @@ -144,7 +136,7 @@ Ox.random Returns a random integer within a given range > Ox.random(1, 2) == 1 true @*/ -export function random() { +Ox.random = function() { var min = arguments.length == 2 ? arguments[0] : 0, max = arguments.length ? Ox.last(arguments) : 2; return min + Math.floor(Math.random() * (max - min)); @@ -159,7 +151,7 @@ Ox.round Rounds a number with a given number of decimals > Ox.round(1 / 2) 1 @*/ -export function round(number, decimals) { +Ox.round = function(number, decimals) { var pow = Math.pow(10, decimals || 0); return Math.round(number * pow) / pow; }; @@ -177,7 +169,7 @@ Ox.sign Returns the sign of a number (-1, 0 or 1) > Ox.sign(Infinity) 1 @*/ -export function sign(x) { +Ox.sign = function(x) { x = +x; return x !== x || x === 0 ? x : x < 0 ? -1 : 1; }; @@ -188,7 +180,7 @@ Ox.sinh Hyperbolic sine > Ox.sinh(0) 0 @*/ -export function sinh(x) { +Ox.sinh = function(x) { return (Math.exp(x) - Math.exp(-x)) / 2; }; @@ -202,7 +194,7 @@ Ox.splitInt Splits an integer into an array of integers > Ox.splitInt(100, 6) [16, 16, 17, 17, 17, 17] @*/ -export function splitInt(number, by) { +Ox.splitInt = function(number, by) { var div = Math.floor(number / by), mod = number % by; return Ox.range(by).map(function(i) { @@ -216,7 +208,7 @@ Ox.tanh Hyperbolic tangent > Ox.tanh(0) 0 @*/ -export function tanh(x) { +Ox.tanh = function(x) { return (Math.exp(x) - Math.exp(-x)) / (Math.exp(x) + Math.exp(-x)); }; @@ -225,6 +217,6 @@ Ox.trunc Truncates a number > Ox.trunc(-1.5) -1 @*/ -export function trunc(x) { +Ox.trunc = function(x) { return ~~x; }; diff --git a/source/Ox/js/Object.js b/source/Ox/js/Object.js index b6c89bc8..0534d820 100644 --- a/source/Ox/js/Object.js +++ b/source/Ox/js/Object.js @@ -1,25 +1,5 @@ 'use strict'; -import * as OxCore from './Core.js'; -import * as OxFunction from './Function.js'; -import * as OxConstants from './Constants.js'; -import * as OxCollection from './Collection.js'; -import * as OxMath from './Math.js'; -import * as OxType from './Type.js'; -import * as OxArray from './Array.js'; - -const Ox = {}; - -Object.assign(Ox, - OxCore, - OxFunction, - OxConstants, - OxCollection, - OxMath, - OxType, - OxArray, -); - /*@ Ox.extend Extends an object with one or more other objects > Ox.extend({a: 1, b: 1, c: 1}, {b: 2, c: 2}, {c: 3}) @@ -29,10 +9,10 @@ Ox.extend Extends an object with one or more other objects > Ox.extend({a: 1}, 'b') {a: 1, b: void 0} @*/ -export function extend(object) { +Ox.extend = function(object) { var args = Ox.slice(arguments, 1); if (!Ox.isObject(args[0])) { - args = [makeObject(args)]; + args = [Ox.makeObject(args)]; } Ox.forEach(args, function(arg) { Ox.forEach(arg, function(value, key) { @@ -82,7 +62,7 @@ Ox.getset Generic getter and setter function > Ox.test.object.options({foo: "foo", bar: "bar"}).options() {"key": "val", "foo": "foo", "bar": "bar"} @*/ -export function getset(object, args, callback, that) { +Ox.getset = function(object, args, callback, that) { var object_ = Ox.clone(object), ret; if (args.length == 0) { // [] @@ -92,7 +72,7 @@ export function getset(object, args, callback, that) { ret = Ox.clone(object[args[0]]); } else { // [key, val] or [{key: val, ...}] - args = makeObject(args); + args = Ox.makeObject(args); object = Ox.extend(object, args); Ox.forEach(args, function(value, key) { if (!object_ || !Ox.isEqual(object_[key], value)) { @@ -104,7 +84,7 @@ export function getset(object, args, callback, that) { return ret; }; -export function hasOwn(object, value) { +Ox.hasOwn = function(object, value) { return Object.prototype.hasOwnProperty.call(object, value); }; @@ -113,7 +93,7 @@ Ox.keyOf Equivalent of [].indexOf for objects > Ox.keyOf({a: 1, b: 2, c: 3}, 1) 'a' @*/ -export function keyOf(object, value) { +Ox.keyOf = function(object, value) { var key; Ox.forEach(object, function(v, k) { if (v === value) { @@ -137,7 +117,7 @@ Ox.makeObject Takes an array and returns an object > (function() { return Ox.makeObject(arguments); }()) {} @*/ -export function makeObject(array) { +Ox.makeObject = function(array) { var ret = {}; if (Ox.isObject(array[0])) { // [{foo: 'bar'}] @@ -154,7 +134,7 @@ Ox.methods Returns a sorted list of all method names of an object > Ox.methods({a: [], b: false, f: function() {}, n: 0, o: {}, s: ''}) ['f'] @*/ -export function methods(object, includePrototype) { +Ox.methods = function(object, includePrototype) { var key, keys; if (includePrototype) { keys = []; @@ -178,7 +158,7 @@ Ox.serialize Parses an object into query parameters > Ox.serialize({a: [1, 2], b: true, n: 1, o: {k: 'v'}, s: 'foo'}, true) 'a=[1,2]&b=true&n=1&o={"k":"v"}&s="foo"' @*/ -export function serialize(object, isJSON) { +Ox.serialize = function(object, isJSON) { var ret = []; Ox.forEach(object, function(value, key) { var value; @@ -204,7 +184,7 @@ Ox.unserialize Parses query parameters into an object {a: [1, 2], b: true, n: 1.2, o: {k: 'v'}, s1: 'foo', s2: 'bar'} @*/ -export function unserialize(string, isJSON) { +Ox.unserialize = function(string, isJSON) { var ret = {}; Ox.filter(string.split('&')).forEach(function(value) { var array = value.split('='); @@ -228,7 +208,7 @@ Ox.zipObject Takes a keys and a values array, returns a new object > Ox.zipObject(['a', 'b'], [1, 2]) {a: 1, b: 2} @*/ -export function zipObject(keys, values) { +Ox.zipObject = function(keys, values) { var object = {}; keys = Ox.makeArray(keys); values = Ox.makeArray(values); diff --git a/source/Ox/js/Polyfill.js b/source/Ox/js/Polyfill.js index 85b42231..98cd94d3 100644 --- a/source/Ox/js/Polyfill.js +++ b/source/Ox/js/Polyfill.js @@ -1,6 +1,6 @@ 'use strict'; -export function loadPolyfill(window, Ox) { +(function(window) { var canDefineProperty = !!Object.defineProperty && (function() { try { @@ -426,4 +426,5 @@ export function loadPolyfill(window, Ox) { ); } } -} + +}(this)); diff --git a/source/Ox/js/RegExp.js b/source/Ox/js/RegExp.js index 6c5b0a02..ac636e2f 100644 --- a/source/Ox/js/RegExp.js +++ b/source/Ox/js/RegExp.js @@ -1,5 +1,3 @@ -'use strict'; - /*@ Ox.escapeRegExp Escapes a string for use in a regular expression (str) -> Escaped string @@ -10,6 +8,6 @@ Ox.escapeRegExp Escapes a string for use in a regular expression true @*/ // see https://developer.mozilla.org/en/JavaScript/Guide/Regular_Expressions -export function escapeRegExp(string) { +Ox.escapeRegExp = function(string) { return (string + '').replace(/([\/\\^$*+?.\-|(){}[\]])/g, '\\$1'); -}; +}; \ No newline at end of file diff --git a/source/Ox/js/Request.js b/source/Ox/js/Request.js index a8e10473..6ea7f530 100644 --- a/source/Ox/js/Request.js +++ b/source/Ox/js/Request.js @@ -1,25 +1,5 @@ 'use strict'; -import * as OxArray from './Array.js'; -import * as OxObject from './Object.js'; -import * as OxConstants from './Constants.js'; -import * as OxMath from './Math.js'; -import * as OxString from './String.js'; -import * as OxType from './Type.js'; -import * as OxCollection from './Collection.js'; - -const Ox = {}; - -Object.assign(Ox, - OxArray, - OxObject, - OxConstants, - OxMath, - OxString, - OxType, - OxCollection, -); - /*@ Ox.get Get a remote resource (url, callback) -> undefined @@ -30,7 +10,7 @@ Ox.get Get a remote resource code Status code text Status text @*/ -export function get(url, callback) { +Ox.get = function(url, callback) { var request = new XMLHttpRequest(); request.open('GET', url, true); request.onreadystatechange = function() { @@ -69,7 +49,7 @@ Ox.getAsync Runs an asynchonous loader for an array of URLs code Error code (like `404`) text Error text (like `'Not Found'`) @*/ -export function getAsync(urls, get, callback) { +Ox.getAsync = function(urls, get, callback) { urls = Ox.makeArray(urls); var errors = {}, i = 0, n = urls.length, results = {}; function done() { @@ -96,7 +76,7 @@ export function getAsync(urls, get, callback) { } function getSerial() { var url = urls.shift(); - getAsync(url, get, function(result, error) { + Ox.getAsync(url, get, function(result, error) { extend(results, result, url); extend(errors, error, url); urls.length ? getSerial() : done(); @@ -105,139 +85,142 @@ export function getAsync(urls, get, callback) { urls.some(Ox.isArray) ? getSerial() : getParallel(); }; +(function() { -var cache = {}, - head = document.head - || document.getElementsByTagName('head')[0] - || document.documentElement; + var cache = {}, + head = document.head + || document.getElementsByTagName('head')[0] + || document.documentElement; -function _getFile(type, url, callback) { - var element, tagValue, typeValue, urlKey; - if (!cache[url]) { - if (!type) { - type = Ox.parseURL(url).pathname.split('.').pop(); - type = type == 'css' ? 'stylesheet' - : type == 'js' ? 'script' : 'image'; - } - if (type == 'image') { - element = new Image(); - element.onerror = onError; - element.onload = onLoad; - element.src = url; - } else { - tagValue = type == 'script' ? 'script' : 'link'; - typeValue = type == 'script' ? 'text/javascript' : 'text/css'; - urlKey = type == 'script' ? 'src' : 'href'; - if (Ox.some( - document.getElementsByTagName(tagValue), - function(element) { - return element[urlKey] == url; - } - )) { - onLoad(); - } else { - element = document.createElement(tagValue); + function getFile(type, url, callback) { + var element, tagValue, typeValue, urlKey; + if (!cache[url]) { + if (!type) { + type = Ox.parseURL(url).pathname.split('.').pop(); + type = type == 'css' ? 'stylesheet' + : type == 'js' ? 'script' : 'image'; + } + if (type == 'image') { + element = new Image(); element.onerror = onError; - element.onload = element.onreadystatechange = onLoad; - element.type = typeValue; - element[urlKey] = url; - if (type == 'stylesheet') { - element.rel = 'stylesheet'; + element.onload = onLoad; + element.src = url; + } else { + tagValue = type == 'script' ? 'script' : 'link'; + typeValue = type == 'script' ? 'text/javascript' : 'text/css'; + urlKey = type == 'script' ? 'src' : 'href'; + if (Ox.some( + document.getElementsByTagName(tagValue), + function(element) { + return element[urlKey] == url; + } + )) { + onLoad(); + } else { + element = document.createElement(tagValue); + element.onerror = onError; + element.onload = element.onreadystatechange = onLoad; + element.type = typeValue; + element[urlKey] = url; + if (type == 'stylesheet') { + element.rel = 'stylesheet'; + } + head.appendChild(element); + } + if (type == 'stylesheet') { + //fixme only call if browser does not support onload + // Safari 5 does not fire onload + waitForCSS(); } - head.appendChild(element); } - if (type == 'stylesheet') { - //fixme only call if browser does not support onload - // Safari 5 does not fire onload - waitForCSS(); - } - } - } else { - callback(cache[url], null); - } - function onError() { - callback(null, {code: 404, text: 'Not Found'}); - } - function onLoad() { - if ( - !this || !this.readyState - || this.readyState == 'loaded' || this.readyState == 'complete' - ) { - // for an image, keep a reference to the element - // to keep the image in the browser cache - cache[url] = type == 'image' ? this : true; + } else { callback(cache[url], null); } - } - function waitForCSS() { - var error = false; - try { - element.sheet.cssRule; - } catch (e) { - error = true; - setTimeout(function() { - waitForCSS(); - }, 25); + function onError() { + callback(null, {code: 404, text: 'Not Found'}); + } + function onLoad() { + if ( + !this || !this.readyState + || this.readyState == 'loaded' || this.readyState == 'complete' + ) { + // for an image, keep a reference to the element + // to keep the image in the browser cache + cache[url] = type == 'image' ? this : true; + callback(cache[url], null); + } + } + function waitForCSS() { + var error = false; + try { + element.sheet.cssRule; + } catch (e) { + error = true; + setTimeout(function() { + waitForCSS(); + }, 25); + } + !error && onLoad(); } - !error && onLoad(); } -} -function getFiles(type, urls, callback) { - getAsync(urls, function(url, callback) { - _getFile(type, url, callback); - }, callback); -} + function getFiles(type, urls, callback) { + Ox.getAsync(urls, function(url, callback) { + getFile(type, url, callback); + }, callback); + } -/*@ -Ox.getFile Loads a file (image, script or stylesheet) - (file, callback) -> undefined - file Local path or remote URL, or array of those, or array of such arrays - Multiple files in the same array will be processed simultaneously, - but multiple arrays of files will be processed in that order. - callback Callback function - image DOM element (if the file is an image) -@*/ -export function getFile(url, callback) { - getFiles(null, url, callback); -}; + /*@ + Ox.getFile Loads a file (image, script or stylesheet) + (file, callback) -> undefined + file Local path or remote URL, or array of those, or array of such arrays + Multiple files in the same array will be processed simultaneously, + but multiple arrays of files will be processed in that order. + callback Callback function + image DOM element (if the file is an image) + @*/ + Ox.getFile = function(url, callback) { + getFiles(null, url, callback); + }; -/*@ -Ox.getImage Loads an image - (file, callback) -> undefined - file Local path or remote URL, or array of those, or array of such arrays - Multiple files in the same array will be processed simultaneously, - but multiple arrays of files will be processed in that order. - callback Callback function - image DOM element -@*/ -export function getImage(url, callback) { - getFiles('image', url, callback); -}; + /*@ + Ox.getImage Loads an image + (file, callback) -> undefined + file Local path or remote URL, or array of those, or array of such arrays + Multiple files in the same array will be processed simultaneously, + but multiple arrays of files will be processed in that order. + callback Callback function + image DOM element + @*/ + Ox.getImage = function(url, callback) { + getFiles('image', url, callback); + }; -/*@ -Ox.getScript Loads a script - (file, callback) -> undefined - file Local path or remote URL, or array of those, or array of such arrays - Multiple files in the same array will be processed simultaneously, - but multiple arrays of files will be processed in that order. - callback Callback function -@*/ -export function getScript(url, callback) { - getFiles('script', url, callback); -}; + /*@ + Ox.getScript Loads a script + (file, callback) -> undefined + file Local path or remote URL, or array of those, or array of such arrays + Multiple files in the same array will be processed simultaneously, + but multiple arrays of files will be processed in that order. + callback Callback function + @*/ + Ox.getScript = function(url, callback) { + getFiles('script', url, callback); + }; -/*@ -Ox.getStylesheet Loads a stylesheet - (file, callback) -> undefined - file Local path or remote URL, or array of those, or array of such arrays - Multiple files in the same array will be processed simultaneously, - but multiple arrays of files will be processed in that order. - callback Callback function -@*/ -export function getStylesheet(url, callback) { - getFiles('stylesheet', url, callback); -}; + /*@ + Ox.getStylesheet Loads a stylesheet + (file, callback) -> undefined + file Local path or remote URL, or array of those, or array of such arrays + Multiple files in the same array will be processed simultaneously, + but multiple arrays of files will be processed in that order. + callback Callback function + @*/ + Ox.getStylesheet = function(url, callback) { + getFiles('stylesheet', url, callback); + }; + +}()); /*@ Ox.getJSON Get and parse one or more remote JSON files @@ -251,10 +234,10 @@ Ox.getJSON Get and parse one or more remote JSON files code Error code (like `404`) text Error text (like `'Not Found'`) @*/ -export function getJSON(url, callback, isJSONC) { +Ox.getJSON = function(url, callback, isJSONC) { var urls = Ox.makeArray(url); - getAsync(urls, function(url, callback) { - get(url, function(data, error) { + Ox.getAsync(urls, function(url, callback) { + Ox.get(url, function(data, error) { callback(JSON.parse( isJSONC ? Ox.minify(data || '') : data ), error); @@ -275,7 +258,7 @@ Ox.getJSONC Get and parse a remote JSONC file code Error code (like `404`) text Error text (like `'Not Found'`) @*/ -export function getJSONC(url, callback) { +Ox.getJSONC = function(url, callback) { Ox.getJSON(url, callback, true); }; @@ -292,9 +275,9 @@ Ox.getJSONP Get and parse one or more remote JSONP files code Error code (like `404`) text Error text (like `'Not Found'`) @*/ -export function getJSONP(url, callback) { +Ox.getJSONP = function(url, callback) { var urls = Ox.makeArray(url); - getAsync(urls, function(url, callback) { + Ox.getAsync(urls, function(url, callback) { var id = 'callback' + Ox.uid(); Ox.getJSONP[id] = function(data) { delete Ox.getJSONP[id]; @@ -318,7 +301,7 @@ Ox.post post to a remote resource code Status code text Status text @*/ -export function post(url, data, callback) { +Ox.post = function(url, data, callback) { var request = new XMLHttpRequest(); request.open('post', url, true); request.onreadystatechange = function() { diff --git a/source/Ox/js/String.js b/source/Ox/js/String.js index 6bd7515d..3f89e93d 100644 --- a/source/Ox/js/String.js +++ b/source/Ox/js/String.js @@ -1,21 +1,9 @@ 'use strict'; -import * as OxCore from './Core.js'; -import * as OxBase from './Base.js'; -import * as OxCollection from './Collection.js'; - -const Ox = {}; - -Object.assign(Ox, - OxCore, - OxBase, - OxCollection, -); - /*@ Ox.char Alias for String.fromCharCode @*/ -export const char = String.fromCharCode; +Ox.char = String.fromCharCode; /*@ Ox.clean Remove leading, trailing and double whitespace from a string @@ -30,7 +18,7 @@ Ox.clean Remove leading, trailing and double whitespace from a string > Ox.clean(' foo\tbar ') 'foo bar' @*/ -export function clean(string) { +Ox.clean = function(string) { return Ox.filter(Ox.map(string.split('\n'), function(string) { return string.replace(/\s+/g, ' ').trim() || ''; })).join('\n'); @@ -42,7 +30,7 @@ Ox.codePointAt Returns the code point at a given index > Ox.codePointAt('\uD83D\uDCA9', 0) 0x1F4A9 @*/ -export function codePointAt(string, index) { +Ox.codePointAt = function(string, index) { var first, length = string.length, ret, second; if (index >= 0 && index < length) { first = string.charCodeAt(index); @@ -67,7 +55,7 @@ Ox.endsWith Tests if a string ends with a given substring > Ox.endsWith('foobar', 'foo') false @*/ -export function endsWith(string, substring) { +Ox.endsWith = function(string, substring) { string = string.toString(); substring = substring.toString(); return string.slice(string.length - substring.length) == substring; @@ -81,7 +69,7 @@ Ox.fromCodePoint Returns a string for one or more given code points > Ox.fromCodePoint(0x1F4A9) '\uD83D\uDCA9' @*/ -export function fromCodePoint() { +Ox.fromCodePoint = function() { var ret = ''; Ox.forEach(arguments, function(number) { if (number < 0 || number > 0x10FFFF || !Ox.isInt(number)) { @@ -111,7 +99,7 @@ Ox.isValidEmail Tests if a string is a valid e-mail address > Ox.isValidEmail('foo@bar..com') false @*/ -export function isValidEmail(string) { +Ox.isValidEmail = function(string) { return !!/^[0-9A-Z\.\+\-_]+@(?:[0-9A-Z\-]+\.)+[A-Z]{2,64}$/i.test(string); }; @@ -152,7 +140,7 @@ Ox.pad Pad a string to a given length > Ox.pad('foo', -1) '' @*/ -export function pad(string, position, length, padding) { +Ox.pad = function(string, position, length, padding) { var hasPosition = Ox.isString(arguments[1]), isNumber = Ox.isNumber(arguments[0]), last = Ox.last(arguments); @@ -178,7 +166,7 @@ Ox.parseDuration Takes a formatted duration, returns seconds > Ox.parseDuration('1::') 3600 @*/ -export function parseDuration(string) { +Ox.parseDuration = function(string) { return string.split(':').reverse().slice(0, 4).reduce(function(p, c, i) { return p + (parseFloat(c) || 0) * (i == 3 ? 86400 : Math.pow(60, i)); }, 0); @@ -199,7 +187,7 @@ Ox.parsePath Returns the components of a path > Ox.parsePath('.foo') {extension: '', filename: '.foo', pathname: ''} @*/ -export function parsePath(string) { +Ox.parsePath = function(string) { var matches = /^(.+\/)?(.+?(\..+)?)?$/.exec(string); return { pathname: matches[1] || '', @@ -218,7 +206,7 @@ Ox.parseSRT Parses an srt subtitle file > Ox.parseSRT('1\n01:02:00,000 --> 01:02:03,400\nHello World') [{'in': 3720, out: 3723.4, text: 'Hello World'}] @*/ -export function parseSRT(string, fps) { +Ox.parseSRT = function(string, fps) { return string.replace(/\r\n/g, '\n').trim().split('\n\n') .map(function(block) { var lines = block.split('\n'), points; @@ -276,35 +264,22 @@ Ox.parseURL Takes a URL, returns its components > Ox.parseURL('http://www.foo.com:8080/bar/index.html?a=0&b=1#c').search '?a=0&b=1' @*/ -export const parseURL = (function() { - const keys = [ - 'hash', 'host', 'hostname', 'origin', - 'pathname', 'port', 'protocol', 'search' - ]; - if (typeof document == 'undefined') { - return function(string) { - const a = new URL(string); - var ret = {}; - keys.forEach(function(key) { - ret[key] = a[key]; - }); - return ret; - } - } else { - var a = document.createElement('a'); - return function(string) { - var ret = {}; - a.href = string; - keys.forEach(function(key) { - ret[key] = a[key]; - }); - return ret; - }; - } +Ox.parseURL = (function() { + var a = document.createElement('a'), + keys = ['hash', 'host', 'hostname', 'origin', + 'pathname', 'port', 'protocol', 'search']; + return function(string) { + var ret = {}; + a.href = string; + keys.forEach(function(key) { + ret[key] = a[key]; + }); + return ret; + }; }()); // FIXME: can we get rid of this? -export function parseUserAgent(userAgent) { +Ox.parseUserAgent = function(userAgent) { var aliases = { browser: { 'Firefox': /(Fennec|Firebird|Iceweasel|Minefield|Namoroka|Phoenix|SeaMonkey|Shiretoko)/ @@ -465,7 +440,7 @@ Ox.repeat Repeat a value multiple times @*/ // FIXME: see https://github.com/paulmillr/es6-shim/blob/master/es6-shim.js // for a faster version -export function repeat(value, times) { +Ox.repeat = function(value, times) { var ret; if (Ox.isArray(value)) { ret = []; @@ -483,7 +458,7 @@ Ox.splice `[].splice` for strings, returns a new string > Ox.splice('12xxxxx89', 2, 5, 3, 4, 5, 6, 7) '123456789' @*/ -export function splice(string, index, remove) { +Ox.splice = function(string, index, remove) { var array = string.split(''); Array.prototype.splice.apply(array, Ox.slice(arguments, 1)); return array.join(''); @@ -499,7 +474,7 @@ Ox.startsWith Tests if a string ends with a given substring > Ox.startsWith('foobar', 'bar') false @*/ -export function startsWith(string, substring) { +Ox.startsWith = function(string, substring) { string = string.toString(); substring = substring.toString(); return string.slice(0, substring.length) == substring; @@ -514,7 +489,7 @@ Ox.toCamelCase Takes a string with '-', '/' or '_', returns a camelCase stri > Ox.toCamelCase('foo_bar_baz') 'fooBarBaz' @*/ -export function toCamelCase(string) { +Ox.toCamelCase = function(string) { return string.replace(/[\-\/_][a-z]/g, function(string) { return string[1].toUpperCase(); }); @@ -525,7 +500,7 @@ Ox.toDashes Takes a camelCase string, returns a string with dashes > Ox.toDashes('fooBarBaz') 'foo-bar-baz' @*/ -export function toDashes(string) { +Ox.toDashes = function(string) { return string.replace(/[A-Z]/g, function(string) { return '-' + string.toLowerCase(); }); @@ -536,7 +511,7 @@ Ox.toSlashes Takes a camelCase string, returns a string with slashes > Ox.toSlashes('fooBarBaz') 'foo/bar/baz' @*/ -export function toSlashes(string) { +Ox.toSlashes = function(string) { return string.replace(/[A-Z]/g, function(string) { return '/' + string.toLowerCase(); }); @@ -549,7 +524,7 @@ Ox.toTitleCase Returns a string with capitalized words > Ox.toTitleCase('Apple releases iPhone, IBM stock plummets') 'Apple Releases iPhone, IBM Stock Plummets' @*/ -export function toTitleCase(string) { +Ox.toTitleCase = function(string) { return string.split(' ').map(function(value) { var substring = value.slice(1), lowercase = substring.toLowerCase(); @@ -565,7 +540,7 @@ Ox.toUnderscores Takes a camelCase string, returns string with underscores > Ox.toUnderscores('fooBarBaz') 'foo_bar_baz' @*/ -export function toUnderscores(string) { +Ox.toUnderscores = function(string) { return string.replace(/[A-Z]/g, function(string) { return '_' + string.toLowerCase(); }); @@ -587,7 +562,7 @@ Ox.truncate Truncate a string to a given length > Ox.truncate('anticonstitutionellement', 'center', 16, '...') 'anticon...lement' @*/ -export function truncate(string, position, length, padding) { +Ox.truncate = function(string, position, length, padding) { var hasPosition = Ox.isString(arguments[1]), last = Ox.last(arguments); position = hasPosition ? arguments[1] : 'right'; length = hasPosition ? arguments[2] : arguments[1]; @@ -614,7 +589,7 @@ Ox.words Splits a string into words, removing punctuation > Ox.words('Let\'s "split" array-likes into key/value pairs--okay?') ['let\'s', 'split', 'array-likes', 'into', 'key', 'value', 'pairs', 'okay'] @*/ -export function words(string) { +Ox.words = function(string) { var array = string.toLowerCase().split(/\b/), length = array.length, startsWithWord = /\w/.test(array[0]); @@ -660,7 +635,7 @@ Ox.wordwrap Wrap a string at word boundaries > Ox.wordwrap('These are short words', 16, true) 'These are \nshort words' @*/ -export function wordwrap(string, length) { +Ox.wordwrap = function(string, length) { var balanced, lines, max, newline, words; string = String(string); length = length || 80; diff --git a/source/Ox/js/Type.js b/source/Ox/js/Type.js index efda598b..a3f7843b 100644 --- a/source/Ox/js/Type.js +++ b/source/Ox/js/Type.js @@ -1,27 +1,13 @@ 'use strict'; -import * as OxObject from './Object.js'; -import * as OxFunction from './Function.js'; -import * as OxConstants from './Constants.js'; -import * as OxMath from './Math.js'; - -const Ox = {}; - -Object.assign(Ox, - OxObject, - OxFunction, - OxConstants, - OxMath -); - /*@ Ox.checkType Throws a TypeError if a value is not of a given type (val, type) -> undefined val <*> Any value type Type, or array of types @*/ -export function checkType(value, type) { - if (!Ox.contains(Ox.makeArray(type), typeOf(value))) { +Ox.checkType = function(value, type) { + if (!Ox.contains(Ox.makeArray(type), Ox.typeOf(value))) { throw new TypeError(); } }; @@ -33,8 +19,8 @@ Ox.isArguments Tests if a value is an arguments "array" > Ox.isArguments((function() { return arguments; }())) true @*/ -export function isArguments(value) { - return typeOf(value) == 'arguments'; +Ox.isArguments = function(value) { + return Ox.typeOf(value) == 'arguments'; }; /*@ @@ -50,8 +36,8 @@ Ox.isArray Tests if a value is an array > Ox.isArray({0: 0, length: 1}) false @*/ -export function isArray(value) { - return typeOf(value) == 'array'; +Ox.isArray = function(value) { + return Ox.typeOf(value) == 'array'; }; /*@ @@ -61,8 +47,8 @@ Ox.isBoolean Tests if a value is boolean > Ox.isBoolean(false) true @*/ -export function isBoolean(value) { - return typeOf(value) == 'boolean'; +Ox.isBoolean = function(value) { + return Ox.typeOf(value) == 'boolean'; }; /*@ @@ -72,8 +58,8 @@ Ox.isDate Tests if a value is a date > Ox.isDate(new Date()) true @*/ -export function isDate(value) { - return typeOf(value) == 'date'; +Ox.isDate = function(value) { + return Ox.typeOf(value) == 'date'; }; /*@ @@ -83,7 +69,7 @@ Ox.isElement Tests if a value is a DOM element > Ox.isElement(document.createElement('a')) true @*/ -export function isElement(value) { +Ox.isElement = function(value) { return Ox.endsWith(Ox.typeOf(value), 'element'); }; @@ -97,8 +83,8 @@ Ox.isEmpty Tests if a value is an empty array, object or string true > Ox.isEmpty('') true - > Ox.isEmpty(document.getElementsByTagName('')) - true + > Ox.isEmpty(document.getElementsByTagName('')) + true > Ox.isEmpty(function() {}) false > Ox.isEmpty(false) @@ -110,7 +96,7 @@ Ox.isEmpty Tests if a value is an empty array, object or string > Ox.isEmpty() false @*/ -export function isEmpty(value) { +Ox.isEmpty = function(value) { return Ox.len(value) === 0; }; @@ -171,12 +157,12 @@ Ox.isEqual Tests if two values are equal > Ox.isEqual(void 0, void 0) true @*/ -export function isEqual(a, b) { - var ret = false, type = typeOf(a); +Ox.isEqual = function(a, b) { + var ret = false, type = Ox.typeOf(a); if (a === b) { // 0 === -0, but not equal ret = a !== 0 || 1 / a === 1 / b; - } else if (type == typeOf(b)) { + } else if (type == Ox.typeOf(b)) { // NaN !== NaN, but equal if (a == b || a !== a) { ret = true; @@ -207,8 +193,8 @@ Ox.isError Tests if a value is an error > Ox.isError(new Error()) true @*/ -export function isError(value) { - return typeOf(value) == 'error'; +Ox.isError = function(value) { + return Ox.typeOf(value) == 'error'; }; /*@ @@ -220,8 +206,8 @@ Ox.isFunction Tests if a value is a function > Ox.isFunction(new RegExp()) false @*/ -export function isFunction(value) { - return typeOf(value) == 'function'; +Ox.isFunction = function(value) { + return Ox.typeOf(value) == 'function'; }; /*@ @@ -235,8 +221,8 @@ Ox.isInfinity Tests if a value is positive or negative Infinity > Ox.isInfinity(NaN) false @*/ -export function isInfinity(value) { - return typeOf(value) == 'number' && !isFinite(value) && !Ox.isNaN(value); +Ox.isInfinity = function(value) { + return Ox.typeOf(value) == 'number' && !isFinite(value) && !Ox.isNaN(value); }; /*@ @@ -250,7 +236,7 @@ Ox.isInt Tests if a value is an integer > Ox.isInt(Infinity) false @*/ -export function isInt(value) { +Ox.isInt = function(value) { return isFinite(value) && value === Math.floor(value); }; @@ -261,7 +247,7 @@ Ox.isInvalidDate Tests if a value is an invalid date > Ox.isInvalidDate(new Date('foo')) true @*/ -export function isInvalidDate(value) { +Ox.isInvalidDate = function(value) { return Ox.isDate(value) && Ox.isNaN(value.getTime()); }; @@ -272,7 +258,7 @@ Ox.isNaN Tests if a value is `NaN` > Ox.isNaN(NaN) true @*/ -export function isNaN(value) { +Ox.isNaN = function(value) { return value !== value; }; @@ -282,8 +268,8 @@ Ox.isNodeList Tests if a value is a nodelist > Ox.isNodeList(document.getElementsByTagName('a')) true @*/ -export function isNodeList(value) { - return typeOf(value) == 'nodelist'; +Ox.isNodeList = function(value) { + return Ox.typeOf(value) == 'nodelist'; }; /*@ @@ -293,8 +279,8 @@ Ox.isNull Tests if a value is `null` > Ox.isNull(null) true @*/ -export function isNull(value) { - return typeOf(value) == 'null'; +Ox.isNull = function(value) { + return Ox.typeOf(value) == 'null'; }; /*@ @@ -310,8 +296,8 @@ Ox.isNumber Tests if a value is a number > Ox.isNumber(NaN) true @*/ -export function isNumber(value) { - return typeOf(value) == 'number'; +Ox.isNumber = function(value) { + return Ox.typeOf(value) == 'number'; }; /*@ @@ -329,8 +315,8 @@ Ox.isObject Tests if a value is a an object > Ox.isObject(/ /) false @*/ -export function isObject(value) { - return typeOf(value) == 'object'; +Ox.isObject = function(value) { + return Ox.typeOf(value) == 'object'; }; /*@ @@ -352,9 +338,9 @@ Ox.isPrimitive Tests if a value is a primitive > Ox.isPrimitive({}) false @*/ -export function isPrimitive(value) { +Ox.isPrimitive = function(value) { return Ox.contains( - ['boolean', 'null', 'number', 'string', 'undefined'], typeOf(value) + ['boolean', 'null', 'number', 'string', 'undefined'], Ox.typeOf(value) ); }; @@ -365,8 +351,8 @@ Ox.isRegExp Tests if a value is a regular expression > Ox.isRegExp(new RegExp()) true @*/ -export function isRegExp(value) { - return typeOf(value) == 'regexp'; +Ox.isRegExp = function(value) { + return Ox.typeOf(value) == 'regexp'; }; /*@ @@ -376,8 +362,8 @@ Ox.isString Tests if a value is a string > Ox.isString('') true @*/ -export function isString(value) { - return typeOf(value) == 'string'; +Ox.isString = function(value) { + return Ox.typeOf(value) == 'string'; }; /*@ @@ -387,8 +373,8 @@ Ox.isUndefined Tests if a value is undefined > Ox.isUndefined() true @*/ -export function isUndefined(value) { - return typeOf(value) == 'undefined'; +Ox.isUndefined = function(value) { + return Ox.typeOf(value) == 'undefined'; }; /*@ @@ -398,7 +384,7 @@ Ox.isValidDate Tests if a value is a valid date > Ox.isValidDate(new Date()) true @*/ -export function isValidDate(value) { +Ox.isValidDate = function(value) { return Ox.isDate(value) && !Ox.isNaN(value.getTime()); }; @@ -447,7 +433,7 @@ Ox.typeOf Returns the type of a value > Ox.typeOf(JSON) 'json' @*/ -export function typeOf(value) { +Ox.typeOf = function(value) { return Object.prototype.toString.call(value).slice(8, -1).toLowerCase(); }; // Firefox 3.6 returns 'Object' for arguments, @@ -456,13 +442,13 @@ export function typeOf(value) { // Mobile Safari returns 'DOMWindow' for null and undefined // Firefox 30+ returns 'window' for window if ( - typeOf((function() { return arguments; }())) != 'arguments' - || (typeof document !== 'undefined' && typeOf(document.getElementsByTagName('a')) != 'nodelist') - || typeof null != 'null' - || typeof window != 'global' - || typeOf() != 'undefined' + Ox.typeOf((function() { return arguments; }())) != 'arguments' + || Ox.typeOf(document.getElementsByTagName('a')) != 'nodelist' + || Ox.typeOf(null) != 'null' + || Ox.typeOf(window) != 'global' + || Ox.typeOf() != 'undefined' ) { - typeOf = function(value) { + Ox.typeOf = function(value) { var type = Object.prototype.toString.call( value ).slice(8, -1).toLowerCase(); @@ -470,7 +456,7 @@ if ( type = 'null'; } else if (value === void 0) { type = 'undefined'; - } else if (typeof window !== 'undefined' && value === window) { + } else if (value === window) { type = 'global'; } else if (type == 'object' && typeof value.callee == 'function') { type = 'arguments'; diff --git a/source/Ox/js/Video.js b/source/Ox/js/Video.js index 53b04ae6..a8f2252a 100644 --- a/source/Ox/js/Video.js +++ b/source/Ox/js/Video.js @@ -1,35 +1,11 @@ 'use strict'; -import * as OxCore from './Core.js'; -import * as OxType from './Type.js'; -import * as OxArray from './Array.js'; -import * as OxObject from './Object.js'; -import * as OxCollection from './Collection.js'; -import * as OxFunction from './Function.js'; -import * as OxConstants from './Constants.js'; -import * as OxMath from './Math.js'; -import * as OxString from './String.js'; - -const Ox = {}; - -Object.assign(Ox, - OxCore, - OxType, - OxArray, - OxObject, - OxFunction, - OxCollection, - OxConstants, - OxMath, - OxString, -); - /*@ Ox.getVideoFormat Get supported video format (formats) -> List of supported formats format First supported format form list @*/ -export function getVideoFormat(formats) { +Ox.getVideoFormat = function(formats) { var aliases = { mp4: 'h264', m4v: 'h264', diff --git a/source/UI/index.js b/source/UI/index.js deleted file mode 100644 index 2f39c960..00000000 --- a/source/UI/index.js +++ /dev/null @@ -1,548 +0,0 @@ -'use strict'; - -import * as AudioAudioElement from './js/Audio/AudioElement.js'; -import * as AudioAudioPlayer from './js/Audio/AudioPlayer.js'; -import * as BarBar from './js/Bar/Bar.js'; -import * as BarProgressbar from './js/Bar/Progressbar.js'; -import * as BarResizebar from './js/Bar/Resizebar.js'; -import * as BarTabbar from './js/Bar/Tabbar.js'; -import * as CalendarCalendarEditor from './js/Calendar/CalendarEditor.js'; -import * as CalendarCalendar from './js/Calendar/Calendar.js'; -import * as CodeDocPage from './js/Code/DocPage.js'; -import * as CodeDocPanel from './js/Code/DocPanel.js'; -import * as CodeExamplePage from './js/Code/ExamplePage.js'; -import * as CodeExamplePanel from './js/Code/ExamplePanel.js'; -import * as CodeSourceViewer from './js/Code/SourceViewer.js'; -import * as CodeSyntaxHighlighter from './js/Code/SyntaxHighlighter.js'; -import * as CoreAPI from './js/Core/API.js'; -import * as CoreApp from './js/Core/App.js'; -import * as CoreClipboard from './js/Core/Clipboard.js'; -import * as CoreContainer from './js/Core/Container.js'; -import * as CoreCookies from './js/Core/Cookies.js'; -import * as CoreElement from './js/Core/Element.js'; -import * as CoreEvent from './js/Core/Event.js'; -import * as CoreFocus from './js/Core/Focus.js'; -import * as CoreFullscreen from './js/Core/Fullscreen.js'; -import * as CoreGarbageCollection from './js/Core/GarbageCollection.js'; -import * as CoreHistory from './js/Core/History.js'; -import * as CoreLoadingIcon from './js/Core/LoadingIcon.js'; -import * as CoreLoadingScreen from './js/Core/LoadingScreen.js'; -import * as CoreRequest from './js/Core/Request.js'; -import * as CoreTheme from './js/Core/Theme.js'; -import * as CoreUI from './js/Core/UI.js'; -import * as CoreURL from './js/Core/URL.js'; -import * as FormArrayEditable from './js/Form/ArrayEditable.js'; -import * as FormArrayInput from './js/Form/ArrayInput.js'; -import * as FormButtonGroup from './js/Form/ButtonGroup.js'; -import * as FormButton from './js/Form/Button.js'; -import * as FormCheckboxGroup from './js/Form/CheckboxGroup.js'; -import * as FormCheckbox from './js/Form/Checkbox.js'; -import * as FormColorInput from './js/Form/ColorInput.js'; -import * as FormColorPicker from './js/Form/ColorPicker.js'; -import * as FormDateInput from './js/Form/DateInput.js'; -import * as FormDateTimeInput from './js/Form/DateTimeInput.js'; -import * as FormEditableContent from './js/Form/EditableContent.js'; -import * as FormEditable from './js/Form/Editable.js'; -import * as FormFileButton from './js/Form/FileButton.js'; -import * as FormFileInput from './js/Form/FileInput.js'; -import * as FormFilter from './js/Form/Filter.js'; -import * as FormFormElementGroup from './js/Form/FormElementGroup.js'; -import * as FormFormItem from './js/Form/FormItem.js'; -import * as FormForm from './js/Form/Form.js'; -import * as FormFormPanel from './js/Form/FormPanel.js'; -import * as FormInputGroup from './js/Form/InputGroup.js'; -import * as FormInput from './js/Form/Input.js'; -import * as FormInsertHTMLDialog from './js/Form/InsertHTMLDialog.js'; -import * as FormLabel from './js/Form/Label.js'; -import * as FormObjectArrayInput from './js/Form/ObjectArrayInput.js'; -import * as FormObjectInput from './js/Form/ObjectInput.js'; -import * as FormOptionGroup from './js/Form/OptionGroup.js'; -import * as FormPicker from './js/Form/Picker.js'; -import * as FormPlaceInput from './js/Form/PlaceInput.js'; -import * as FormPlacePicker from './js/Form/PlacePicker.js'; -import * as FormRange from './js/Form/Range.js'; -import * as FormSelectInput from './js/Form/SelectInput.js'; -import * as FormSelect from './js/Form/Select.js'; -import * as FormSpreadsheet from './js/Form/Spreadsheet.js'; -import * as FormTimeInput from './js/Form/TimeInput.js'; -import * as ImageImageElement from './js/Image/ImageElement.js'; -import * as ImageImageViewer from './js/Image/ImageViewer.js'; -import * as ListChart from './js/List/Chart.js'; -import * as ListColumnList from './js/List/ColumnList.js'; -import * as ListCustomList from './js/List/CustomList.js'; -import * as ListIconItem from './js/List/IconItem.js'; -import * as ListIconList from './js/List/IconList.js'; -import * as ListInfoList from './js/List/InfoList.js'; -import * as ListListItem from './js/List/ListItem.js'; -import * as ListList from './js/List/List.js'; -import * as ListSortList from './js/List/SortList.js'; -import * as ListTableList from './js/List/TableList.js'; -import * as ListTreeList from './js/List/TreeList.js'; -import * as MapMapEditor from './js/Map/MapEditor.js'; -import * as MapMapImage from './js/Map/MapImage.js'; -import * as MapMap from './js/Map/Map.js'; -import * as MapMapMarkerImage from './js/Map/MapMarkerImage.js'; -import * as MapMapMarker from './js/Map/MapMarker.js'; -import * as MapMapPlace from './js/Map/MapPlace.js'; -import * as MapMapRectangle from './js/Map/MapRectangle.js'; -import * as MapMapRectangleMarker from './js/Map/MapRectangleMarker.js'; -import * as MenuMainMenu from './js/Menu/MainMenu.js'; -import * as MenuMenuButton from './js/Menu/MenuButton.js'; -import * as MenuMenuItem from './js/Menu/MenuItem.js'; -import * as MenuMenu from './js/Menu/Menu.js'; -import * as PanelCollapsePanel from './js/Panel/CollapsePanel.js'; -import * as PanelSlidePanel from './js/Panel/SlidePanel.js'; -import * as PanelSplitPanel from './js/Panel/SplitPanel.js'; -import * as PanelTabPanel from './js/Panel/TabPanel.js'; -import * as VideoAnnotationFolder from './js/Video/AnnotationFolder.js'; -import * as VideoAnnotationPanel from './js/Video/AnnotationPanel.js'; -import * as VideoBlockVideoTimeline from './js/Video/BlockVideoTimeline.js'; -import * as VideoClipPanel from './js/Video/ClipPanel.js'; -import * as VideoLargeVideoTimeline from './js/Video/LargeVideoTimeline.js'; -import * as VideoSmallVideoTimelineImage from './js/Video/SmallVideoTimelineImage.js'; -import * as VideoSmallVideoTimeline from './js/Video/SmallVideoTimeline.js'; -import * as VideoVideoAnnotationPanel from './js/Video/VideoAnnotationPanel.js'; -import * as VideoVideoEditPanel from './js/Video/VideoEditPanel.js'; -import * as VideoVideoElement from './js/Video/VideoElement.js'; -import * as VideoVideoPlayer from './js/Video/VideoPlayer.js'; -import * as VideoVideoPlayerMenu from './js/Video/VideoPlayerMenu.js'; -import * as VideoVideoPlayerPanel from './js/Video/VideoPlayerPanel.js'; -import * as VideoVideoPreview from './js/Video/VideoPreview.js'; -import * as VideoVideoTimelinePanel from './js/Video/VideoTimelinePanel.js'; -import * as VideoVideoTimelinePlayer from './js/Video/VideoTimelinePlayer.js'; -import * as VideoYouTubeElement from './js/Video/YouTubeElement.js'; -import * as WindowDialog from './js/Window/Dialog.js'; -import * as WindowLayer from './js/Window/Layer.js'; -import * as WindowSortDialog from './js/Window/SortDialog.js'; -import * as WindowTooltip from './js/Window/Tooltip.js'; - - -const UI = {} - -Object.assign(UI, - AudioAudioElement, - AudioAudioPlayer, - BarBar, - BarProgressbar, - BarResizebar, - BarTabbar, - CalendarCalendarEditor, - CalendarCalendar, - CodeDocPage, - CodeDocPanel, - CodeExamplePage, - CodeExamplePanel, - CodeSourceViewer, - CodeSyntaxHighlighter, - CoreAPI, - CoreApp, - CoreClipboard, - CoreContainer, - CoreCookies, - CoreElement, - CoreEvent, - CoreFocus, - CoreFullscreen, - CoreGarbageCollection, - CoreHistory, - CoreLoadingIcon, - CoreLoadingScreen, - CoreRequest, - CoreTheme, - CoreUI, - CoreURL, - FormArrayEditable, - FormArrayInput, - FormButtonGroup, - FormButton, - FormCheckboxGroup, - FormCheckbox, - FormColorInput, - FormColorPicker, - FormDateInput, - FormDateTimeInput, - FormEditableContent, - FormEditable, - FormFileButton, - FormFileInput, - FormFilter, - FormFormElementGroup, - FormFormItem, - FormForm, - FormFormPanel, - FormInputGroup, - FormInput, - FormInsertHTMLDialog, - FormLabel, - FormObjectArrayInput, - FormObjectInput, - FormOptionGroup, - FormPicker, - FormPlaceInput, - FormPlacePicker, - FormRange, - FormSelectInput, - FormSelect, - FormSpreadsheet, - FormTimeInput, - ImageImageElement, - ImageImageViewer, - ListChart, - ListColumnList, - ListCustomList, - ListIconItem, - ListIconList, - ListInfoList, - ListListItem, - ListList, - ListSortList, - ListTableList, - ListTreeList, - MapMapEditor, - MapMapImage, - MapMap, - MapMapMarkerImage, - MapMapMarker, - MapMapPlace, - MapMapRectangle, - MapMapRectangleMarker, - MenuMainMenu, - MenuMenuButton, - MenuMenuItem, - MenuMenu, - PanelCollapsePanel, - PanelSlidePanel, - PanelSplitPanel, - PanelTabPanel, - VideoAnnotationFolder, - VideoAnnotationPanel, - VideoBlockVideoTimeline, - VideoClipPanel, - VideoLargeVideoTimeline, - VideoSmallVideoTimelineImage, - VideoSmallVideoTimeline, - VideoVideoAnnotationPanel, - VideoVideoEditPanel, - VideoVideoElement, - VideoVideoPlayer, - VideoVideoPlayerMenu, - VideoVideoPlayerPanel, - VideoVideoPreview, - VideoVideoTimelinePanel, - VideoVideoTimelinePlayer, - VideoYouTubeElement, - WindowDialog, - WindowLayer, - WindowSortDialog, - WindowTooltip, -); - -export default UI; -export { UI }; - -if (typeof window !== 'undefined') { - window.Ox.UI = UI - Ox.load.UI = function(options, callback) { - - options = Ox.extend({ - hideScreen: true, - loadCSS: true, - loadThemes: true, - showScreen: false, - theme: 'oxlight' - }, options); - - var browsers = [ - { - name: 'Chrome Frame', - url: 'http://www.google.com/chromeframe/' - }, - { - name: 'Chrome', - regexp: /Chrome\/(\d+)\./, - url: 'http://www.google.com/chrome/', - version: 10 - }, - { - name: 'Firefox', - regexp: /Firefox\/(\d+)\./, - url: 'http://www.mozilla.org/firefox/', - version: 4 - }, - { - name: 'Safari', - regexp: /Version\/(\d+).*? Safari/, - url: 'http://www.apple.com/safari/', - version: 5 - }, - { - name: 'WebKit', - regexp: /AppleWebKit\/(\d+)\./, - version: 534 - }, - { - name: 'Googlebot', - regexp: /Googlebot\/(\d+)\./, - version: 2 - }, - { - name: 'YandexBot', - regexp: /YandexBot\/(\d+)\./, - version: 3 - }, - { - name: 'YandexMobileBot', - regexp: /YandexMobileBot\/(\d+)\./, - version: 3 - }, - { - name: 'Internet Explorer', - url: 'http://windows.microsoft.com/en-US/internet-explorer/products/ie/home', - version: 9 - } - ], - browserSupported = false, - isInternetExplorer = /MSIE/.test(navigator.userAgent); - - browsers.forEach(function(browser) { - var match = browser.regexp && browser.regexp.exec(navigator.userAgent); - if (match && match[1] >= browser.version) { - browserSupported = true; - } - }); - - Ox.UI = {}; - - Ox.UI.LoadingScreen = (function() { - - var $body = Ox.$('body'), - $screen = Ox.$('
') - .addClass('OxLoadingScreen') - .css({ - position: 'absolute', - left: 0, - top: 0, - right: 0, - bottom: 0, - padding: '4px', - background: 'rgb(' + ( - options.theme == 'oxlight' ? '240, 240, 240' - : options.theme == 'oxmedium' ? '144, 144, 144' - : '16, 16, 16' - ) + ')', - opacity: 1, - zIndex: 1000 - }), - css = { - position: 'absolute', - left: 0, - top: 0, - right: 0, - bottom: 0, - margin: 'auto', - MozUserSelect: 'none', - WebkitUserSelect: 'none' - }, - loadingInterval, - $icon, - deg = 0; - - browserSupported ? showIcon() : showWarning(); - - function showIcon() { - /* - // SVG transform performs worse than CSS transform - var src = Ox.PATH + 'UI/themes/' + options.theme + '/svg/symbolLoadingAnimated.svg' - Ox.getFile(src, function() { - Ox.$('') - .attr({ - src: src - }) - .css(Ox.extend({ - width: '32px', - height: '32px' - }, css)) - .on({ - mousedown: function(e) { - e.preventDefault(); - } - }) - .appendTo(div); - }); - */ - var src = Ox.PATH + 'UI/themes/' + options.theme + '/svg/symbolLoading.svg' - Ox.getFile(src, function() { - $icon = Ox.$('') - .attr({ - src: src - }) - .css(Ox.extend({ - width: '32px', - height: '32px' - }, css)) - .on({ - mousedown: function(e) { - e.preventDefault() - } - }) - .appendTo($screen); - }); - } - - function showWarning() { - var counter = 0; - browsers = browsers.filter(function(browser) { - return browser.url; - }); - isInternetExplorer ? browsers.pop() : browsers.shift(); - browsers.forEach(function(browser) { - browser.src = Ox.PATH + 'UI/png/browser' + browser.name.replace(' ', '') + '128.png'; - Ox.getFile(browser.src, function() { - ++counter == browsers.length && showIcons(); - }); - }); - function showIcons() { - var $box = Ox.$('
') - .css(Ox.extend({ - width: (browsers.length * 72) + 'px', - height: '72px' - }, css)) - .appendTo($screen); - browsers.forEach(function(browser, i) { - Ox.$('') - .attr({ - href: browser.url, - title: ( - browser.name == 'Chrome Frame' - ? Ox._('Install') : Ox._('Download') - ) + ' ' + browser.name - }) - .css({ - position: 'absolute', - left: (i * 72) + 'px', - width: '72px', - height: '72px' - }) - .append( - Ox.$('') - .attr({ - src: browser.src - }) - .css(Ox.extend({ - width: '64px', - height: '64px', - border: 0, - cursor: 'pointer' - }, css)) - .on({ - mousedown: function(e) { - e.preventDefault(); - } - }) - ) - .appendTo($box); - }); - } - } - - return { - hide: function() { - $('.OxLoadingScreen').animate({ - opacity: browserSupported ? 0 : 0.9 - }, 1000, function() { - if (browserSupported) { - clearInterval(loadingInterval); - loadingInterval = null; - $screen.remove(); - } else { - $screen.on({ - click: function() { - $screen.remove(); - } - }); - } - }); - }, - show: function() { - if (!loadingInterval) { - loadingInterval = setInterval(function() { - if ($icon) { - deg = (deg + 30) % 360; - $icon.css({ - MozTransform: 'rotate(' + deg + 'deg)', - OTransform: 'rotate(' + deg + 'deg)', - WebkitTransform: 'rotate(' + deg + 'deg)', - transform: 'rotate(' + deg + 'deg)' - }); - } - }, 83); - } - $screen.appendTo($body); - } - }; - - }()); - - Ox.documentReady(function() { - Ox.$('body').addClass('OxTheme' + Ox.toTitleCase(options.theme)); - options.showScreen && Ox.UI.LoadingScreen.show(); - }); - - loadUI(); - - function loadUI() { - - var path = Ox.PATH + 'UI/jquery/jquery.js?' + Ox.VERSION; - Ox.getFile(path, function() { - path = Ox.PATH + 'UI/json/UI.json?' + Ox.VERSION; - Ox.getJSON(path, function(data) { - var counter = 0, length; - if (!options.loadCSS) { - data.files = data.files.filter(function(file) { - return !Ox.endsWith(file, '.css'); - }); - } - if (!options.loadThemes) { - data.files = data.files.filter(function(file) { - return !Ox.contains(file, '/themes/'); - }); - } - length = data.files.length; - Ox.UI.IMAGES = data.images; - Ox.UI.THEMES = {}; - data.files.forEach(function(file) { - path = Ox.PATH + file + '?' + Ox.VERSION; - if (/\.jsonc$/.test(file)) { - Ox.getJSONC(path, function(data) { - var theme = /\/themes\/(\w+)\/json\//.exec(file)[1]; - Ox.UI.THEMES[theme] = data; - ++counter == length && initUI(); - }); - } else { - Ox.getFile(path, function() { - ++counter == length && initUI(); - }); - } - }); - }); - }); - - } - - function initUI() { - - Ox.documentReady(function() { - // fixme: is this the right place to do this? - $.browser.mozilla && Ox.$document.on('dragstart', function() { - return false; - }); - if (options.showScreen && options.hideScreen) { - Ox.UI.LoadingScreen.hide(); - } - callback(browserSupported); - }); - - } - - }; -}