Файл ajs.js
;(function(global){
define(function(){
// Advance JavaScript (AJS) - функции упрощающие написание кода
// -------------------------------------------------
// Функция для получения типа элемента
// Возвращает название типа элемента
// Примеры использования:
// getTypeOf(window); // Global
// getTypeOf(undefined); // Undefined
// getTypeOf(null); // Null
// getTypeOf(NaN); // NaN
// getTypeOf(Infinity); // Number
// getTypeOf(-Infinity); // Number
// getTypeOf(10); // Number
// getTypeOf(0.1); // Number
// getTypeOf('abc // String
// getTypeOf(true); // Boolean
// getTypeOf({a: 1, b: 2}); // Object
// getTypeOf([1, 2, 3]); // Array
// getTypeOf(new Uint32Array()); // Uint32Array
// (function(){
// return getTypeOf(arguments); // Arguments или Object в Internet Explorer < 9
// })();
// getTypeOf(function(){}); // Function
// getTypeOf(new Date()); // Date
// getTypeOf(/a-z/); // RegExp
// getTypeOf(Math); // Math
// getTypeOf(new Error()); // Error
function getTypeOf (element) {
if (element === global) {return 'Global';}
if (element === null) {return 'Null';}
if (element === undefined) {return 'Undefined';}
if (element !== element) {return 'NaN';}
return Object.prototype.toString.call(element).slice(8, -1);
}
// -------------------------------------------------
// Функция для проверки типа элемента
// Возвращает true, если тип элемента верный и false, если тип элемента не совпадает
// Примеры использования:
// isTypeOf(window, 'Global'); // true
// isTypeOf(undefined, 'Undefined'); // true
// isTypeOf(null, 'Null'); // true
// isTypeOf(NaN, 'NaN'); // true
// isTypeOf(1, 'Number'); // true
// isTypeOf(10, 'Integer'); // true
// isTypeOf(0.1, 'Float'); // true
// isTypeOf(Infinity, 'Infinity'); // true
// isTypeOf(-Infinity, '-Infinity'); // true
// isTypeOf('abc', 'String'); // true
// isTypeOf(true, 'Boolean'); // true
// isTypeOf({a: 1, b: 2}, 'Object'); // true
// isTypeOf([1, 2, 3], 'Array'); // true
// isTypeOf(new Int8Array(), 'Int8Array'); // true
// isTypeOf(new Uint8Array(), 'Uint8Array'); // true
// isTypeOf(new Int16Array(), 'Int16Array'); // true
// isTypeOf(new Uint16Array(), 'Uint16Array'); // true
// isTypeOf(new Int32Array(), 'Int32Array'); // true
// isTypeOf(new Uint32Array(), 'Uint32Array'); // true
// isTypeOf(new Float32Array(), 'Float32Array'); // true
// isTypeOf(new Float64Array(), 'Float64Array'); // true
// (function(){
// return isTypeOf(arguments, 'Arguments'); // true, если Arguments или Object в Internet Explorer < 9
// })();
// isTypeOf(function(){}, 'Function'); // true
// isTypeOf(new Date(), 'Date'); // true
// isTypeOf(/a-z/, 'RegExp'); // true
// isTypeOf(Math, 'Math'); // true
// isTypeOf(new Error(), 'Error'); // true
// isTypeOf('{"a": 1}', 'JSON'); // true
function isTypeOf (element, type) {
if (arguments.length < 2) {throw new Error ('You should provide "element" and "type" arguments to function isTypeOf (element, type) {...}');}
if (getTypeOf(type) !== 'String') {throw new Error ('Parameter "type" of function isTypeOf (element, type) {...} must be a string');}
if (type === 'Global') {
if (getTypeOf(element) === 'Global') {return true;
} else {return false;
}
}
if (type === 'Undefined') {if (getTypeOf(element) === 'Undefined') {return true;} else {return false;}}
if (type === 'Null') {if (getTypeOf(element) === 'Null') {return true;} else {return false;}}
if (type === 'NaN') {if (getTypeOf(element) === 'NaN') {return true;} else {return false;}}
if (type === 'Number') { if (getTypeOf(element) === 'Number') {return true;} else {return false;}}
if (type === 'Integer') {
if (getTypeOf(element) === 'Number' && element.toString().split('.').length === 1) {return true;} else {return false;}
}
if (type === 'Float') {
if (getTypeOf(element) === 'Number' && element.toString().split('.').length > 1) {return true;} else {return false;}
}
if (type === 'Infinity') {
if (getTypeOf(element) === 'Number' && element === Infinity) {return true;} else {return false;}
}
if (type === '-Infinity') {
if (getTypeOf(element) === 'Number' && element === -Infinity) {return true;} else {return false;}
}
if (type === 'String') {if (getTypeOf(element) === 'String') {return true;} else {return false;}}
if (type === 'Boolean') {if (getTypeOf(element) === 'Boolean') {return true;} else {return false;}}
if (type === 'Object') {if (getTypeOf(element) === 'Object') {return true;} else {return false;}}
if (type === 'Array') {if (getTypeOf(element) === 'Array') {return true;} else {return false;}}
if (type === 'Int8Array') { if (getTypeOf(element) === 'Int8Array') {return true;} else {return false;}}
if (type === 'Uint8Array') {if (getTypeOf(element) === 'Uint8Array') {return true;} else {return false;}}
if (type === 'Int16Array') {if (getTypeOf(element) === 'Int16Array') {return true;} else {return false;}}
if (type === 'Uint16Array') {if (getTypeOf(element) === 'Uint16Array') {return true;} else {return false;}}
if (type === 'Int32Array') {if (getTypeOf(element) === 'Int32Array') {return true;} else {return false;}}
if (type === 'Uint32Array') {if (getTypeOf(element) === 'Uint32Array') {return true;} else {return false;}}
if (type === 'Float32Array') {if (getTypeOf(element) === 'Float32Array') {return true;} else {return false;}}
if (type === 'Float64Array') {if (getTypeOf(element) === 'Float64Array') {return true;} else {return false;}}
if (type === 'Arguments') {
if (getTypeOf(element) === 'Arguments' || getTypeOf(element) === 'Object') {return true;} else {return false;}
}
if (type === 'Function') {if (getTypeOf(element) === 'Function') {return true;} else {return false;}}
if (type === 'Date') {if (getTypeOf(element) === 'Date') {return true;} else {return false;}}
if (type === 'RegExp') {if (getTypeOf(element) === 'RegExp') {return true;} else {return false;}}
if (type === 'Math') {if (getTypeOf(element) === 'Math') {return true;} else {return false;}}
if (type === 'Error') {if (getTypeOf(element) === 'Error') {return true;} else {return false;}}
if (type === 'JSON') {
if (getTypeOf(element) === 'String' && (function(){
var isJSON = true
, result;
try {
result = parseJSON(element);
} catch (error) {
isJSON = false;
}
return isJSON;
})()
) {return true;} else {return false;}
}
throw new Error('Unknown element in function has(element, data) {...}.');
}
// Вспомогательная функция parseJSON из jQuery
function parseJSON (data) {
if (window.JSON && window.JSON.parse) {return window.JSON.parse('' + data);}
function trimText (text) {
return text == null ? '' : ('' + text).replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
}
var rvalidtokens = /(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g
, requireNonComma
, depth = null
, str = trimText('' + data);
return str && !trimText(str.replace(rvalidtokens, function(token, comma, open, close){
if (requireNonComma && comma) {depth = 0;}
if (depth === 0) {return token;}
requireNonComma = open || comma;
depth += !close - !open;
return '';
})) ? (new Function('return ' + str))() : new Error('Invalid JSON: ' + data);
}
// -------------------------------------------------
// Функция для проверки пустая ли строка, массив или объект
// Возвращает true, если строка, массив или объект пусты и false, если не пусты
// Пример использования:
// isEmpty(''); // true
// isEmpty([]); // true
// isEmpty({}); // true
function isEmpty (element) {
var key;
if (isTypeOf(element, 'String')) {return element.length > 0 ? false : true;}
if (isTypeOf(element, 'Array')) {return element.length > 0 ? false : true;}
if (isTypeOf(element, 'Object')) {
for (key in element) {return false;}
return true;
}
throw new Error('Parameter "element" of function isEmpty (element) {...} must be type of: string, object or array.');
}
// -------------------------------------------------
// Функция для проверки имеет ли строка, массив или объект заданный элемент
// Возвращает true, если строка, массив или объект имеют заданный элемент и false, если не имеют
// Примеры использования:
// has(window, 'a'); // true
// has({'a': 1}, 'a'); // true
// has(functionA, 'methodB'); // true
// has(arguments, 'text'); // true
// has([ , null, 1, 'text', false], undefined); // true
// has([ , null, 1, 'text', false], null); // true
// has([ , null, 1, 'text', false], 1); // true
// has([ , null, 1, 'text', false], 'text'); // true
// has([ , null, 1, 'text', false], false); // true
// has('1 text', 1); // true
// has('1 text', 'text'); // true
// has('1 text', /te/gi); // true
function has (element, data) {
var len;
if (isTypeOf(element, 'Object') || isTypeOf(element, 'Function') || isTypeOf(element, 'Global')) {
if (!isTypeOf(data, 'String')) {throw new Error('Parameter "data" of function has(element, data) {...} for "object", "function" or "global" must be a string.');}
return element.hasOwnProperty(data);
}
if (
(isTypeOf(element, 'Array') || isTypeOf(element, 'Arguments'))
&& (
isTypeOf(data, 'Undefined')
|| isTypeOf(data, 'Null')
|| isTypeOf(data, 'Number')
|| isTypeOf(data, 'String')
|| isTypeOf(data, 'Boolean')
)
) {
len = element.length;
if (len > 0) {
for (;len--;) {
if (element[len] === data) {return true;}
}
}
return false;
}
if (
isTypeOf(element, 'String')
&& (
isTypeOf(data, 'Number')
|| isTypeOf(data, 'String')
|| isTypeOf(data, 'RegExp')
)
) {
if (isTypeOf(data, 'Number')) {data = '' + data;}
if (element.search(data) > -1) {return true;}
return false;
}
}
// -------------------------------------------------
// Функция для формирования массива чисел в пределах заданного диапазона значений
// Возвращает массив значений
// Примеры использования:
// range(1, 10, 2); // [1, 3, 5, 7, 9]
// range(-2, 2, 1); // [-2, -1, 0, 1, 2]
// range(1, 5); // [1, 2, 3, 4, 5]
// range(5, 1); // [5, 4, 3, 2, 1]
// range(5, 5); // [5]
// range(5, 5, 5); // [5]
function range (start, end, step) {
var list = []
, condition;
if (start < end) {
if (step === undefined) {step = 1;}
condition = function () {return start <= end;};
} else if (start > end) {
if (step === undefined) {step = -1;}
condition = function () {return start >= end;};
} else if (start === end) {
list.push(start);
return list;
}
while (condition()) {
list.push(start);
start += step;
}
return list;
}
// -------------------------------------------------
// Функция для проверки нахождения числа в пределах заданного диапазона значений
// Возвращает true, если число находится в пределах заданного диапазона и false, если нет
// Примеры использования:
// isInRange(1, 10, 5); // true
// isInRange(1, 10, 20); // false
// isInRange(5, 5, 5); // true
function isInRange (min, max, value) {
if (!isTypeOf(min, 'Number')) {throw new Error('Parameter "min" of function isInRange(min, max, value) {...} must be a number.');}
if (!isTypeOf(max, 'Number')) {throw new Error('Parameter "max" of function isInRange(min, max, value) {...} must be a number.');}
if (!isTypeOf(value, 'Number')) {throw new Error('Parameter "value" of function isInRange(min, max, value) {...} must be a number.');}
if (min <= value && value <= max) {
return true;
} else {
return false;
}
}
// -------------------------------------------------
// Функция для проверки входит ли число в допустимый в JavaScript максимальный предел значений
// Возвращает true, если число находится в пределах допустимого диапазона и false, если нет
// Примеры использования:
// isSafeInteger(10); // true
// isSafeInteger(9007199254740992); // false
function isSafeInteger (number) {
return (
isTypeOf(number, 'Number')
&& Math.round(number) === number
&& -(Math.pow(2, 53) - 1) <= number && number <= (Math.pow(2, 53) - 1)
);
}
// -------------------------------------------------
// Функция для обрезания дробной части либого числа
// Возвращает целую часть любого числа
// Примеры использования:
// mathTrunc(42.7); // 42
// mathTrunc( 0.1); // 0
// mathTrunc(-0.1); // -0
function mathTrunc (x) {
return (x < 0 ? Math.ceil(x) : Math.floor(x));
}
// -------------------------------------------------
// Функция для обработка элементов массивов и объектов в цикле
// Обрабатывает все элементы массива или объекта в цикле
// Примеры использования:
// forEachIn({one: 1, two: 2, three: 3, four: 4, five: 5}, function(value, key, object){
// if (key === 'one') {return 'continue';} else if (key === 'four') {return 'break';}
// console.log(value);
// });
// forEachIn(['one', 'two', 'three', 'four', 'five'], function(value, index, array){
// if (index === 0) {return 'continue';} else if (index === 3) {return 'break';}
// console.log(value);
// });
// forEachIn(range(1, 10), function(value, index, array){
// console.log(value);
// });
// forEachIn('abc' function(value, index, string){
// console.log(value);
// });
function forEachIn (object, callback) {
if (!isTypeOf(callback, 'Function')) {
throw new Error('Parameter "callback" of function forEachIn(object, callback) {...} must be a function.');
}
var key, length, result;
if (isTypeOf(object, 'Object') || isTypeOf(object, 'Function') || isTypeOf(object, 'Global')) {
for (key in object) {
result = callback(object[key], key, object);
if (result) {
if (result === 'continue') {continue;} else if (result === 'break') {break;}
}
}
} else if (isTypeOf(object, 'Array') || isTypeOf(object, 'Arguments') || isTypeOf(object, 'String')) {
length = object.length;
for (key = 0; key < length; key++) {
result = callback(object[key], key, object);
if (result) {
if (result === 'continue') {continue;} else if (result === 'break') {break;}
}
}
} else {
throw new Error('Parameter "object" of function forEachIn(object, callback) {...} must be an global, object, function, array, arguments or string.');
}
}
// -------------------------------------------------
// Функция для изменения значения булева значения на противоположное
// Изменяет true на false и false на true
// Примеры использования:
// not(true); // false
// not(false); // true
// not(isEmpty('')); // false
function not (bool) {
return !bool;
}
// -------------------------------------------------
// Функция для обрезания пробельных символов в начале и конце строки
// Обрезает все пробельные символы в начале и конце строки
// Примеры использования:
// trim(' text ');
function trim (text) {
return text == null ? '' : ('' + text).replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); // Trim space: \s, BOM: \uFEFF, NBSP: \xA0.
}
// -------------------------------------------------
// Функция для удаления множественных пробельных символов внутри строки
// Заменяет повторяющиеся символы пробела на один пробел
// Примеры использования:
// removeExtraSpaces('text text text');
function removeExtraSpaces (text) {
return text == null ? '' : ('' + text).replace(/[\uFEFF\xA0]+/g, ' ').replace(/\s{2,}/g, ' '); // Space: \s, BOM: \uFEFF, NBSP: \xA0.
}
// -------------------------------------------------
// Функция для динамического создания любой функции с именем и без имени.
// Создает любую функцию с заданными параметрами
// Примеры использования:
// var func = createFunction ();
// console.log(func.toString()); // function () {}
// console.log(func()); // undefined
// var func = createFunction ('a');
// console.log(func.toString()); // function a () {}
// console.log(func()); // undefined
// var func = createFunction ('a', 'return 1;');
// console.log(func.toString()); // function a () {return 1;}
// console.log(func()); // 1
// var func = createFunction ('a', 'b, c', 'return b + c;');
// console.log(func.toString()); // function a (b, c) {return b + c;}
// console.log(func(2, 3)); // 5
function createFunction (name, args, body) {
var argumentsLength = arguments.length
, isString = function (element) {return Object.prototype.toString.call(element) === '[object String]';};
if (argumentsLength === 0) { // createFunction();
name = '';
args = '';
body = '';
} else if (argumentsLength === 1) { // createFunction('name');
if (!isString(name)) {throw new Error('Function name must be a string.');}
args = '';
body = '';
} else if (argumentsLength === 2) { // createFunction('name', 'body');
if (!isString(name)) {throw new Error('Function name must be a string.');}
body = args;
args = '';
if (!isString(body)) {throw new Error('Function body must be a string.');}
} else if (argumentsLength === 3) { // createFunction('name', 'args', 'body');
if (!isString(name)) {throw new Error('Function name must be a string.');}
if (!isString(args)) {throw new Error('Function arguments must be a string.');}
if (!isString(args)) {throw new Error('Function body must be a string.');}
}
return (new Function('return function ' + name + ' (' + args + ') {' + body + '};'))();
}
// -------------------------------------------------
// Функция для наследования потомком свойств и методов родителя
// Копирует свойства и методы родителя в потомка
// Примеры использования:
// extend(СhildClassFunction, ParentClassFunction);
// extend(СhildObject, ParentObject);
// extend(СhildClassFunction, ParentObject);
function extend (child, parent) {
var key;
function F () {}
if (isTypeOf(child, 'Object') && isTypeOf(parent, 'Object')) {
for (key in parent) {if (parent.hasOwnProperty(key)) {child[key] = parent[key];}}
} else if (isTypeOf(child, 'Function') && isTypeOf(parent, 'Object')) {
child.prototype = parent;
child.prototype.constructor = child;
} else if (isTypeOf(child, 'Function') && isTypeOf(parent, 'Function')) {
for (key in parent) {if (parent.hasOwnProperty(key)) {child[key] = parent[key];}}
F.prototype = parent.prototype;
child.prototype = new F();
child.prototype.constructor = child;
}
}
// -------------------------------------------------
// Функция для подмешивания в класс свойств и методов другого класса
// Копирует свойства и методы родителя в потомка
// Примеры использования:
// mixin(СhildClassFunction, [MixinClassFunction1, MixinClassFunction2, MixinClassFunction3]);
// mixin(СhildClassFunction, MixinClassFunction1, MixinClassFunction2, MixinClassFunction3);
function mixin (child) {
var mixins = Array.prototype.slice.call(arguments, 1);
if (not(isEmpty(mixins))) {
if (isTypeOf(mixins[0], 'Array')) {mixins = mixins[0];}
forEachIn(mixins, function(mixinClass){
forEachIn(mixinClass, function(propertyInClass, propertyName){if (mixinClass.hasOwnProperty(propertyName)) {child[propertyName] = propertyInClass;}});
forEachIn(mixinClass.prototype, function(propertyInPrototype, propertyName){if (propertyName !== 'constructor') {child.prototype[propertyName] = propertyInPrototype;}});
});
}
}
// -------------------------------------------------
// Функция для проверки соотвествия свойств и методов класса своему интерфейсу
// Проверяет наличие свойств и методов класса описанных в интерфейсе
// Примеры использования:
// implement(СlassFunction, СlassInterface);
function implement (classFunction, classInterface) {
var key, errorMessage = 'Class "' + classFunction.name + '" does not implement the interface "' + classInterface.name + '" correctly for ';
for (key in classInterface) {if (not(key in classFunction)) {throw new Error(errorMessage + 'static ' + '"' + key + '".');}}
for (key in classInterface.prototype) {if (not(key in classFunction.prototype)) {throw new Error(errorMessage + '"' + key + '".');}}
}
// -------------------------------------------------
// Функция для создания класса
// Создает класс по с заданными параметрами
// Примечания:
// В названиях допускает вводить несколько пробелов для удобства записи и просмотра, так как они потом вырезаются.
// Методы без типа по умолчанию становятся public и записываются в prototype.
// singleton constructor - функция-конструктор объектов, вызывается через статический метод new класса и создающая объект в единственном экземпляре.
// constructor - функция-конструктор объектов, вызывается через статический метод new класса. Всегда возврящает объект.
// destructor - функция-деструктор объекта, вызывается через публичный метод destroy объекта. Всегда возвращает пустоту.
// default - значение по умолчанию, заданное общим для всех объектов, если это значение не задано в классе при создании объекта или удалено в процессе использования объекта.
// private - частный метод записывается в prototype с обязательным префиксом _. private лишь проверяет наличие префикса _ в имени метода.
// По-настоящему частные переменные и функции создаются только в функции-конструкторе объектов.
// public - публичный метод записывается в prototype.
// static - статичный метод записывается в класс. Наследуется потомками класса.
// final - публичный метод не предназначенный для перезаписи в классах потомках.
// override - перезаписанный публичный метод - записывается в prototype.
// get - метод getter.
// set - метод setter.
// abstract - абстрактный публичный метод, который автоматом заменяется на пустую функцию.
// :String - тип возвращаемых данных. Можно записывать с пробелами или без. Названия типов можно писать с большой буквы или с маленькой буквы. Тип данных нигде не проверяется.
// Class - коструктор класса.
// Interface - коструктор интерфейса. Интерфейсы по возможности описывают свойства и методы сразу.
// A extends B - наследование класса.
// A implements C - имплементация интерфейса - проверяет правильности записи методов в классе.
// A mixin D - подмешивание методов в класс.
// Примеры использования:
// var ComplexClass = Class ('ComplexClass extends OtherClass implements SomeInterface mixin SomeClass', {
// 'singleton constructor:Object': function () {}
// , 'destructor:Void': function () {}
// , 'default defaulVarOrFunction:String': function () {}
// , 'publicFunctionWithoutMoldifier:String': function () {}
// , 'public publicFunctionWithMoldifier:String': function () {}
// , 'private _privateFunction:String': function () {}
// , 'static staticVarOrFunction:String': function () {}
// , 'final finalPublicFunction:String': function () {}
// , 'override overridePublicFunction:String': function () {}
// , 'abstract abstractPublicFunction:String': function () {}
// , 'get x:String': function () {return this._x;}
// , 'set x:String': function (value) {this._x = value;}
// });
// ...
// ...
// var Calendar = Class ('Calendar', {
// 'costructor': function (calendarDate) {this.calendar = '<div id="calendar">' + calendarDate + '</div>';}
// , 'destructor': function () {$('#calendar').remove();}
// , 'public appendTo': function (element) {element.empty().append(this.calendar);}
// });
// ...
// ...
// var cal = Calendar.new('10/05/2015 15:30:45:350'); // Создание объекта календаря
// cal.appendTo($('#calendar-block')); // Добавление календаря в блок на страницу
// ...
// ...
// cal.setDate('25/01/1995 22:35:50:700'); // Установка новой даты в календаре
// ...
// ...
// cal.destroy(); // Удаление календаря со страницы
// cal = null; // Уничтожение объекта календаря
// Абстрактный класс
function AbstractClass () {}
// Функция-конструктор классов
function Class (classDescription, classBody) {
if (classDescription === undefined) {throw new Error('You should provide "classDescription" argument to function Class (classDescription, classBody) {...}');}
if (!isTypeOf(classDescription, 'String')) {throw new Error('Parameter "classDescription" of function Class (classDescription, classBody) {...} must be a string.');}
classDescription = removeExtraSpaces(classDescription);
classDescription = trim(classDescription);
if (isEmpty(classDescription)) {throw new Error('Parameter "classDescription" of function Class (classDescription, classBody) {...} must be a not empty string.');}
classDescription = classDescription.split(' ');
var classDescriptionLength = classDescription.length
, classFunction
, keywordIndex;
function findKeywordIndex (keyword) {
for (var i = 0; i < classDescriptionLength; i++) {if (keyword === classDescription[i]) {return i;}}
return false;
}
// 1 keyword:
// 'ClassName'
// 3 keywords:
// 'ClassName extends OtherClass'
// 'ClassName implements SomeInterface'
// 'ClassName mixin SomeClass'
// 5 keywords:
// 'ClassName extends OtherClass implements SomeInterface'
// 'ClassName implements SomeInterface mixin SomeClass'
// 'ClassName extends OtherClass mixin SomeClass'
// 7 keywords:
// 'ClassName extends OtherClass implements SomeInterface mixin SomeClass'
if (not(
classDescriptionLength === 1
|| classDescriptionLength === 3
|| classDescriptionLength === 5
|| classDescriptionLength === 7
)) {throw new Error('Parameter "classDescription" of function Class (classDescription, classBody) {...} has wrong format.');}
classFunction = createFunction(classDescription[0]);
keywordIndex = findKeywordIndex('extends');
if (keywordIndex) {eval('extend(classFunction, ' + classDescription[keywordIndex + 1] + ');');} else {extend(classFunction, AbstractClass);}
if (isTypeOf(classBody, 'Object') && !isEmpty(classBody)) {defineClassBody(classFunction, classBody);}
keywordIndex = findKeywordIndex('mixin');
if (keywordIndex) {eval('mixin(classFunction, ' + classDescription[keywordIndex + 1] + ');');}
keywordIndex = findKeywordIndex('implements');
if (keywordIndex) {eval('implement(classFunction, ' + classDescription[keywordIndex + 1] + ');');}
return classFunction;
}
// -------------------------------------------------
// Функция для создания синглтона
// Создает синглтон
// Примеры использования:
// var instance = Class.createSingleton(ClassFunction);
Class.createSingleton = function (ClassFunction) {
if (ClassFunction.instance !== undefined) {
return ClassFunction.instance;
} else {
if (isTypeOf(ClassFunction, 'Object')) {
return ClassFunction.instance = ClassFunction;
} else {
return ClassFunction.instance = new ClassFunction();
}
}
};
// -------------------------------------------------
// Функция для конструирования тела класса
// Конструирует класс по заданным параметрам
// Примеры использования:
// defineClassBody (ClassFunction, ClassBody);
function defineClassBody (classFunction, classBody) {
var key, propertyDescription;
for (key in classBody) {
propertyDescription = removeExtraSpaces(key);
propertyDescription = trim(propertyDescription);
if (isEmpty(propertyDescription)) {throw new Error('Property description of class "' + classFunction.name + '" must be a not empty string.');}
propertyDescription = propertyDescription.split(':');
propertyDescription = trim(propertyDescription[0]);
propertyDescription = propertyDescription.split(' ');
classFunction.instance = undefined;
if (propertyDescription[0] === 'singleton' && propertyDescription[1] === 'constructor') {classFunction.new = classBody[key];
} else if (propertyDescription[0] === 'constructor') {classFunction.new = classBody[key];
} else if (propertyDescription[0] === 'destructor') {classFunction.prototype.destroy = classBody[key];
} else if (propertyDescription[0] === 'default' && propertyDescription[1] !== undefined) {checkKeywords(propertyDescription[1], 'default'); classFunction.prototype[propertyDescription[1]] = classBody[key];
} else if (propertyDescription[0] === 'public' && propertyDescription[1] !== undefined) {checkKeywords(propertyDescription[1], 'public'); classFunction.prototype[propertyDescription[1]] = classBody[key];
} else if (propertyDescription[0] === 'private' && propertyDescription[1] !== undefined) {checkKeywords(propertyDescription[1], 'private'); classFunction.prototype[propertyDescription[1]] = classBody[key];
if (propertyDescription[1].substring(0, 1) !== '_') {throw new Error('Private property name "' + propertyDescription[1] + '" must starts from symbol "_".');}
} else if (propertyDescription[0] === 'static' && propertyDescription[1] !== undefined) {checkKeywords(propertyDescription[1], 'static'); classFunction[propertyDescription[1]] = classBody[key];
} else if (propertyDescription[0] === 'abstract' && propertyDescription[1] !== undefined) {checkKeywords(propertyDescription[1], 'abstract'); classFunction.prototype[propertyDescription[1]] = function () {};
} else if (propertyDescription[0] === 'final' && propertyDescription[1] !== undefined) {checkKeywords(propertyDescription[1], 'final'); classFunction.prototype[propertyDescription[1]] = classBody[key];
} else if (propertyDescription[0] === 'override' && propertyDescription[1] !== undefined) {checkKeywords(propertyDescription[1], 'override'); classFunction.prototype[propertyDescription[1]] = classBody[key];
} else if (propertyDescription[0] === 'get' && propertyDescription[1] !== undefined) {checkKeywords(propertyDescription[1], 'public');
Object.defineProperty(classFunction.prototype, propertyDescription[1], {get: classBody[key], enumerable: true, configurable: true});
} else if (propertyDescription[0] === 'set' && propertyDescription[1] !== undefined) {checkKeywords(propertyDescription[1], 'public');
Object.defineProperty(classFunction.prototype, propertyDescription[1], {set: classBody[key], enumerable: true, configurable: true});
} else {checkKeywords(propertyDescription[1], 'public'); classFunction.prototype[propertyDescription[0]] = classBody[key];
}
}
}
// -------------------------------------------------
// Функция для проверки использования запрещенных ключевых слов в качестве названий свойств и методов
// Проверяет наличие запрещенных ключевых слов: new, instance, destroy, get, set
// Примеры использования:
// checkKeywords('new', 'static');
// checkKeywords('instance', 'static');
// checkKeywords('destroy', 'public');
function checkKeywords (keyword, inside) {
if (keyword === 'new' && inside === 'static') {throw new Error('You can\'t use keyword "new" as static method for class.');
} else if (keyword === 'instance' && inside === 'static') {throw new Error('You can\'t use keyword "instance" as static method for class.');
} else if (keyword === 'destroy'
&& (
inside === 'default'
|| inside === 'public'
|| inside === 'private'
|| inside === 'abstract'
|| inside === 'final'
|| inside === 'override'
|| inside === 'get'
|| inside === 'set'
)
) {throw new Error('You can\'t use keyword "destroy" as method for object.');
} else if (keyword === 'get'
&& (
inside === 'default'
|| inside === 'public'
|| inside === 'private'
|| inside === 'abstract'
|| inside === 'final'
|| inside === 'override'
|| inside === 'get'
|| inside === 'set'
)
) {throw new Error('You can\'t use keyword "get" as method for object.');
} else if (keyword === 'set'
&& (
inside === 'default'
|| inside === 'public'
|| inside === 'private'
|| inside === 'abstract'
|| inside === 'final'
|| inside === 'override'
|| inside === 'get'
|| inside === 'set'
)
) {throw new Error('You can\'t use keyword "set" as method for object.');
}
}
// -------------------------------------------------
// Функция для создания интерфейса класса
// Создает интерфейс по заданным параметрам
// Примеры использования:
// var ComplexClassInterface = Interface ('ComplexClassInterface extends SomeInterface', [
// 'singleton constructor :Object'
// , 'destructor :Void'
// , 'default defaulVarOrFunction :String'
// , 'publicFunctionWithoutMoldifier :String'
// , 'public publicFunctionWithMoldifier :String'
// , 'private _privateFunction :String'
// , 'static staticVarOrFunction :String'
// , 'final finalPublicFunction :String'
// , 'abstract abstractPublicFunction :String'
// , 'get x :String'
// , 'set x :String'
// ]);
// Абстрактный интерфейс
function AbstractInterface () {}
// Функция-конструктор интерфейсов
function Interface (interfaceDescription, interfaceBody) {
if (interfaceDescription === undefined) {throw new Error('You should provide "interfaceDescription" argument to function Interface (interfaceDescription, interfaceBody) {...}');}
if (!isTypeOf(interfaceDescription, 'String')) {throw new Error('Parameter "interfaceDescription" of function Interface (interfaceDescription, interfaceBody) {...} must be a string.');}
interfaceDescription = removeExtraSpaces(interfaceDescription);
interfaceDescription = trim(interfaceDescription);
if (isEmpty(interfaceDescription)) {throw new Error('Parameter "interfaceDescription" of function Interface (interfaceDescription, interfaceBody) {...} must be a not empty string.');}
interfaceDescription = interfaceDescription.split(' ');
var interfaceDescriptionLength = interfaceDescription.length
, interfaceFunction
, keywordIndex;
function findKeywordIndex (keyword) {
for (var i = 0; i < interfaceDescriptionLength; i++) {if (keyword === interfaceDescription[i]) {return i;}}
return false;
}
// 1 keyword:
// 'InterfaceName'
// 3 keywords:
// 'InterfaceName extends OtherInterface'
if (not(
interfaceDescriptionLength === 1
|| interfaceDescriptionLength === 3
)) {throw new Error('Parameter "interfaceDescription" of function Interface (interfaceDescription, interfaceBody) {...} has wrong format.');}
interfaceFunction = createFunction(interfaceDescription[0]);
keywordIndex = findKeywordIndex('extends');
if (keywordIndex) {eval('extend(interfaceFunction, ' + interfaceDescription[keywordIndex + 1] + ');');} else {extend(interfaceFunction, AbstractInterface);}
if (isTypeOf(interfaceBody, 'Array') && !isEmpty(interfaceBody)) {defineInterfaceBody(interfaceFunction, interfaceBody);}
return interfaceFunction;
}
// -------------------------------------------------
// Функция для конструирования тела интерфейса
// Конструирует интерфейс по заданным параметрам
// Примеры использования:
// defineInterfaceBody(InterfaceFunction, InterfaceBody);
function defineInterfaceBody (interfaceFunction, interfaceBody) {
forEachIn(interfaceBody, function(propertyDescription){
propertyDescription = removeExtraSpaces(propertyDescription);
propertyDescription = trim(propertyDescription);
if (isEmpty(propertyDescription)) {throw new Error('Property description of interface "' + interfaceFunction.name + '" must be a not empty string.');}
propertyDescription = propertyDescription.split(':');
propertyDescription = trim(propertyDescription[0]);
propertyDescription = propertyDescription.split(' ');
interfaceFunction.instance = undefined;
if (propertyDescription[0] === 'singleton' && propertyDescription[1] === 'constructor') {interfaceFunction.new = undefined;
} else if (propertyDescription[0] === 'constructor') {interfaceFunction.new = undefined;
} else if (propertyDescription[0] === 'destructor') {interfaceFunction.prototype.destroy = undefined;
} else if (propertyDescription[0] === 'default' && propertyDescription[1] !== undefined) {checkKeywords(propertyDescription[1], 'default'); interfaceFunction.prototype[propertyDescription[1]] = undefined;
} else if (propertyDescription[0] === 'public' && propertyDescription[1] !== undefined) {checkKeywords(propertyDescription[1], 'public'); interfaceFunction.prototype[propertyDescription[1]] = undefined;
} else if (propertyDescription[0] === 'private' && propertyDescription[1] !== undefined) {checkKeywords(propertyDescription[1], 'private'); interfaceFunction.prototype[propertyDescription[1]] = undefined;
if (propertyDescription[1].substring(0, 1) !== '_') {throw new Error('Private property name "' + propertyDescription[1] + '" must starts from symbol "_".');}
} else if (propertyDescription[0] === 'static' && propertyDescription[1] !== undefined) {checkKeywords(propertyDescription[1], 'static'); interfaceFunction[propertyDescription[1]] = undefined;
} else if (propertyDescription[0] === 'abstract' && propertyDescription[1] !== undefined) {checkKeywords(propertyDescription[1], 'abstract'); interfaceFunction.prototype[propertyDescription[1]] = undefined;
} else if (propertyDescription[0] === 'final' && propertyDescription[1] !== undefined) {checkKeywords(propertyDescription[1], 'final'); interfaceFunction.prototype[propertyDescription[1]] = undefined;
} else if (propertyDescription[0] === 'override' && propertyDescription[1] !== undefined) {checkKeywords(propertyDescription[1], 'override'); interfaceFunction.prototype[propertyDescription[1]] = undefined;
} else if (propertyDescription[0] === 'get' && propertyDescription[1] !== undefined) {checkKeywords(propertyDescription[1], 'public');
Object.defineProperty(interfaceFunction.prototype, propertyDescription[1], {get: undefined, enumerable: true, configurable: true});
} else if (propertyDescription[0] === 'set' && propertyDescription[1] !== undefined) {checkKeywords(propertyDescription[1], 'public');
Object.defineProperty(interfaceFunction.prototype, propertyDescription[1], {set: undefined, enumerable: true, configurable: true});
} else {checkKeywords(propertyDescription[1], 'public'); interfaceFunction.prototype[propertyDescription[0]] = undefined;
}
});
}
// -------------------------------------------------
// Тесты функций-конструкторов классов и интерфейсов
/*
var ParentInterface = Interface ('ParentInterface', [
' constructor:Object '
, ' destructor:Void '
, ' default defaultVar:String '
, ' publicMethodWithoutMoldifier:String '
, ' public publicMethodWithMoldifier:String '
, ' private _privateMethod:String '
, ' static staticMethod:String '
, ' final finalMethod:String '
, ' abstract abstractMethod:String '
, ' get x:String '
, ' set x:String '
]);
var ChildInterface = Interface ('ChildInterface extends ParentInterface', [
' singleton constructor :Object '
, ' publicChildMethodWithoutMoldifier :String '
, ' static staticChildMethod :String '
, ' abstract abstractChildMethod :String'
, ' get y :String '
, ' set y :String '
]);
console.log('ParentInterface static');
console.dir(ParentInterface);
console.log('ParentInterface public');
console.dir(ParentInterface.prototype);
console.log('ChildInterface static');
console.dir(ChildInterface);
console.log('ChildInterface public');
console.dir(ChildInterface.prototype);
console.log('ParentInterface child');
console.dir(new ParentInterface());
console.log('ChildInterface child');
console.dir(new ChildInterface());
var ParentClass = Class ('ParentClass', {
' constructor:Object ': function (initVar) {
var instance = new ParentClass();
instance.initVar = initVar;
instance._x = 0;
var privateVar = 'ParentClass private var';
function privateFunction () {return 'ParentClass private function can show ' + privateVar + ' and ' + instance._privateMethod();}
instance.eventFunction = function () {alert(privateFunction());}
document.addEventListener('click', instance.eventFunction, false);
return instance;
}
, ' destructor:Void ': function () {
document.removeEventListener('click', this.eventFunction, false);
console.log('ParentClass destroyed.');
}
, ' default defaultVar:String ': 'ParentClass default var.'
, ' publicMethod:String ': function () {return 'ParentClass public method.';}
, ' public publicMethodWithMoldifier:String ': function () {return 'ParentClass public method with moldifier.';}
, ' private _privateMethod:String ': function () {return 'ParentClass private method.';}
, ' static staticMethod:String ': function () {return 'ParentClass static method.';}
, ' final finalMethod:String ': function () {return 'ParentClass final method.';}
, ' abstract abstractMethod:String ': function () {}
, ' get x:String ': function () {return this._x;}
, ' set x:String ': function (value) {this._x = value;}
});
console.log('ParentClass static');
console.dir(ParentClass);
console.log('ParentClass public');
console.dir(ParentClass.prototype);
var parentObject = ParentClass.new('ParentClass init var');
console.log('1) ParentClass object created.');
console.log('2) Constructor name: ' + parentObject.constructor.name);
console.log('3) DeafultVar value: ' + parentObject.defaultVar);
console.log('4) InitVar value: ' + parentObject.initVar);
console.log('5) PublicMethod result: ' + parentObject.publicMethod());
console.log('6) PublicMethodWithMoldifier result: ' + parentObject.publicMethodWithMoldifier());
console.log('7) StaticMethod result: ' + ParentClass.staticMethod());
console.log('8) FinalMethod result: ' + parentObject.finalMethod());
console.log('9) AbstractMethod result: ' + parentObject.abstractMethod());
console.log('10) Get x result: ' + parentObject.x);
console.log('11) Set x.');
parentObject.x = 1;
console.log('12) Get x after set: ' + parentObject.x);
setTimeout(function(){console.log('13) Object destruction. '); parentObject.destroy(); parentObject = null;}, 5000);
var MixinClass = Class ('MixinClass', {
' public mixinPublicMethod :String ': function () {return 'MixinClass public method.';}
, ' static mixinStaticMethod :String ': function () {return 'MixinClass static method.';}
});
console.log('MixinClass static');
console.dir(MixinClass);
console.log('MixinClass public');
console.dir(MixinClass.prototype);
var ChildClass = Class ('ChildClass extends ParentClass implements ChildInterface mixin MixinClass', {
' singleton constructor :Object ': function (initVar) {
if (ChildClass.instance) {return ChildClass.instance;}
var instance = Class.createSingleton(ChildClass);
extend(instance, ParentClass.new(initVar));
instance.initVar = initVar;
instance._y = 0;
var privateVar = 'ChildClass private var';
function privateFunction () {return 'ChildClass private function can show ' + privateVar + ' and ' + instance._privateMethod();}
console.log(privateFunction());
return instance;
}
, ' destructor :Void ': function () {
document.removeEventListener('click', this.eventFunction, false);
console.log('ChildClass destroyed.');
}
, ' public publicChildMethodWithMoldifier :String ': function () {return 'ChildClass public child method with moldifier.';}
, ' override publicMethod :String ': function () {return 'ChildClass public method.';}
, ' static staticChildMethod :String ': function () {return 'ChildClass static child method.';}
, ' publicChildMethodWithoutMoldifier :String ': function () {return 'ChildClass public child method with modifier.';}
, ' abstract abstractChildMethod :String': function () {}
, ' publicMethodWithoutMoldifier:String ': function () {return 'ChildClass public method without modifier.';}
, ' get y:String ': function () {return this._y;}
, ' set y:String ': function (value) {this._y = value;}
});
console.log('ChildClass static');
console.dir(ChildClass);
console.log('ChildClass public');
console.dir(ChildClass.prototype);
var childObject = ChildClass.new('ChildClass init var');
console.log('1) ChildClass object 1 created.');
var childObject2 = ChildClass.new('ChildClass init var 2');
console.log('2) ChildClass object 2 created.');
console.log('3) Is singleton? ' + (childObject === childObject2));
console.log('4) Constructor name: ' + childObject.constructor.name);
console.log('5) DeafultVar value: ' + childObject.defaultVar);
console.log('6) InitVar value: ' + childObject.initVar);
console.log('7) PublicMethod result: ' + childObject.publicMethod());
console.log('8) PublicMethodWithMoldifier result: ' + childObject.publicMethodWithMoldifier());
console.log('9) PublicChildMethodWithMoldifier result: ' + childObject.publicChildMethodWithMoldifier());
console.log('10) StaticMethod result: ' + ChildClass.staticMethod());
console.log('11) FinalMethod result: ' + childObject.finalMethod());
console.log('12) AbstractMethod result: ' + childObject.abstractMethod());
console.log('13) MixinPublicMethod result: ' + childObject.mixinPublicMethod());
console.log('14) MixinStaticMethod result: ' + ChildClass.mixinStaticMethod());
console.log('15) Get x result: ' + childObject.x);
console.log('16) Set x.');
childObject.x = 2;
console.log('17) Get x after set: ' + childObject.x);
console.log('18) Get y result: ' + childObject.y);
console.log('19) Set y.');
childObject.y = 1;
console.log('20) Get y after set: ' + childObject.y);
console.log('21) StaticChildMethod result: ' + ChildClass.staticChildMethod());
console.log('22) PublicChildMethodWithoutMoldifier result: ' + childObject.publicChildMethodWithoutMoldifier());
console.log('23) AbstractChildMethod result: ' + childObject.abstractChildMethod());
console.log('24) PublicMethodWithoutMoldifier result: ' + childObject.publicMethodWithoutMoldifier());
setTimeout(function(){console.log('25) Object destruction. '); childObject.destroy(); childObject = null;}, 5000);
*/
// -------------------------------------------------
// Функция для конструирования перечислений
// Конструирует перечисление по списку параметров
// Примеры использования:
// var Colors = new Enum('Red', 'Green', 'Blue');
// console.dir(Colors);
// console.log(Colors[0]); // Red
// console.log(Colors.Red); // 0
// var Colors = new Enum({'Red': 4, 'Green': 5, 'Blue': 6});
// console.dir(Colors);
// console.log(Colors[4]); // Red
// console.log(Colors.Red); // 4
function Enum () {
var i
, key
, len = arguments.length;
if (len === 1 && isTypeOf(arguments[0], 'Object')) {
for (key in arguments[0]) {
this[key] = arguments[0][key];
this['' + arguments[0][key]] = key;
}
} else {
for (i = 0; i < len; i++) {
this[arguments[i]] = i;
this['' + i] = arguments[i];
}
}
}
// -------------------------------------------------
return {
getTypeOf: getTypeOf
, isTypeOf: isTypeOf
, isEmpty: isEmpty
, has: has
, range: range
, isInRange: isInRange
, isSafeInteger: isSafeInteger
, mathTrunc: mathTrunc
, forEachIn: forEachIn
, not: not
, trim: trim
, removeExtraSpaces: removeExtraSpaces
, createFunction: createFunction
, extend: extend
, mixin: mixin
, implement: implement
, Class: Class
, Interface: Interface
, Enum: Enum
};
});
})(this);
Файл async.js
;(function (name, context, definition) {
if (typeof module !== 'undefined' && module.exports) {
module.exports = definition();
} else if (typeof define === 'function' && define.amd) {
define(name, definition);
} else {
context[name] = definition();
}
})('async', this, function () {
// setImmediate polyfill для использования setImmediate в асинхронных циклах
(function (global, undefined) {
'use strict';
var tasks = (function () {
function Task(handler, args) {
this.handler = handler;
this.args = args;
}
Task.prototype.run = function () {
var scriptSource;
// Смотри шаги в секции 5 спецификации.
if (typeof this.handler === 'function') {
// Аргумент "this.args" не представлен в спецификации setImmediate,
// хотя "undefined" представлен в спецификации setTimeout:
// http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html
this.handler.apply(undefined, this.args);
} else {
scriptSource = '' + this.handler;
/* jshint evil: true */
eval(scriptSource);
}
};
var nextHandle = 1 // В спецификации указано: больше нуля
, tasksByHandle = {}
, currentlyRunningATask = false;
return {
addFromSetImmediateArguments: function (args) {
var handler = args[0]
, argsToHandle = Array.prototype.slice.call(args, 1)
, task = new Task(handler, argsToHandle)
, thisHandle = nextHandle++;
tasksByHandle[thisHandle] = task;
return thisHandle;
}
, runIfPresent: function (handle) {
// Из спецификации: "Дождитесь пока любые вызовы этого алгоритма, начатые до этого момента, завершатся."
// Поэтому, если мы запускаем задание, то мы должны сделать задержку перед его вызовом.
var task;
if (!currentlyRunningATask) {
task = tasksByHandle[handle];
if (task) {
currentlyRunningATask = true;
try {
task.run();
} catch (e) {
} finally {
delete tasksByHandle[handle];
currentlyRunningATask = false;
}
}
} else {
// Делаем задержку посредством setTimeout.
// Была попытка использовать вместо этого setImmediate,
// но в Firefox 7 это приводило к возникновению ошибки "too much recursion".
global.setTimeout(function(){tasks.runIfPresent(handle);}, 0);
}
}
, remove: function (handle) {
delete tasksByHandle[handle];
}
};
})();
function canUseNextTick() {
// Не дай себя обмануть окружению browserify.
return typeof process === 'object' && Object.prototype.toString.call(process) === '[object process]';
}
function canUseMessageChannel() {
return !!global.MessageChannel;
}
function canUsePostMessage() {
// Проверка на наличие "importScripts" предотвращает имплементацию внутри web worker,
// там где "global.postMessage" означает кое-что принципиально другое и не может использоваться для данного назначения.
if (!global.postMessage || global.importScripts) {
return false;
}
var postMessageIsAsynchronous = true
, oldOnMessage = global.onmessage;
global.onmessage = function () {postMessageIsAsynchronous = false;};
global.postMessage('', '*');
global.onmessage = oldOnMessage;
return postMessageIsAsynchronous;
}
function canUseReadyStateChange() {
return 'document' in global && 'onreadystatechange' in global.document.createElement('script');
}
function installNextTickImplementation(attachTo) {
attachTo.setImmediate = function () {
var handle = tasks.addFromSetImmediateArguments(arguments);
process.nextTick(function () {
tasks.runIfPresent(handle);
});
return handle;
};
}
function installMessageChannelImplementation (attachTo) {
var channel = new global.MessageChannel();
channel.port1.onmessage = function (event) {
var handle = event.data;
tasks.runIfPresent(handle);
};
attachTo.setImmediate = function () {
var handle = tasks.addFromSetImmediateArguments(arguments);
channel.port2.postMessage(handle);
return handle;
};
}
function installPostMessageImplementation (attachTo) {
// Устанавливаем обработчик событий на глобальный объект global для отслеживания события типа "message".
// Смотри документацию по ссылкам:
// https://developer.mozilla.org/en/DOM/window.postMessage
// http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages
var MESSAGE_PREFIX = 'com.bn.NobleJS.setImmediate' + Math.random();
function isStringAndStartsWith(string, putativeStart) {
return typeof string === 'string' && string.substring(0, putativeStart.length) === putativeStart;
}
function onGlobalMessage(event) {
// Эта функция будет перехватывать все входящие сообщения (даже приходящие из других открытых окон), поэтому необходимо
// исключить возможность срабатывания обработчика вхолостую. Мы проверяем, что все еще на ходимся в данном окне по
// наличию случайно сгенерированного значения префикса.
var handle;
if (event.source === global && isStringAndStartsWith(event.data, MESSAGE_PREFIX)) {
handle = event.data.substring(MESSAGE_PREFIX.length);
tasks.runIfPresent(handle);
}
}
if (global.addEventListener) {
global.addEventListener('message', onGlobalMessage, false);
} else {
global.attachEvent('onmessage', onGlobalMessage);
}
attachTo.setImmediate = function () {
var handle = tasks.addFromSetImmediateArguments(arguments);
// Отправляем сообщение через глобальный объект global самому себе с последующей обработкой и идентификацией префикса
// для запуска функции слушателя onGlobalMessage, описанной выше.
global.postMessage(MESSAGE_PREFIX + handle, '*');
return handle;
};
}
function installReadyStateChangeImplementation (attachTo) {
attachTo.setImmediate = function () {
var handle = tasks.addFromSetImmediateArguments(arguments);
// Создать тэг <script>. Его событие readystatechange быдет вызвано 1 раз асинхронно, как только он будет вставлен в документ.
// Делая так мы ставим задание в очередь. Помним о необходимости удалить тэг <script> после того, как событие будет вызвано.
var scriptEl = global.document.createElement('script');
scriptEl.onreadystatechange = function () {
tasks.runIfPresent(handle);
// Удаляем тэг <script>
scriptEl.onreadystatechange = null;
scriptEl.parentNode.removeChild(scriptEl);
scriptEl = null;
};
global.document.documentElement.appendChild(scriptEl);
return handle;
};
}
function installSetTimeoutImplementation (attachTo) {
attachTo.setImmediate = function () {
var handle = tasks.addFromSetImmediateArguments(arguments);
global.setTimeout(function(){tasks.runIfPresent(handle);}, 0);
return handle;
};
}
var attachTo;
if (!global.setImmediate) {
// Если возможно, то мы должны прикрепить setImmediate к prototype глобального объекта, в котором существует setTimeout
if (
typeof Object.getPrototypeOf === 'function'
&& 'setTimeout' in Object.getPrototypeOf(global)
) {
attachTo = Object.getPrototypeOf(global);
} else {
attachTo = global;
}
if (canUseNextTick()) {
installNextTickImplementation(attachTo); // Для Node.js версии ранее 0.9
} else if (canUsePostMessage()) {
installPostMessageImplementation(attachTo); // Для современных браузеров, кроме IE 10
} else if (canUseMessageChannel()) {
installMessageChannelImplementation(attachTo); // Для web workers, в тех браузерах, которые их поддерживают
} else if (canUseReadyStateChange()) {
installReadyStateChangeImplementation(attachTo); // Для IE 6-8
} else {
installSetTimeoutImplementation(attachTo); // Для остальных старых браузеров
}
attachTo.clearImmediate = tasks.remove;
}
})(typeof global === 'object' && global ? global : this);
// Асинхронные функции
var async = {}; // Объект, который будет хранить в себе перечень асинхронных функций
// Асинхронный цикл for
async.forLoop = function (params) {
params = params || {}; // Параметры асинхронного цикла
params.init = params.init || {}; // Начальные значения счетчиков цикла
params.condition = params.condition || function () {return true;}; // Функция, задающая и проверяющая условия выхода из цикла
params.iterate = params.iterate || function () {}; // Функция, выполняющая приращение счетчиков цикла
params.body = params.body || function () {}; // Тело цикла
params.done = params.done || function () {}; // Функция, выполняющаяся после завершения цикла
params.timeout = (params.timeout !== undefined) ? params.timeout : 0; // Время задержки в мс перед началом следующего шага цикла
// В общем случае для выполнения маленьких задержек рекомендуется использовать значение не менее 25 мс,
// потому что меньшие задержки оставляют слишком короткие интервалы времени для обновления пользовательского интерфейса.
// Однако для ускорения выполнения цикла оставлена задержка 0 мс, если время задержки специально не указано.
params.breakFunction = function () {}; // Функция, выполняющаясь при прерывания цикла
params.continueFunction = function () {}; // Функция, выполняющаясь при перепрыгивании на следующий шаг цикла
params.breakLoop = function (breakFunction) { // Определение вида прерывания цикла
if (breakFunction === undefined) {
return 'break';
} else {
params.breakFunction = breakFunction;
return 'break function';
}
};
params.continueLoop = function (continueFunction) { // Определение вида перепрыгивания на следующий шаг цикла
if (continueFunction === undefined) {
return 'continue';
} else {
params.continueFunction = continueFunction;
return 'continue function';
}
};
function executeBody () { // Функция, выполняющая тело цикла и проверяющая условия прерывания цикла
switch (params.body(params)) { // Выполнить тело цикла
case 'break': params.done(params); break; // Прервать цикл
case 'break function': params.breakFunction(params); break; // Прервать цикл и выполнить функцию, в которой описан переход во внешний цикл
case 'continue': params.next(); break; // Перепрыгнуть на следующий шаг цикла
case 'continue function': params.continueFunction(params); break; // Выполнить функцию, в которой описан переход во внешний цикл
}
}
params.counter = 0; // Счетчик числа выполненных шагов цикла
params.next = function () { // Выполнить шаг цикла
if (params.counter > 0) {
params.iterate(params); // Произвести приращение счетчиков цикла при выполнении следующего шага, за исключением первого шага
}
params.counter++;
if (params.condition(params)) { // Проверить тестовое условие выхода из цикла
if (params.timeout) { // Если время задержки в мс перед началом следующего шага цикла было установлено, то выполнить тело цикла с задержкой
setTimeout(executeBody, params.timeout);
} else { // Если время задержки в мс перед началом следующего шага цикла не было установлено или равно 0, то выполнить тело цикла немедленно
setImmediate(executeBody);
}
} else {
params.done(params); // Выполнить завершающую функцию в конце работы цикла
}
};
params.next(); // Выполнить проверку тестового условия цикла и затем выполнить первый шаг цикла
};
/* Примеры выполнения асинхронных циклов for
// Пример 1 - Одиночный асинхронный цикл
async.forLoop({
init: {i: 0}
, condition: function (loop) {if (loop.init.i < 10) {return true;} else {return false;}}
, iterate: function (loop) {loop.init.i = loop.init.i + 1;}
, body: function (loop) {
console.log(loop.init.i);
loop.next(); // Переход на следующий шаг цикла
}
, done: function (loop) {console.log('The End. Last index: ' + loop.init.i);}
});
// Пример 2 - Вложенные асинхронные циклы
// Внешний цикл - начало
async.forLoop({
init: {i: 0}
, condition: function (outerLoop) {if (outerLoop.init.i < 2) {return true;} else {return false;}}
, iterate: function (outerLoop) {outerLoop.init.i = outerLoop.init.i + 1;}
, body: function (outerLoop) {
// Внутренний цикл - начало
async.forLoop({
init: {j: 0}
, condition: function (innerLoop) {if (innerLoop.init.j < 2) {return true;} else {return false;}}
, iterate: function (innerLoop) {innerLoop.init.j = innerLoop.init.j + 1;}
, body: function (innerLoop) {
console.log(outerLoop.init.i + ' ' + innerLoop.init.j);
innerLoop.next(); // Переход на следующий шаг внутреннего цикла
}
, done: function (innerLoop) {
outerLoop.next(); // После завершения внутреннего цикла осуществляется переход на следующий шаг внешнего цикла
}
});
// Внутренний цикл - конец
}
, done: function (outerLoop) {
console.log('All cycles done');
}
});
// Внешний цикл - конец
// Пример 3 - Прерывание одиночного асинхронного цикла
async.forLoop({
init: {i: 0}
, condition: function (loop) {if (loop.init.i < 10) {return true;} else {return false;}}
, iterate: function (loop) {loop.init.i = loop.init.i + 1;}
, body: function (loop) {
if (loop.init.i === 3) {
// Перепрыгивание на следующий шаг цикла
return loop.continueLoop(
// Если в качестве метки label будет передана функция, то будет выполнен её код
// function(loop) {
// console.log('Continue label. Current index: ' + loop.init.i);
// loop.done(loop); // В результате перепрыгивания шага можно просто завершить выполнение цикла
// }
);
}
if (loop.init.i === 5) {
// Прерывание цикла
return loop.breakLoop(
// Если в качестве метки label будет передана функция, то будет выполнен её код
// function(loop) {
// console.log('Break label. Current index: ' + loop.init.i);
// loop.done(loop); // В результате прерывания цикла можно просто завершить выполнение цикла
// }
);
}
console.log(loop.init.i);
loop.next(); // Обычный переход на следующий шаг цикла
}
, done: function (loop) {
console.log('The End. Last index: ' + loop.init.i);
}
});
// Пример 4 - Прерывание вложенных асинхронных циклов
// Внешний цикл - начало
async.forLoop({
init: {i: 0}
, condition: function (outerLoop) {if (outerLoop.init.i < 5) {return true;} else {return false;}}
, iterate: function (outerLoop) {outerLoop.init.i = outerLoop.init.i + 1;}
, body: function (outerLoop) {
// Внутренний цикл - начало
async.forLoop({
init: {j: 0}
, condition: function (innerLoop) {if (innerLoop.init.j < 5) {return true;} else {return false;}}
, iterate: function (innerLoop) {innerLoop.init.j = innerLoop.init.j + 1;}
, body: function (innerLoop) {
if (innerLoop.init.j === 2) {
// Перепрыгивание на следующий шаг цикла
return innerLoop.continueLoop(
// Если в качестве метки label будет передана функция, то будет выполнен её код
// function(outerLoop){
// console.log('Continue label. Current index: ' + innerLoop.init.j);
// innerLoop.done(); // В результате перепрыгивания шага можно просто завершить выполнение внутреннего цикла
// }
);
}
if (innerLoop.init.j === 2) {
// Прерывание цикла
return innerLoop.breakLoop(
// Если в качестве метки label будет передана функция, то будет выполнен её код
// function(innerLoop){
// console.log('Break label. Current index: ' + innerLoop.init.j);
// outerLoop.done(outerLoop); // В результате прерывания цикла можно просто завершить выполнение внешнего цикла
// }
);
}
console.log(outerLoop.init.i + ' ' + innerLoop.init.j);
innerLoop.next(); // Обычный переход на следующий шаг внутреннего цикла
}
, done: function (innerLoop) {
outerLoop.next(); // После завершения внутреннего цикла осуществляется переход на следующий шаг внешнего цикла
}
});
// Внутренний цикл - конец
}
, done: function (outerLoop) {
console.log('All cycles done');
}
});
// Внешний цикл - конец
*/
// Асинхронный цикл forEach
async.forEachInArray = function (params) {
params = params || {}; // Параметры асинхронного цикла обработки массива
params.array = params.array || []; // Исходный массив, предназначенный для асинхронной обработки
params.process = params.process || function () {}; // Функция, выполняющая обработку элементов массива
params.done = params.done || function () {}; // Функция, выполняющаяся после завершения обработки всех элементов массива
params.processTimeLimit = (params.processTimeLimit !== undefined) ? params.processTimeLimit : 50; // Время в мс, отведенное для выполнения процесса обработки элементов массива
// Если процесс обработки массива занимает больше времени, чем указано в processTimeLimit,
// то следующий шаг обработки будет начат после некоторой задержки, указанной в params.timeout
// Для ускорения выполнения процесса обработки элементов массива ограничение времени по умолчанию оставлено равным 50 мс
params.timeout = (params.timeout !== undefined) ? params.timeout : 0; // Время задержки в мс перед началом следующего шага цикла по обработке элементов массива
// В общем случае для выполнения маленьких задержек рекомендуется использовать значение не менее 25 мс,
// потому что меньшие задержки оставляют слишком короткие интервалы времени для обновления пользовательского интерфейса.
// Однако для ускорения выполнения цикла оставлена задержка 0 мс, если время задержки специально не указано
// Мы создаем временный массив, в котором будем вести асинхронную обработку элементов, чтобы не повредить исходный массив,
// который в данный момент может обрабатываться другой асинхронной функцией
var tempArray = params.array.slice()
, tempArrayLength = tempArray.length
, currentTempArrayIndex = 0
, breakProcess;
if (tempArrayLength) {
startIterationProcess();
} else {
params.done(tempArray);
}
function startIterationProcess () {
var startTime = +new Date();
do {
breakProcess = params.process(tempArray[currentTempArrayIndex], currentTempArrayIndex, tempArray);
if (breakProcess === 'break') {
tempArrayLength = 0;
} else {
currentTempArrayIndex++;
}
} while (
currentTempArrayIndex < tempArrayLength
&& ((+new Date() - startTime) < params.processTimeLimit) // Установить таймаут, если цикл процесса обработки выполняется дольше установленного временного предела
);
if (currentTempArrayIndex < tempArrayLength) {
if (params.timeout) { // Если время задержки в мс перед началом следующего шага цикла было установлено, то выполнить тело цикла с задержкой
setTimeout(startIterationProcess, params.timeout);
} else { // Если время задержки в мс перед началом следующего шага цикла не было установлено или равно 0, то выполнить следующий шаг цикла немедленно
setImmediate(startIterationProcess);
}
} else {
params.done(tempArray);
}
}
};
/* Пример кода асинхронного цикла forEach
var arr = new Array(5000);
async.forEachInArray({
array: arr
, process: function (item, index, array) {
array[index] = item + '-' + index;
if (index === 2000) {
// Прервать выполнение итераций
return 'break';
}
}
, done: function (array) {
arr = array;
console.log('All done');
console.log(arr);
}
});
*/
// Асинхронная сортировка массива sort
/* Описание алгоритма сортировки массивов
Алгоритм сортировки массивов merge sort используют браузеры Firefox и Safari в своей реализации Array.prototype.sort()
Алгоритм merge sort быстр в работе и легок в реализации.
Он базируется на идее, что легче сравнить и слить два уже отсортированных списка,
нежели разбираться с одним несортированным списком.
Реализация алгоритма merge sort начинается с создания некоторого числа N одноэлементных массивов,
где N - общее число элементов в оригинальном массиве, который требуется отсортировать.
Затем эти одноэлементные массивы объединяются обратно в единый отсортированный массив.
Сравнение и слияние двух уже отсортированных списков представляет собой довольной простой алгоритм.
Представим, что у нас есть два списка: список A и список B.
Мы начинаем с самого начала каждого списка и сравниваем два их значения.
Меньшее из двух сравниваемых значений вставляется в итоговый массив.
Пусть к примеру меньшим окажется значение из списка A, тогда это значение будет помещено в итоговый массив.
Далее второе значение из списка A сравнивается с первым значением из списка B.
Снова меньшее из двух значений вставляется в итоговый массив.
Теперь для примера меньшим пусть будет число из списка B, тогда на следующем шаге будут сравниваться второй элемент из
списка A и второй элемент из списка B.
Код такого сравнения будет следующим:
function merge (leftArray, rightArray) {
var resultArray = []
, leftArrayLength = leftArray.length
, rightArrayLength = rightArray.length
, indexLeft = 0,
, indexRight = 0;
while (
indexLeft < leftArrayLength
&& indexRight < rightArrayLength
) {
if (leftArray[indexLeft] < rightArray[indexRight]) {
result.push(leftArray[indexLeft]);
indexLeft++;
} else {
result.push(rightArray[indexRight]);
indexRight++;
}
}
return resultArray.concat(leftArray.slice(indexLeft)).concat(rightArray.slice(indexRight));
}
В приведенной выше функции сравнивается и сливается два массива: левый и правый.
Переменная indexLeft хранит в себе индекс сравниваемого элемента из списка leftArray,
в то время как переменная indexRight хранит в себе индекс сравниваемого элемента из списка rightArray.
Кажлый раз, когда значение из одного массива добавляется в итоговый массив,
его соотвествующий индекс элемента увеличивается на единицу.
Как только один из массивов закончится, то оставшиеся значания будут добавлены в конец итогового массива
посредством операции concat().
Функция merge() работает просто, но теперь необходимо объединить два отсортированных списка.
Как было замечено ранее, это делается путем разделения массива на несколько одноэлементных массивов,
которые затем объединяются в списки.
Это можно легко сделать, применив рекурсию:
function mergeSort (array) {
var arrayLength = array.length;
// Условие выхода из функции
// Если число элементов в сортируемом массиве 0 или 1, то такой массив не требует сортировки
if (arrayLength < 2) {
return array;
}
var middle = Math.floor(arrayLength / 2) // определяем середину исходного массива
, leftArray = items.slice(0, middle) // левая половина исходного массива
, rightArray = items.slice(middle); // правая половина исходного массива
return merge(mergeSort(leftArray), mergeSort(rightArray)); // Рекурсивно сравниваем и сливаем два массива,
// периодически разделяя их пополам на более мелкие составляющие
}
Сперва стоит отметить, что если в сортируемом массивке 0 или 1 элемент, то такой массив не требует сортировки.
Если массив содержит в себе 2 или более значений, то он разделяется пополам на левый и правый массивы.
Каждый из этих массивов затем опять разделяется пополам, помещаясь обратно в функцию mergeSort(), после завершения которой
результат помещается в функцию merge(), где происходит сравнение и слияние двух маленьких массивов с
последующим помещением их в итоговый отсортированный массив.
Таким образом сортируется сначала левая половина массива, а затем сортируется правая часть массива,
после чего итоговые результаты сравниваются и объединяются.
Благодаря рекурсии вы в конечно итоге доходите до точки, где два одноэлементных массива сравниваются и сливаются в итоговый массив.
Имплементация алгоритма merge sort возвращает другой массив, отличающийся от того, который был передан для сортировки,
то есть это не сортировка массива на месте. Если вам требуется отсортировать исходный массив на месте,
то в этом случае вы можете очистить оригинальный массив и заполнить его отсортированным содержимым следующим образом:
function mergeSort (array) {
var arrayLength = array.length;
// Условие выхода из функции
// Если число элементов в сортируемом массиве 0 или 1, то такой массив не требует сортировки
if (arrayLength < 2) {
return array;
}
var middle = Math.floor(arrayLength / 2) // определяем середину исходного массива
, leftArray = array.slice(0, middle) // левая половина исходного массива
, rightArray = array.slice(middle) // правая половина исходного массива
, params = merge(mergeSort(leftArray), mergeSort(rightArray));
// Добавляем аргументы для замены всего, начиная с 0 и до последнего элемента в массиве
params.unshift(0, arrayLength);
array.splice.apply(array, params);
return array;
}
Эта версия функции mergeSort() хранит результат сортировки в переменной params.
Самый лучший способ заменить элементы в массиве - это использовать метод splice(), который принимает два и более аргументов.
Первый аргумент - это индекс первого заменяемого значения, а второй аргумент - это число элементов, которые надо заменить.
Каждый последующий аргумент - это значение, которое надо вставить на соотвествующщую позицию.
Поскольку нет способа передать значения в метод splice(), то необходимо использовать метод apply(),
в который передать первые два аргумента объединенные с отсортированным массивом.
Таким образом 0 и array.length добавляются в начало массива, используя метод unshift() так,
что apply() может быть использован совместно со splice().
После этого исходный массив возвращается из функции.
// Функция arrayMergeSort сортирует массив синхронно
// По умолчанию сортировка производится по возрастанию
// Параметр array - массив требующий сортировки
// Необязательный параметр compareFunction - функция, задающая порядок сравнения элементов массива
// Функция compareFunction должна возвращать:
// 1 - если первый сравниваемый элемент массива больше второго сравниваемого элемента
// -1 - если первый сравниваемый элемент массива меньше второго сравниваемого элемента
// 0 - если сравниваемые элементы массива равны друг другу
// Функция возвращает отсортированный массив
function arrayMergeSort (array, compareFunction) {
// для примера array = [5, 4, 6]
var work = []
, arrayLength = array.length // для примера arrayLength = 3
, limit
, i
, j
, k;
// Для массивов с числом элементов 0 или 1 сортировка не требуется
if (arrayLength < 2) {return array;}
// Фомируем массив, состоящий из одноэлементных массивов, для примера work = [[5], [4], [6]]
for (i = 0; i < arrayLength; i++){
work.push([array[i]]);
}
work.push([]); // на случай, если в массиве нечетное число элементов work = [[5], [4], [6], []]
// для примера изначально limit = 3, на каждом шаге цикла limit = Math.floor( (3 + 1) / 2 )), поскольку мы добавили пустой массив []
for (limit = arrayLength; limit > 1; limit = Math.floor( (limit + 1) / 2 )) {
for (j = 0, k = 0; k < limit; j++, k += 2) {
work[j] = merge(work[k], work[k + 1], compareFunction);
}
work[j] = []; // на случай, если в массиве нечетное число элементов
}
return work[0];
}
*/
async.sort = function (params) {
params = params || {}; // Параметры асинхронной сортировки массива
params.array = params.array || []; // Исходный массив, предназначенный для асинхронной сортировки
params.compare = params.compare || function (a, b) { // Функция, выполяющая сравнение элементов массива друг с другом
if (a > b) {
return 1;
} else if (a < b) {
return -1;
} else {
return 0;
}
};
params.done = params.done || function () {}; // Функция, выполняющаяся после завершения сортировки массива
// Для примера params.array = [5, 4, 6]
var work = []
, arrayLength = params.array.length; // Для примера arrayLength = 3
// Для массивов с числом элементов 0 или 1 сортировка не требуется
if (arrayLength < 2) {
params.done(params.array); // Возвращение исходного массива
return;
}
// Асинхронно фомируем массив, состоящий из одноэлементных массивов, для примера work = [[5], [4], [6]]
async.forEachInArray({
array: params.array
, process: addElementsToWorkArray
, done: startMergeProcess
});
// Добавляем элементы в рабочий массив work
function addElementsToWorkArray (item) {
work.push([item]);
}
// Начинаем асинхронный процесс сравнения и слияния элементов сортируемого массива
function startMergeProcess () {
work.push([]); // На случай, если в массиве нечетное число элементов work = [[5], [4], [6], []]
// Для примера изначально limit = 3, на каждом шаге цикла limit = Math.floor( (3 + 1) / 2 )), поскольку мы добавили пустой массив []
var limit = arrayLength;
async.forLoop({
init: {limit: limit}
, condition: function (outerLoop) {if (outerLoop.init.limit > 1) {return true;} else {return false;}}
, iterate: function (outerLoop) {outerLoop.init.limit = Math.floor( (outerLoop.init.limit + 1) / 2 );}
, body: function (outerLoop) {
async.forLoop({
init: {j: 0, k: 0}
, condition: function (innerLoop) {if (innerLoop.init.k < outerLoop.init.limit) {return true;} else {return false;}}
, iterate: function (innerLoop) {
innerLoop.init.j = innerLoop.init.j + 1;
innerLoop.init.k = innerLoop.init.k + 2;
}
, body: function (innerLoop) {
work[innerLoop.init.j] = merge(work[innerLoop.init.k], work[innerLoop.init.k + 1], params.compare); // Производим сравнение и слияние двух массивов
innerLoop.next(); // Переход на следующий шаг внутреннего цикла
}
, done: function (innerLoop) {
work[innerLoop.init.j] = []; // на случай, если в массиве нечетное число элементов
outerLoop.next(); // Переход на следующий шаг внешнего цикла
}
});
}
, done: function () { // Завершение внешнего цикла
params.done(work[0]); // Возвращение отсортированного массива
}
});
}
// Функция merge производит сравнение и слияние двух массивов по естесвенному порядку расположения чисел и строк в них
// Параметр leftArray - первый массив для сравнение и слияния
// Параметр rightArray - второй массив для сравнение и слияния
// Необязательный параметр compareFunction - функция, задающая порядок сравнения элементов массива
// Функция compareFunction должна возвращать:
// 1 - если первый сравниваемый элемент массива больше второго сравниваемого элемента
// -1 - если первый сравниваемый элемент массива меньше второго сравниваемого элемента
// 0 - если сравниваемые элементы массива равны друг другу
// Функция возвращает слитый массив
function merge (leftArray, rightArray, compareFunction) {
var resultArray = [];
/* Функция compareFunction подставляется по умолчанию выше, однако этот код нужен на случай, если функция merge() будет вынесена отдельно
if (compareFunction === undefined) {
compareFunction = function (a, b) { // Функция, выполяющая сравнение элементов массива друг с другом
if (a > b) {
return 1;
} else if (a < b) {
return -1;
} else {
return 0;
}
};
}
*/
while (leftArray.length > 0 && rightArray.length > 0) {
if (compareFunction(leftArray[0], rightArray[0]) === -1) {
resultArray.push(leftArray.shift());
} else {
resultArray.push(rightArray.shift());
}
}
resultArray = resultArray.concat(leftArray).concat(rightArray);
// Убедимся, что оставшиеся массивы пусты
leftArray.splice(0, leftArray.length);
rightArray.splice(0, rightArray.length);
return resultArray;
}
};
/* Примеры асинхронной сортировки массива
// Пример 1 - Асинхронная сортировка обычного массива
var array = []
, len = 999;
for (;len--;) {
array.push(len);
}
async.sort({
array: array
, done: function (result) {
array = result;
console.log(array);
}
});
// Пример 2 - Асинхронная сортировка массива, содержащего объекты, по убыванию
var array = [
{n: 5}
, {n: 4}
, {n: 1}
, {n: 3}
, {n: 2}
];
async.sort({
array: array
, compare: function (a, b) {
if (a.n < b.n) {
return 1;
} else if (a.n > b.n) {
return -1;
} else if (a.n === b.n) {
return 0;
}
}
, done: function (result) {
array = result;
console.dir(array);
}
});
*/
// Асинхронная перестановка элементов массива в обратном порядке reverse
async.reverse = function (params) {
params = params || {}; // Параметры функции асинхронной перестановки элементов массива в обратном порядке
params.array = params.array || []; // Исходный массив, предназначенный для асинхронной перестановки элементов в обратном порядке
params.done = params.done || function () {}; // Функция, выполняющаяся после завершения перестановки всех элементов массива
params.timeout = (params.timeout !== undefined) ? params.timeout : 0; // Время задержки в мс перед началом следующего шага цикла перестановки элементов массива
// В общем случае для выполнения маленьких задержек рекомендуется использовать значение не менее 25 мс,
// потому что меньшие задержки оставляют слишком короткие интервалы времени для обновления пользовательского интерфейса.
// Однако для ускорения выполнения цикла оставлена задержка 0 мс, если время задержки специально не указано
// Мы создаем временный массив, в котором будем вести асинхронную перестановку элементов, чтобы не повредить исходный массив,
// который в данный момент может обрабатываться другой асинхронной функцией
var tempArray = params.array.slice()
, tempArrayLength = tempArray.length
, result = [];
if (tempArrayLength > 2) {
async.forLoop({
init: {i: tempArrayLength}
, timeout: params.timeout
, condition: function (loop) {if (loop.init.i > 0) {return true;} else {return false;}}
, iterate: function (loop) {loop.init.i = loop.init.i - 1;}
, body: function (loop) {
result.push(tempArray.pop());
loop.next(); // Переход на следующий шаг цикла
}
, done: function () {
params.done(result);
}
});
} else {
params.done(tempArray);
}
};
/* Пример асинхронной перестановки элементов массива в обратном порядке
var array = []
, len = 999;
for (;len--;) {
array.push(len);
}
async.reverse({
array: array
, done: function (result) {
array = result;
console.log(array);
async.reverse({
array: array
, timeout: 1
, done: function (result) {
array = result;
console.log(array);
}
});
}
});
*/
// Выполнение асинхронных функций параллельно
// Все результаты выполнения асинхронных функций собираются и передаются в виде массива в итоговую функцию doneFunction()
// Данные из этого массива берутся согласно порядковому номеру записи асинхронной функции
// Функция doneFunction() будет вызвана после того, как выполнение всех асинхронных функций будет завершено
async.parallel = function (asyncFunctionsArray, doneFunction) {
if (Object.prototype.toString.call(asyncFunctionsArray) !== '[object Array]') {
throw new Error('First argument in async.parallel function must be array.');
}
if (doneFunction === undefined) {
doneFunction = function () {};
}
var asyncFunctionsArrayLength = asyncFunctionsArray.length
, counter = asyncFunctionsArrayLength // Счетчик хранит в себе число выполняющихся в данный момент асинхронных функций
, results = [] // В этот общий массив будут сохраняться результаты выполнения всех асинхронных функций
, i;
if (asyncFunctionsArrayLength) {
for (i = 0; i < asyncFunctionsArrayLength; i++) {
asyncFunctionsArray[i](makeCallback(i)); // Запустить функцию makeCallback() внутри каждой асинхронной функции, когда её выполнение завершится
}
} else {
doneFunction(results);
}
// Функция makeCallback() будет вызываться в каждой асинхронной функции в случае её завершения
// Она принимает порядковый индекс (номер) асинхронной функции для отслеживания того, какие результаты к какой функции относятся
function makeCallback (index) {
return function () { // Функция makeCallback() возвращает функцию, которая будет вызвана в каждой асинхронной функции в случае её завершения
// Здесь мы используем объект функции arguments, поскольку некоторые асинхронные функции могут возвращать несколько значений сразу
// Все итоговые результаты из асинхронной функции, переданные в качестве аргументов, добавляются в общий массив результатов в соотвествии с индексом,
// который был присвоен данной функции
results[index] = Array.prototype.slice.call(arguments);
counter--; // При завершении работы асинхронной функции счетчик работающих в данный момент асинхронных функций будет уменьшаться на единицу
// Далее производится проверка все ли асинхронные функции уже завершили свою работу
// Если все асинхронные функции уже завершили работу, то счетчик будет равен 0
// Следовательно пора запускать итоговую функцию, которая должна быть запущена после завершения всех асинхронных функций
if (counter === 0) {
doneFunction(results);
}
};
}
};
/* Примеры выполнения асинхронных функций параллельно
// Пример 1 - Асинхронные функции без аргументов
function a (callback) {
setTimeout(function(){
console.log('first');
callback(1, 2);
}, 3000);
}
function b (callback) {
setTimeout(function(){
console.log('second');
callback(3, 4);
}, 1000);
}
function c (callback) {
setTimeout(function(){
console.log('third');
callback(5, 6);
}, 2000);
}
function done (results) {
console.log('done');
console.log(results[0]);
console.log(results[1]);
console.log(results[2]);
}
async.parallel([a,b,c], done);
// Пример 2 - Асинхронные функции с аргументами
function a (x, y, callback) {
setTimeout(function(){
console.log('first');
callback(x, y);
}, 3000);
}
function b (x, y, callback) {
setTimeout(function(){
console.log('second');
callback(x, y);
}, 1000);
}
function c (x, y, callback) {
setTimeout(function(){
console.log('third');
callback(x, y);
}, 2000);
}
function done (message, results) {
console.log(message);
console.log(results[0]);
console.log(results[1]);
console.log(results[2]);
}
async.parallel([
function (callback) { a(1, 2, callback); }
, function (callback) { b(3, 4, callback); }
, function (callback) { c(5, 6, callback); }
], function (results) { done('done', results); });
*/
// Выполнение асинхронных функций по очереди
// Все результаты выполнения асинхронных функций собираются и передаются в виде массива в итоговую функцию doneFunction()
// Данные из этого массива берутся согласно порядковому номеру записи асинхронной функции
// Функция doneFunction() будет вызвана после того, как все асинхронные функции будут выполнены
async.queue = function (asyncFunctionsArray, doneFunction) {
if (Object.prototype.toString.call(asyncFunctionsArray) !== '[object Array]') {
throw new Error('First argument in async.queue function must be array.');
}
if (doneFunction === undefined) {
doneFunction = function () {};
}
var asyncFunctionsArrayLength = asyncFunctionsArray.length
, counter = asyncFunctionsArrayLength // Счетчик хранит в себе число оставшихся в очереди асинхронных функций
, results = [] // В этот общий массив будут сохраняться результаты выполнения всех асинхронных функций
, i = 0; // Индекс текущей выполняемой асинхронной функции
if (asyncFunctionsArrayLength) {
asyncFunctionsArray[i](makeCallback(i)); // Запустить функцию makeCallback() внутри первой асинхронной функции, когда её выполнение завершится
} else {
doneFunction(results);
}
// Функция makeCallback() будет вызываться в каждой асинхронной функции в случае её завершения
// Она принимает порядковый индекс (номер) асинхронной функции для отслеживания того,
// какие результаты к какой функции относятся и какую асинхронную функцию нужно запускать следующей
function makeCallback (index) {
return function () { // Функция makeCallback() возвращает функцию, которая будет вызвана в асинхронной функции в случае её завершения
// Здесь мы используем объект функции arguments, поскольку некоторые асинхронные функции могут возвращать несколько значений сразу
// Все итоговые результаты из асинхронной функции, переданные в качестве аргументов, добавляются в общий массив результатов в соотвествии с индексом,
// который был присвоен данной функции
results[index] = Array.prototype.slice.call(arguments);
counter--; // При завершении работы асинхронной функции счетчик оставшихся в очереди асинхронных функций будет уменьшаться на единицу
i++; // А индекс текущей выполняемой асинхронной функции будет увеличен на единицу, чтобы можно было вызвать следующую асинхронную функцию из очереди
// Далее производится проверка все ли асинхронные функции уже завершили свою работу
// Если все асинхронные функции уже завершили работу, то счетчик оставшихся в очереди асинхронных функций будет равен 0
// Следовательно пора запускать итоговую функцию, которая должна быть запущена после завершения всех асинхронных функций
if (counter === 0) {
doneFunction(results);
} else { // В противном случае мы вызваем следующую асинхронную функцию из очереди
asyncFunctionsArray[i](makeCallback(i));
}
};
}
};
/* Примеры выполнения асинхронных функций по очереди
// Пример 1 - Асинхронные функции без аргументов
function a (callback) {
setTimeout(function(){
console.log('first');
callback(1, 2);
}, 3000);
}
function b (callback) {
setTimeout(function(){
console.log('second');
callback(3, 4);
}, 1000);
}
function c (callback) {
setTimeout(function(){
console.log('third');
callback(5, 6);
}, 2000);
}
function done (results) {
console.log('done');
console.log(results[0]);
console.log(results[1]);
console.log(results[2]);
}
async.queue([a,b,c], done);
// Пример 2 - Асинхронные функции с аргументами
function a (x, y, callback) {
setTimeout(function(){
console.log('first');
callback(x, y);
}, 3000);
}
function b (x, y, callback) {
setTimeout(function(){
console.log('second');
callback(x, y);
}, 1000);
}
function c (x, y, callback) {
setTimeout(function(){
console.log('third');
callback(x, y);
}, 2000);
}
function done (message, results) {
console.log(message);
console.log(results[0]);
console.log(results[1]);
console.log(results[2]);
}
async.queue([
function (callback) { a(1, 2, callback); }
, function (callback) { b(3, 4, callback); }
, function (callback) { c(5, 6, callback); }
], function (results) { done('done', results); });
*/
// Выполнение асинхронных функций по очереди каскадом
// Результат выполнения предыдущей асинхронной функции будет передаваться в следующую вызываемую асинхронную функцию
// Результат выполнения последней асинхронной функции передается в итоговую функцию doneFunction()
// Функция doneFunction() будет вызвана после того, как все асинхронные функции будут успешно выполнены
// В случае ошибки хотя бы в одной из асинхронных функций из очереди, выполнение последующих асинхронных функций будет прервано и будет
// вызвана функция errorFunction(), куда будут переданы успешно полученные из предыдущей асинзронной функции результаты
// и индекс текущей асинхронной функции, в которой произошла ошибка
async.cascade= function (asyncFunctionsArray, doneFunction, errorFunction) {
if (Object.prototype.toString.call(asyncFunctionsArray) !== '[object Array]') {
throw new Error('First argument in async.cascade function must be array.');
}
if (doneFunction === undefined) {
doneFunction = function () {};
}
if (errorFunction === undefined) {
errorFunction = function () {};
}
var asyncFunctionsArrayLength = asyncFunctionsArray.length
, counter = asyncFunctionsArrayLength // Счетчик хранит в себе число оставшихся в очереди асинхронных функций
, results = [] // В этот массив будут сохраняться результаты выполнения асинхронной функций
, errors = [] // В этот массив будут сохраняться данные об ошибке в случае нейспешного завершения асинхронной функции
, i = 0; // Индекс текущей выполняемой асинхронной функции
if (asyncFunctionsArrayLength) {
asyncFunctionsArray[i](makeSuccessCallback(), makeErrorCallback()); // Запустить функцию makeSuccessCallback() или функцию makeErrorCallback()
// внутри первой асинхронной функции, когда её выполнение завершится
} else {
doneFunction(results);
}
// Функция makeSuccessCallback() будет вызываться в каждой асинхронной функции в случае её успешного завершения
function makeSuccessCallback () {
return function () { // Функция makeSuccessCallback() возвращает функцию, которая будет вызвана в асинхронной функции в случае её успешного завершения
// Здесь мы используем объект функции arguments, поскольку некоторые асинхронные функции могут возвращать несколько значений сразу
// Все итоговые результаты из асинхронной функции, переданные в качестве аргументов, добавляются в массив результатов
results = Array.prototype.slice.call(arguments);
counter--; // При завершении работы асинхронной функции счетчик оставшихся в очереди асинхронных функций будет уменьшаться на единицу
i++; // А индекс текущей выполняемой асинхронной функции будет увеличен на единицу, чтобы можно было вызвать следующую асинхронную функцию из очереди
// Далее производится проверка все ли асинхронные функции уже завершили свою работу
// Если все асинхронные функции уже завершили работу, то счетчик оставшихся в очереди асинхронных функций будет равен 0
// Следовательно пора запускать итоговую функцию, которая должна быть запущена после завершения всех асинхронных функций
if (counter === 0) {
doneFunction(results);
} else { // В противном случае мы вызваем следующую асинхронную функцию из очереди
if (asyncFunctionsArray.length) { // Проверяем длину массива на случай, если предыдущая асинхронная функций завершилась неуспешно
asyncFunctionsArray[i](makeSuccessCallback(), makeErrorCallback(), results);
}
}
};
}
// Функция makeErrorCallback() будет вызываться в каждой асинхронной функции в случае её неуспешного завершения
function makeErrorCallback () {
return function () { // Функция makeErrorCallback() возвращает функцию, которая будет вызвана в асинхронной функции в случае её неуспешного завершения
asyncFunctionsArrayLength = [];
counter = -1;
errors = Array.prototype.slice.call(arguments);
errorFunction(results, errors, i); // Мы прерываем цепочку вызова асинхронных функций и передаем управление в функцию обработки ошибок
};
}
};
/* Примеры выполнения асинхронных функций по очереди каскадом
// Пример 1 - Успешное завершение всех асинхронных функций без аргументов
function a (success, error) {
setTimeout(function(){
console.log('first');
success(1, 2);
}, 3000);
}
function b (success, error, results) {
console.log('results from a');
console.log(results);
setTimeout(function(){
console.log('second');
success(3, 4);
}, 1000);
}
function c (success, error, results) {
console.log('results from b');
console.log(results);
setTimeout(function(){
console.log('third');
success(5, 6);
}, 2000);
}
function done (results) {
console.log('done');
console.log('results from c');
console.log(results);
}
function error (results, index) {
console.log('error in function with index: ' + index);
console.log('results');
for (var i = 0, len = results.length; i < len; i++) {
console.log(results[i]);
}
}
async.cascade([a,b,c], done, error);
// Пример 2 - Успешное завершение всех асинхронных функций с аргументами
function a (success, error, num) {
console.log(num);
setTimeout(function(){
console.log('first');
success(1, 2);
}, 3000);
}
function b (success, error, results, num) {
console.log(num);
console.log('results from a');
console.log(results);
setTimeout(function(){
console.log('second');
success(3, 4);
}, 1000);
}
function c (success, error, results, num) {
console.log(num);
console.log('results from b');
console.log(results);
setTimeout(function(){
console.log('third');
success(5, 6);
}, 2000);
}
function done (results, message) {
console.log(message);
console.log('done');
console.log('results from c');
console.log(results);
}
function error (results, index, message) {
console.log(message);
console.log('error in function with index: ' + index);
console.log('results');
for (var i = 0, len = results.length; i < len; i++) {
console.log(results[i]);
}
}
async.cascade(
[
function (success, error) { a(success, error, 1); }
, function (success, error, results) { b(success, error, results, 2); }
, function (success, error, results) { c(success, error, results, 3); }
]
, function (results) { done(results, 'Well done'); }
, function (results, index) { error(results, index, 'Super error'); }
);
// Пример 3 - Неуспешное завершение третьей асинхронной функции без аргументов
function a (success, error) {
var successTimeout = setTimeout(function(){
console.log('first - success');
success(1, 2, errorTimeout);
}, 3000);
var errorTimeout = setTimeout(function(){
console.log('first - error');
error(successTimeout);
}, 5000);
}
function b (success, error, results) {
clearTimeout(results[2]);
console.log('results from a');
console.log(results);
var successTimeout = setTimeout(function(){
console.log('second - success');
success(3, 4, errorTimeout);
}, 1000);
var errorTimeout = setTimeout(function(){
console.log('second - error');
error(successTimeout);
}, 5000);
}
function c (success, error, results) {
clearTimeout(results[2]);
console.log('results from b');
console.log(results);
var successTimeout = setTimeout(function(){
console.log('third - success');
success(5, 6, errorTimeout);
}, 2000);
var errorTimeout = setTimeout(function(){
console.log('third - error');
error(successTimeout);
}, 10);
}
function done (results) {
clearTimeout(results[2]);
console.log('done');
console.log('results from c');
console.log(results);
}
function error (results, errors, index) {
clearTimeout(errors[0]);
console.log('error in function with index: ' + index);
console.log('results');
for (var i = 0, len = results.length; i < len; i++) {
console.log(results[i]);
}
console.log('errors');
for (i = 0, len = errors.length; i < len; i++) {
console.log(errors[i]);
}
}
async.cascade([a,b,c], done, error);
// Пример 4 - Неуспешное завершение третьей асинхронной функции с аргументами
function a (success, error, num) {
console.log(num);
var successTimeout = setTimeout(function(){
console.log('first - success');
success(1, 2, errorTimeout);
}, 3000);
var errorTimeout = setTimeout(function(){
console.log('first - error');
error(successTimeout);
}, 5000);
}
function b (success, error, results, num) {
console.log(num);
clearTimeout(results[2]);
console.log('results from a');
console.log(results);
var successTimeout = setTimeout(function(){
console.log('second - success');
success(3, 4, errorTimeout);
}, 1000);
var errorTimeout = setTimeout(function(){
console.log('second - error');
error(successTimeout);
}, 5000);
}
function c (success, error, results, num) {
console.log(num);
clearTimeout(results[2]);
console.log('results from b');
console.log(results);
var successTimeout = setTimeout(function(){
console.log('third - success');
success(5, 6, errorTimeout);
}, 2000);
var errorTimeout = setTimeout(function(){
console.log('third - error');
error(successTimeout);
}, 10);
}
function done (results, message) {
console.log(message);
clearTimeout(results[2]);
console.log('done');
console.log('results from c');
console.log(results);
}
function error (results, errors, index, message) {
console.log(message);
clearTimeout(errors[0]);
console.log('error in function with index: ' + index);
console.log('results');
for (var i = 0, len = results.length; i < len; i++) {
console.log(results[i]);
}
console.log('errors');
for (i = 0, len = errors.length; i < len; i++) {
console.log(errors[i]);
}
}
async.cascade(
[
function (success, error) { a(success, error, 1); }
, function (success, error, results) { b(success, error, results, 2); }
, function (success, error, results) { c(success, error, results, 3); }
]
, function (results) { done(results, 'Well done'); }
, function (results, errors, index) { error(results, errors, index, 'Super error'); }
);
*/
// Выполнение асинхронных функций последовательно друг за другом
// Обещание для выстраивания цепочки вызовов асинхронных функций
async.Promise = function () {
// Создается отдельно для каждого объекта класса Promise
this.queue = []; // Массив очереди ожидающих вызова асинхронных функций
};
// Методы обещания
async.Promise.prototype = {
then: function(asyncFunctionsObject) { // then() создает цепочку вызовов асинхронных функций
asyncFunctionsObject = asyncFunctionsObject || {}; // Набор функций, выполняющихся после завершения работы предыдущей асинхронной функции
asyncFunctionsObject.success = asyncFunctionsObject.success || function () {}; // Функция, выполняющаяся в случае успешного завершения предыдущей асинхронной функции
asyncFunctionsObject.error = asyncFunctionsObject.error || function () {}; // Функция, выполняющаяся в случае неуспешного завершения предыдущей асинхронной функции
this.queue.push(asyncFunctionsObject); // Функции-коллбаки записываются в объект, который записывается в массив очереди ожидающих вызова асинхронных функций
return this; // Возвращаем созданный ранее объект promise для передачи его в следующую функцию then()
}
, success: function(result) { // success() вызывается когда предыдущая асинхронная функция успешно завершается
if (this.queue.length) {
this.queue.shift().success(this, result); // В случае успешного завершения предыдущей асинхронной функции мы запускаем следующую асинхронную функцию из очереди
}
}
, error: function(result) { // error() вызывается когда предыдущая асинхронная функция завершается неуспешно
if (this.queue.length) {
this.queue.shift().error(result); // В случае неуспешного завершения предыдущей асинхронной функции мы запускаем функцию, обрабатывающую ошибку
this.queue = []; // и прерываем цепочку последующих вызовов, очищая очередь асинхронных функций
}
}
};
// Последовательный вызов асинхронных функций when -> then
// В случае успешного завершения предыдущей асинхронной функции будет вызывать следующая асинхронная функция из цепочки
// Результат выполнения предыдущей асинхронной функции будет передаваться в следующую вызываемую асинхронную функцию
// В случае неуспешного завершения предыдущей асинхронной функции будет вызывать следующая функция для обработки ошибок из цепочки
// после чего дальнейшее выполнени цепочки будет прервано
async.when = function (asyncFunction) {
var promise = new async.Promise();
asyncFunction(promise);
return promise;
};
/* Примеры выполнения асинхронных функций последовательно
// Пример 1 - Успешное завершение всех асинхронных функций
async.when(async1
).then({
success: async2
, error: error2
}).then({
success: async3
, error: error3
}).then({
success: done
, error: errorDone
});
function async1 (promise) {
console.log('async1 - Start');
var successTimeout = setTimeout(function(){promise.success([errorTimeout, 'async1 - Success'])}, 3000);
var errorTimeout = setTimeout(function(){promise.error([successTimeout, 'async1 - Error'])}, 4000);
}
function async2 (promise, result) {
clearTimeout(result[0]);
console.log(result[1]);
console.log('async2 - Start');
var successTimeout = setTimeout(function(){promise.success([errorTimeout, 'async2 - Success'])}, 2000);
var errorTimeout = setTimeout(function(){promise.error([successTimeout, 'async2 - Error'])}, 3000);
}
function async3 (promise, result) {
clearTimeout(result[0]);
console.log(result[1]);
console.log('async3 - Start');
var successTimeout = setTimeout(function(){promise.success([errorTimeout, 'async3 - Success'])}, 1000);
var errorTimeout = setTimeout(function(){promise.error([successTimeout, 'async3 - Error'])}, 2000);
}
function done (promise, result) {
clearTimeout(result[0]);
console.log(result[1]);
console.log('All done');
}
function error2 (result) {
clearTimeout(result[0]);
console.log(result[1]);
}
function error3 (result) {
clearTimeout(result[0]);
console.log(result[1]);
}
function errorDone (result) {
clearTimeout(result[0]);
console.log(result[1]);
}
// Пример 2 - Неуспешное завершение второй асинхронной функцией - прерывание последующей цепочки вызовов
async.when(async1
).then({
success: async2
, error: error2
}).then({
success: async3
, error: error3
}).then({
success: done
, error: errorDone
});
function async1 (promise) {
console.log('async1 - Start');
var successTimeout = setTimeout(function(){promise.success([errorTimeout, 'async1 - Success'])}, 3000);
var errorTimeout = setTimeout(function(){promise.error([successTimeout, 'async1 - Error'])}, 4000);
}
function async2 (promise, result) {
clearTimeout(result[0]);
console.log(result[1]);
console.log('async2 - Start');
var successTimeout = setTimeout(function(){promise.success([errorTimeout, 'async2 - Success'])}, 2000);
var errorTimeout = setTimeout(function(){promise.error([successTimeout, 'async2 - Error'])}, 10);
}
function async3 (promise, result) {
clearTimeout(result[0]);
console.log(result[1]);
console.log('async3 - Start');
var successTimeout = setTimeout(function(){promise.success([errorTimeout, 'async3 - Success'])}, 1000);
var errorTimeout = setTimeout(function(){promise.error([successTimeout, 'async3 - Error'])}, 2000);
}
function done (promise, result) {
clearTimeout(result[0]);
console.log(result[1]);
console.log('All done');
}
function error2 (result) {
clearTimeout(result[0]);
console.log(result[1]);
}
function error3 (result) {
clearTimeout(result[0]);
console.log(result[1]);
}
function errorDone (result) {
clearTimeout(result[0]);
console.log(result[1]);
}
*/
return async; // Возвращаем объект, хранящий в себе перечень асинхронных функций из модуля
});
Файл convertobjecttohtmltext.js
define(function(){
// Преобразование содержимого объекта в текст для вставки в HTML-код.
// Параметры:
// obj - конвертируемый объект.
// Пример использовния :
// var obj = {
// a: 1
// , b: null
// , c: undefined
// , d: 'text<br/>text'
// , e: [1,2,3]
// , f: {objkey: 'objvalue'}
// , g: function () {console.log(1);}
// , h: new Date()
// , i: true
// , j: new RegExp('a{1,2}')
// , k: new Error('some error')
// , l: NaN
// , m: []
// , n: {}
// };
// convertObjectToHtmlText(obj);
// Возвращает отформатированную строку, представляющую содержимое объекта, пригодную для вставки в HTML-код.
// Возвращает сообщение об ошибке, если в функцию не был передан объект.
// Преобразовать содержимое объекта в текст для вставки в HTML-код
function convertObjectToHtmlText (obj) {
var indentString = ' '
, newLine = '<br />'
, newLineJoin = ',' + newLine;
// Функция определения типа объекта
function objectType (obj) {
var types = {
'null': 'null'
, 'undefined': 'undefined'
, 'number': 'number'
, 'boolean': 'boolean'
, 'string': 'string'
, '[object Function]': 'function'
, '[object RegExp]': 'regexp'
, '[object Array]': 'array'
, '[object Date]': 'date'
, '[object Error]': 'error'
};
return types[typeof obj] || types[Object.prototype.toString.call(obj)] || (obj ? 'object' : 'null');
}
// Функция определения числа элементов в объекте
function objectSize (obj) {
var size = 0
, key;
for (key in obj) {
if (obj.hasOwnProperty(key)) {size++;}
}
return size;
}
// Привести элемент к нужному формату
function formatElement (element, indent, indentFromArray) {
indentFromArray = indentFromArray || '';
switch (objectType(element)) {
case 'null': return indentFromArray + 'null';
case 'undefined': return indentFromArray + 'undefined';
case 'number': return indentFromArray + element;
case 'boolean': return indentFromArray + (element ? 'true' : 'false');
case 'string': return indentFromArray + element.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/, '"')
.replace(/'/g, ''');
case 'array': return indentFromArray + (element.length > 0 ? '[' + newLine + formatArray(element, indent) + indent + ']' : '[]');
case 'object': return indentFromArray + (objectSize(element) > 0 ? '{' + newLine + formatObject(element, indent) + indent + '}' : '{}');
default: return indentFromArray + element.toString();
}
}
// Привести массив к нужному формату
function formatArray (array, indent) {
var index
, length = array.length
, value = [];
indent += indentString;
for (index = 0; index < length; index += 1) {
value.push(formatElement(array[index], indent, indent));
}
return value.join(newLineJoin) + newLine;
}
// Привести объект к нужному формату
function formatObject (object, indent) {
var value = []
, property;
indent += indentString;
for (property in object) {
if (object.hasOwnProperty(property)) {
value.push(indent + property + ': ' + formatElement(object[property], indent));
}
}
return value.join(newLineJoin) + newLine;
}
if (typeof obj === 'object') {return formatElement(obj, '');
} else {throw new Error ('No javascript object was provided to function convertObjectToHtmlText (obj) {...}');
}
}
return convertObjectToHtmlText;
});
Файл decoder.js
define(function(){
// Кодирование и декодирование значений.
// Параметры:
// value - кодируемое или декодируемое значение.
// decodeArray - массив, позволяющий соспоставить значение и его код, например:
// decodeArray = [ ['value1', 'code1'], ['value2', 'code2'] ]
// operation - тип операции: кодирование - 'encode' или декодирование - 'decode'.
// Пример использовния для кодирования значения:
// decoder.encode('value1', [['value1', 'code1'], ['value2', 'code2']]);
// Возвращает 'code1' в случае успешного кодирования.
// Возвращает пустую строку '', если значение не удалось закодировать.
// Пример использовния для декодирования значения:
// decoder.decode('code1', [['value1', 'code1'], ['value2', 'code2']]);
// Возвращает 'value1' в случае успешного раскодирования.
// Возвращает пустую строку '', если значение не удалось раскодировать.
var decoder = {
encode: function (value, decodeArray) {
return this._encodeOrDecodeValue(value, decodeArray, 'encode');
}
, decode: function (value, decodeArray) {
return this._encodeOrDecodeValue(value, decodeArray, 'decode');
}
, _encodeOrDecodeValue: function (value, decodeArray, operation) {
var decodeArrayLength = decodeArray.length
, result = ''
, i, j, k;
if (operation === 'encode') {
j = 0; k = 1;
} else if (operation === 'decode') {
j = 1; k = 0;
}
for (i = 0; i < decodeArrayLength; i++) {
if (value === decodeArray[i][j]) {
result = decodeArray[i][k];
}
}
return result;
}
};
return decoder;
});
Комментариев нет:
Отправить комментарий