Commit 32887cce authored by Boris Kocherov's avatar Boris Kocherov

add CUBEMEMBER, CUBEVALUE

CUBEMEMBER and CUBEVALUE functions has been implemented, using xmla
library.

For this purpose the support of lazy calculations in parserFormula has
been added. Now, during the implementantion of a function a method for
lazy calculations  (CalculateLazy) can be defined. CalculateLazy returns
not a value, as it did Calculation, but RSVP.Queue. And now it is the
returned queue that is to return the value which is the result of the
calculations. During the calculation of CUBEVALUE the calculation
process waits until all the calculations of all the arguments are
finished. It is made to send an EXECUTE request to olap. This request
guarantees the consistency of the returned values. In case of success
CUBEMEMBER.CalculateLazy assigns mdx-expression to cube_value to form
the request and calculate its result. Functions are marked as those
which are calculated every time the document is being opened (.ca=true).
changeOffsetElem, which return the element of the olap-tree, adjacent to
the current one on the same level, have been implemented. It helps to
fill the table.
TODO:
Make the editor of connections to OLAP-servers. In the current
implementation connection parameters are hardcoded. The current
parameters describe a connection to SAIKU olap demoserver, which
supports XMLA-requests.
Use PivotCache as an intermediate structure to store the requests'
results. It will allow to use PivotTable to display the results.

In Russian:
Реализованы CUBEMEMBER, CUBEVALUE функции используя xmla библиотеку.

Для этого добавлена поддержка ленивых вычислений в parserFormula.При
реализации функции теперь можно опредилить метод для ленивых вычислений
CalculateLazy.  CalculateLazy возвращает очередь (RSVP.Queue), а не
значение, как при обычных вычислениях. И уже возвращаемая очередь должна
вернуть значение, которое является результатом вычисления. При
вычислении CUBEVALUE происходит ожидание вычисления всех аргументов всех
CUBEVALUE. Это сделано, для того чтобы посылать один запрос EXECUTE к
olap, что гарантирует согласованность значений, полученых в результате.
CUBEMEMBER.CalculateLazy в случае успеха присваивает cube_value mdx-
выражение которое использовано для вычисления
CUBEMEMBER в свою очередь CUBEVALUE использует cube_value для
формирования запроса и вычисления своего результата. Функции
промаркированы как вычисляемые при каждом открытии документа
(.ca=true). Реализованы changeOffsetElem, которые возвращают элемент
olap-дерева, соседний по отношению к текущему на том же уровне, что
позволяет быстро заполнять таблицу.
TODO:
Сделать редактор подключений к OLAP серверам. В текущей реализации
параметры соединений прописаны прямо в коде. Текущие параметры описывают
соединение с SAIKU olap demoserver, который поддерживает XMLA.
Использовать PivotCache как промежуточную структуру для хранения
результатов запросов, что позволит использовать PivotTable для
отображения результатов.
parent fbf10a1b
......@@ -273,6 +273,7 @@
"../common/externs/jquery-3.2.js",
"../common/externs/xregexp-3.0.0.js",
"../common/externs/sockjs.js",
"../common/externs/Xmla.js",
"../common/externs/jszip.min.js",
"../common/externs/jszip-utils.js"
],
......
/* jshint -W040 */
/*
* (c) Copyright Ascensio System SIA 2010-2017
* Copyright (c) 2017 Nexedi SA and Contributors. All Rights Reserved.
* Author: Boris Kocherov
*
* This extension was developed by Nexedi as part of
* OpenPaaS::NG PSPC collaborative R&D project financed by BPI France
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation. In accordance with
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement
* of any third-party rights.
* version 3 as published by the Free Software Foundation.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia,
* EU, LV-1021.
* You can contact jp@nexedi.com.
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices, as required under
......@@ -34,19 +35,512 @@
(/**
* @param {Window} window
* @param {Object} RSVP
* @param {Xmla} Xmla
* @param {console} console
* @param {undefined} undefined
*/
function (window, undefined) {
function (window, RSVP, Xmla, console, undefined) {
var cBaseFunction = AscCommonExcel.cBaseFunction;
var cFormulaFunctionGroup = AscCommonExcel.cFormulaFunctionGroup;
var cFormulaFunctionGroup = AscCommonExcel.cFormulaFunctionGroup,
cElementType = AscCommonExcel.cElementType,
cNumber = AscCommonExcel.cNumber,
cString = AscCommonExcel.cString,
cBool = AscCommonExcel.cBool,
cError = AscCommonExcel.cError,
cErrorType = AscCommonExcel.cErrorType,
cArea = AscCommonExcel.cArea,
cArea3D = AscCommonExcel.cArea3D,
cRef = AscCommonExcel.cRef,
cRef3D = AscCommonExcel.cRef3D,
cEmpty = AscCommonExcel.cEmpty,
cArray = AscCommonExcel.cArray,
cubeScheme = {},
cubeExecutionScheme = {};
cFormulaFunctionGroup['Cube'] = cFormulaFunctionGroup['Cube'] || [];
cFormulaFunctionGroup['Cube'].push(cCUBEKPIMEMBER, cCUBEMEMBER, cCUBEMEMBERPROPERTY, cCUBERANKEDMEMBER, cCUBESET,
cFormulaFunctionGroup.Cube = cFormulaFunctionGroup.Cube || [];
cFormulaFunctionGroup.Cube.push(cCUBEKPIMEMBER, cCUBEMEMBER, cCUBEMEMBERPROPERTY, cCUBERANKEDMEMBER, cCUBESET,
cCUBESETCOUNT, cCUBEVALUE);
cFormulaFunctionGroup['NotRealised'] = cFormulaFunctionGroup['NotRealised'] || [];
cFormulaFunctionGroup['NotRealised'].push(cCUBEKPIMEMBER, cCUBEMEMBER, cCUBEMEMBERPROPERTY, cCUBERANKEDMEMBER,
cCUBESET, cCUBESETCOUNT, cCUBEVALUE);
cFormulaFunctionGroup.NotRealised = cFormulaFunctionGroup.NotRealised || [];
cFormulaFunctionGroup.NotRealised.push(cCUBEKPIMEMBER, cCUBEMEMBERPROPERTY, cCUBERANKEDMEMBER,
cCUBESET, cCUBESETCOUNT);
var xmla = new Xmla({
// listeners: {
// events: Xmla.EVENT_ERROR,
// handler: function (eventName, eventData, xmla) {
// console.log(eventData.exception);
// // alert(
// // "Snap, an error occurred: " + eventData.exception.message + " (" + eventData.exception.code + ")" +
// // (eventData.exception.code === Xmla.Exception.HTTP_ERROR_CDE
// // ? "\nstatus: " + eventData.exception.data.status + "; statusText: " + eventData.exception.data.statusText
// // : "")
// // );
// }
// },
async: true
});
function xmla_request(func, prop) {
var xmla = new Xmla({async: true});
// return function () {
return new RSVP.Queue()
.push(function () {
return new RSVP.Promise(function (resolve, reject) {
prop.success = function (xmla, options, response) {
resolve(response);
};
prop.error = function (xmla, options, response) {
reject(response);
};
xmla[func](prop);
});
});
}
function xmla_request_retry(func, prop) {
return xmla_request(func, prop)
.push(undefined, function (response) {
// fix mondrian Internal and Sql errors
if (response) {
switch (response["code"]) {
case "SOAP-ENV:Server.00HSBE02":
case "SOAP-ENV:00UE001.Internal Error":
// rarely server error, so try again
return xmla_request(func, prop);
}
}
throw response;
});
}
function discover_hierarchies(connection) {
var settings = getProperties(connection),
prop = settings.prop;
prop.restrictions = {
// 'CATALOG_NAME': 'FoodMart',
// 'HIERARCHY_NAME': hierarchy_name,
// 'HIERARCHY_UNIQUE_NAME': hierarchy_name,
'CUBE_NAME': settings["cube"]
};
return xmla_request_retry("discoverMDHierarchies", prop)
.push(function (response) {
var hierarchies = {},
hierarchy,
uname,
caption,
all_member,
dimension_uname,
dimension,
dimensions = {};
while (response.hasMoreRows()) {
uname = response["getHierarchyUniqueName"]();
caption = response["getHierarchyCaption"]();
all_member = response["getAllMember"]();
dimension_uname = response["getDimensionUniqueName"]();
dimension = dimensions[dimension_uname];
if (!dimension) {
dimension = {
"uname": dimension_uname,
"all_member": all_member
};
dimensions[dimension_uname] = dimension;
}
if (!dimension.all_member && all_member) {
dimension.all_member = all_member;
}
hierarchy = {
"uname": uname,
"caption": caption,
"all_member": all_member,
"dimension_uname": dimension_uname,
"dimension": dimension
};
hierarchies[uname] = hierarchy;
hierarchies[caption] = hierarchy;
response.nextRow();
}
return {
"hierarchies": hierarchies,
"dimensions": dimensions
};
});
}
function getProperties(connection) {
var connections = {
"xmla": {
"prop": {
"url": "https://d1.erp5.ru/saiku/xmla",
"properties": {
"DataSourceInfo": "FoodMart",
"Catalog": "FoodMart"
}
},
"cube": "Sales"
},
"olapy": {
"prop": {
"url": "https://d1.erp5.ru/olapy/xmla",
"properties": {
"DataSourceInfo": "-",
"Catalog": "sales"
}
},
"cube": "Sales"
}
};
connection = connections[connection];
if (!connection) {
throw "connection not exist";
}
connection = JSON.parse(JSON.stringify(connection));
return connection;
}
function getScheme(connection) {
var scheme = cubeScheme[connection],
queue = new RSVP.Queue();
if (scheme) {
return queue.push(function () {
return scheme;
});
}
cubeScheme[connection] = queue;
return queue
.push(function () {
return discover_hierarchies(connection);
})
.push(function (arg) {
scheme = {
members: {},
hierarchies: arg.hierarchies,
dimensions: arg.dimensions
};
cubeScheme[connection] = scheme;
return scheme;
});
}
function getExecutionScheme(connection) {
var scheme = cubeExecutionScheme[connection];
if (scheme) {
return scheme;
} else {
scheme = {
members: {},
hierarchies: {},
levels: {}
};
cubeExecutionScheme[connection] = scheme;
return scheme;
}
}
function getCell(arg0) {
if (arg0 instanceof cArray) {
arg0 = arg0.getElement(0);
// } else if (arg0 instanceof cArea || arg0 instanceof cArea3D) {
// arg0 = arg0.cross(arguments[1].bbox);
} else if (arg0 instanceof cRef || arg0 instanceof cRef3D) {
arg0 = arg0.getValue();
}
return arg0;
}
function parseArgs(mdx_array) {
return function () {
var members = [];
function stringForge(value) {
var array;
if (value.cube_value) {
array = value.cube_value;
} else {
array = value.value.split(',');
}
if (array.length > 0) {
// filter members already existed
members = members.filter(function (i) {
return array.indexOf(i) === -1;
});
members = members.concat(array);
}
}
function cellForge(cell) {
if (cell) {
if (cell.oValue.type === cElementType.error) {
// debugger;
throw "referenced cell contain error";
}
if (cell.formulaParsed && cell.formulaParsed.value) {
stringForge(cell.formulaParsed.value);
} else {
stringForge({value: cell.getValue()});
}
}
}
mdx_array.forEach(function (element) {
if (element instanceof cArea || element instanceof cArea3D ||
element instanceof cRef || element instanceof cRef3D) {
element.getRange()._foreach(cellForge);
} else {
stringForge(element);
}
});
return members;
};
}
var AddCubeValueCalculate = (function () {
var deferred = RSVP.defer(),
cells = [];
return function (cell_id) {
if (cells.indexOf(cell_id) === -1) {
cells.push(cell_id);
}
// console.log('+ ' + cells);
return function () {
var i = cells.indexOf(cell_id);
if (i !== -1) {
cells.splice(i, 1);
}
// console.log('-' + cells);
if (cells.length === 0) {
deferred.resolve();
deferred = RSVP.defer();
return {};
}
return deferred.promise;
};
};
})();
function execute(connection) {
var execution_scheme = getExecutionScheme(connection),
scheme;
if (!execution_scheme.execute) {
execution_scheme.execute = RSVP.defer();
return getScheme(connection)
.push(function (s) {
var settings = getProperties(connection),
prop = settings.prop,
hierarchies = execution_scheme.hierarchies,
hierarchy,
mdx = [],
tuple_str,
all_member;
scheme = s;
for (hierarchy in hierarchies) {
tuple_str = hierarchies[hierarchy].join(",");
all_member = scheme.hierarchies[hierarchy]["all_member"];
if (all_member) {
tuple_str = tuple_str + ',' + all_member;
}
mdx.push("{" + tuple_str + "}");
}
prop.statement = "SELECT " + mdx.join("*") +
" ON 0 FROM [" + settings["cube"] + "]";
return xmla_request("execute", prop);
})
.push(function (dataset) {
var cellset = dataset.getCellset(),
axis_count = dataset.axisCount(),
axis_array = [],
axis_id,
cube = {
"axes": {"length": axis_count},
"members": {},
"hierarchies": {"length": 0},
"hierarchies_info": scheme.hierarchies,
"cells": []
};
for (axis_id = 0; axis_id < axis_count; axis_id++) {
axis_array.push(dataset.getAxis(axis_id));
}
axis_array.forEach(function (axis, axis_id) {
cube.axes[axis_id] = {
tuples: {},
length: 0
};
axis.eachTuple(function (tuple) {
var coordinate_tuple = [];
axis.eachHierarchy(function () {
var member = this.member();
if (!cube.members.hasOwnProperty(member["UName"])) {
cube.members[member["UName"]] = member;
}
coordinate_tuple.push(member["UName"]);
});
cube.axes[axis_id].tuples[coordinate_tuple.join(',')] = tuple.index;
cube.axes[axis_id].length++;
});
axis.eachHierarchy(function (hierarchy) {
cube.hierarchies[hierarchy.name] = {
axis_id: axis_id, tuple_id: hierarchy.index, name: hierarchy.name
};
cube.hierarchies[cube.hierarchies.length] = cube.hierarchies[hierarchy.name];
cube.hierarchies['' + axis_id + ',' + hierarchy.index] = cube.hierarchies[hierarchy.name];
cube.hierarchies.length++;
});
});
do {
cube.cells[cellset.cellOrdinal()] = cellset["cellValue"]();
} while (cellset.nextCell() > 0);
execution_scheme.cube = cube;
execution_scheme.execute.resolve(cube);
execution_scheme.execute = null;
execution_scheme.hierarchies = [];
return cube;
})
.push(undefined, function (error) {
console.error(error);
execution_scheme.execute = null;
execution_scheme.hierarchies = [];
});
}
return execution_scheme.execute.promise;
}
function discover_members(connection, opt) {
return new RSVP.Queue()
.push(function () {
var settings = getProperties(connection),
prop = settings.prop,
cached_member,
scheme = getExecutionScheme(connection);
prop.restrictions = {
// 'CATALOG_NAME': 'FoodMart',
'CUBE_NAME': settings["cube"]
};
if (!opt) {
opt = {};
}
if (opt.member_uname) {
prop.restrictions["MEMBER_UNIQUE_NAME"] = opt.member_uname;
cached_member = scheme.members[opt.member_uname];
}
if (opt.level_uname) {
prop.restrictions["LEVEL_UNIQUE_NAME"] = opt.level_uname;
}
if (cached_member) {
return [cached_member];
} else {
return xmla_request_retry("discoverMDMembers", prop)
.push(function (r) {
var ret = [],
uname,
level,
cached_member;
while (r.hasMoreRows()) {
uname = r["getMemberUniqueName"]();
level = r["getLevelUniqueName"]();
// we can check cache twice because fist check
// only if discover by member_uname
if (!scheme.members.hasOwnProperty(uname)) {
cached_member = {
uname: uname,
h: r["getHierarchyUniqueName"](),
level: r["getLevelUniqueName"](),
caption: r["getMemberCaption"](),
type: r["getMemberType"]()
};
scheme.members[uname] = cached_member;
} else {
cached_member = scheme.members[uname];
}
ret.push(cached_member);
r.nextRow();
if (!scheme.levels.hasOwnProperty(level)) {
scheme.levels[level] = discover_level(connection, scheme, level);
}
}
return ret;
});
}
});
}
function discover_level(connection, scheme, level) {
return discover_members(connection, {
level_uname: level
})
.push(function (members) {
var i;
function compare(a, b) {
if (a.uname < b.uname)
return -1;
if (a.uname > b.uname)
return 1;
return 0;
}
members.sort(compare);
for (i = 0; i < members.length; i++) {
members[i].level_index = i;
}
scheme.levels[level] = members;
});
}
function discover_members_for_arguments(connection, members) {
var promises = [],
hierarchies = {};
function check_interseption(hierarchy) {
if (hierarchies.hasOwnProperty(hierarchy)) {
throw "The tuple is invalid because there is no intersection for the specified values.";
} else {
hierarchies[hierarchy] = 1;
}
}
members.forEach(function (member) {
if (member) {
promises
.push(
discover_members(connection, {
member_uname: member
})
.push(function (members) {
var member;
if (members.length > 0) {
member = members[0];
check_interseption(member.h);
return member;
} else {
throw "member not found";
}
})
);
}
});
return RSVP.all(promises);
}
function error_handler(current_cell_id) {
return function (error) {
console.error(current_cell_id, error);
var ret;
if (error === "referenced cell contain error") {
ret = new cError(cErrorType.wrong_value_type);
} else if (error === "connection not exist" ||
error instanceof Xmla.Exception) {
ret = new cError(cErrorType.wrong_name);
} else {
ret = new cError(cErrorType.not_available);
}
return ret;
};
}
/**
* @constructor
......@@ -73,6 +567,70 @@
cCUBEMEMBER.prototype = Object.create(cBaseFunction.prototype);
cCUBEMEMBER.prototype.constructor = cCUBEMEMBER;
cCUBEMEMBER.prototype.argumentsMin = 2;
cCUBEMEMBER.prototype.argumentsMax = 3;
cCUBEMEMBER.prototype.ca = true;
cCUBEMEMBER.prototype.CalculateLazy = function (queue, bbox, isDefName, ws) {
var connection,
current_cell_id = ws._getCell(bbox.r1, bbox.c2).getId(),
caption;
return queue
.push(function (arg) {
connection = getCell(arg[0]);
caption = getCell(arg[2]);
if (caption) {
caption = caption.getValue();
}
return parseArgs([arg[1]])();
})
.push(function (members) {
return discover_members_for_arguments(connection, members);
})
.push(function (members) {
var last_id = members.length - 1,
ret;
if (!caption) {
caption = members[last_id].caption;
}
ret = new cString(caption);
ret.cube_value = [];
members.forEach(function (member) {
ret.cube_value.push(member.uname);
});
return ret;
})
.push(undefined, error_handler(current_cell_id));
};
cCUBEMEMBER.prototype.changeOffsetElem = function (arg, offset) {
var connection = getCell(arg[0]),
scheme = getExecutionScheme(connection),
i,
elem,
member,
new_member,
level;
for (i = 0; i < arg.length; i++) {
elem = arg[i];
if (cElementType.string === elem.type) {
member = scheme.members[elem.value];
if (member && (member.level_index >= 0)) {
level = scheme.levels[member.level];
new_member = level[member.level_index + offset.offsetCol + offset.offsetRow];
if (new_member) {
elem.value = new_member.uname;
} else {
elem.value = "";
}
}
}
}
};
cCUBEMEMBER.prototype.getInfo = function () {
return {
name: this.name, args: "( connection, members, caption )"
};
};
/**
* @constructor
......@@ -138,4 +696,125 @@
cCUBEVALUE.prototype = Object.create(cBaseFunction.prototype);
cCUBEVALUE.prototype.constructor = cCUBEVALUE;
})(window);
cCUBEVALUE.prototype.argumentsMin = 2;
cCUBEVALUE.prototype.argumentsMax = 5;
cCUBEVALUE.prototype.ca = true;
cCUBEVALUE.prototype.CalculateLazy = function (queue, bbox, isDefName, ws) {
var scheme,
connection,
members = [],
current_cell_id = ws._getCell(bbox.r1, bbox.c2).getId(),
waiter = AddCubeValueCalculate(current_cell_id);
return queue
.push(function (arg) {
connection = getCell(arg[0]);
scheme = getExecutionScheme(connection);
return parseArgs(arg.slice(1))();
})
.push(function (members) {
return discover_members_for_arguments(connection, members);
})
.push(function (m) {
var member_uname,
member,
h,
hierarchy;
for (member_uname in m) {
if (m.hasOwnProperty(member_uname)) {
member = m[member_uname];
hierarchy = member.h;
h = scheme.hierarchies[hierarchy];
if (!h) {
h = [];
scheme.hierarchies[hierarchy] = h;
}
if (h.indexOf(member.uname) === -1) {
h.push(member.uname);
}
members.push(member.uname);
}
}
return waiter();
})
.push(function () {
return execute(connection);
})
.push(function (cube) {
var cell_id = 0,
p_d = 1,
h,
member_path,
coordinate = [],
i,
ret;
function getHierarchyByMember(member_path) {
var h;
h = cube.members[member_path];
if (h === undefined) {
throw "query result not contain data for member:" +
member_path;
}
h = h.hierarchy;
h = cube.hierarchies[h];
return h;
}
for (i = 0; i < cube.hierarchies.length; i++) {
h = cube.hierarchies[i];
if (!coordinate[h.axis_id]) {
coordinate[h.axis_id] = [];
}
coordinate[h.axis_id][h.tuple_id] = null;
}
for (i = 0; i < members.length; i++) {
member_path = members[i];
h = getHierarchyByMember(members[i]);
coordinate[h.axis_id][h.tuple_id] = member_path;
}
coordinate = coordinate.map(function (axis, axis_id) {
return axis.map(function (h, h_id) {
var hierarchy_name,
all_member;
if (!h) {
hierarchy_name = cube.hierarchies[axis_id + ',' + h_id].name;
all_member = cube["hierarchies_info"][hierarchy_name]["all_member"];
if (all_member) {
h = getHierarchyByMember(all_member);
if (h) {
return all_member;
}
}
throw "Axis:" + axis_id + " hierarchy:" +
cube.hierarchies[axis_id + ',' + h_id].name +
" not determinated";
}
return h;
}).join(',');
});
coordinate.forEach(function (tuple, axis_id) {
var axis = cube.axes[axis_id];
cell_id = p_d * axis.tuples[tuple] + cell_id;
p_d = p_d * axis.length;
});
ret = new cNumber(cube.cells[cell_id]);
return ret;
})
.push(undefined, function (error) {
// issue in one cell(cubevalue) not stop calculation in other
return new RSVP.Queue()
.push(function () {
return waiter();
})
.push(function () {
return error_handler(current_cell_id)(error);
});
});
};
cCUBEVALUE.prototype.changeOffsetElem = cCUBEMEMBER.prototype.changeOffsetElem;
cCUBEVALUE.prototype.getInfo = function () {
return {
name: this.name, args: "( connection, member1, member2, .. )"
};
};
})(window, RSVP, Xmla, console);
\ No newline at end of file
......@@ -657,6 +657,10 @@ parserHelp.setDigitSeparator(AscCommon.g_oDefaultCultureInfo.NumberDecimalSepara
this.value = val;
}
cBaseType.prototype.clone = function () {
return new this.constructor(this.value);
};
cBaseType.prototype.cloneTo = function (oRes) {
oRes.numFormat = this.numFormat;
oRes.value = this.value;
......@@ -670,6 +674,110 @@ parserHelp.setDigitSeparator(AscCommon.g_oDefaultCultureInfo.NumberDecimalSepara
cBaseType.prototype.toLocaleString = function () {
return this.toString();
};
cBaseType.prototype.CalculatePromise = function (arg, opt_bbox, isDefName, ws) {
// depromise args
var t = this,
lazy_formulas = [],
lazy_found;
function check_args_promise() {
var promise_flag = false,
element,
length,
i;
function cellForge(cell) {
if (cell && cell.formulaParsed &&
(cell.formulaParsed.lazy_value || cell.formulaParsed.queue)) {
promise_flag = true;
lazy_formulas.push(cell.formulaParsed);
}
}
for (i = 0, length = arg.length; i < length; ++i) {
element = arg[i];
if (typeof element === "function") {
promise_flag = true;
}
if (element instanceof cArea || element instanceof cArea3D ||
element instanceof cRef || element instanceof cRef3D) {
element.getRange()._foreach(cellForge);
}
}
return promise_flag;
}
lazy_found = check_args_promise();
if (t.CalculateLazy) {
return function () {
var queue = new RSVP.Queue();
if (lazy_formulas.length > 0) {
lazy_formulas.forEach(function (formula) {
var lazy = formula.lazy_value;
if (lazy) {
// if value lazy run it
lazy();
}
if (formula.queue) {
// add dependence from already
// running but not computed lazy
queue.push(function () {
return formula.queue;
});
}
});
}
queue
.push(function () {
return RSVP.all(arg.map(function (z) {
if (typeof z === "function") {
return z();
} else {
return z;
}
}));
});
return t.CalculateLazy(queue, opt_bbox, isDefName, ws);
};
} else {
if (lazy_found) {
return function () {
var queue = new RSVP.Queue();
if (lazy_formulas.length > 0) {
lazy_formulas.forEach(function (formula) {
var lazy = formula.lazy_value;
if (lazy) {
// if value lazy add it dependence list
lazy();
}
if (formula.queue) {
// add dependence from already
// running but not computed lazy
queue.push(function () {
return formula.queue;
});
}
});
}
return queue
.push(function () {
return RSVP.all(arg.map(function (z) {
if (typeof z === "function") {
return z();
} else {
return z;
}
}));
})
.push(function (arg) {
return t.Calculate(arg, opt_bbox, isDefName, ws);
});
};
} else {
return t.Calculate(arg, opt_bbox, isDefName, ws);
}
}
};
/*Basic types of an elements used into formulas*/
/**
......@@ -2347,6 +2455,7 @@ parserHelp.setDigitSeparator(AscCommon.g_oDefaultCultureInfo.NumberDecimalSepara
cBaseOperator.prototype.Calculate = function () {
return null;
};
cBaseOperator.prototype.CalculatePromise = cBaseType.prototype.CalculatePromise;
cBaseOperator.prototype.Assemble = function (arg) {
var str = "";
if (this.argumentsCurrent === 2) {
......@@ -2394,6 +2503,7 @@ parserHelp.setDigitSeparator(AscCommon.g_oDefaultCultureInfo.NumberDecimalSepara
this.value = new cError(cErrorType.wrong_name);
return this.value;
};
cBaseFunction.prototype.CalculatePromise = cBaseOperator.prototype.CalculatePromise
cBaseFunction.prototype.DecrementArguments = function () {
--this.argumentsCurrent;
};
......@@ -4225,7 +4335,7 @@ function parserFormula( formula, parent, _ws ) {
this.parent.onFormulaEvent(AscCommon.c_oNotifyParentType.Change, eventData);
}
}
} else if (AscCommon.c_oNotifyType.Changed === data.type) {
} else if (AscCommon.c_oNotifyType.Changed === data.type) {
if (this.parent && this.parent.onFormulaEvent) {
this.parent.onFormulaEvent(AscCommon.c_oNotifyParentType.Change, eventData);
}
......@@ -5102,6 +5212,8 @@ parserFormula.prototype.parse = function(local, digitDelim) {
};
parserFormula.prototype.calculate = function (opt_defName, opt_bbox, opt_offset) {
var value,
formula = this;
if (this.isCalculate && (!this.calculateDefName || this.calculateDefName[opt_bbox ? opt_bbox.getName() :
opt_bbox])) {
//cycle
......@@ -5145,7 +5257,7 @@ parserFormula.prototype.parse = function(local, digitDelim) {
for (var ind = 0; ind < currentElement.getArguments(); ind++) {
arg.unshift(elemArr.pop());
}
_tmp = currentElement.Calculate(arg, opt_bbox, opt_defName, this.ws);
_tmp = currentElement.CalculatePromise(arg, opt_bbox, opt_defName, this.ws);
if (cNumFormatNull !== _tmp.numFormat) {
numFormat = _tmp.numFormat;
} else if (0 > numFormat || cNumFormatNone === currentElement.numFormat) {
......@@ -5163,8 +5275,31 @@ parserFormula.prototype.parse = function(local, digitDelim) {
elemArr.push(currentElement);
}
}
this.value = elemArr.pop();
this.value.numFormat = numFormat;
value = elemArr.pop();
if (typeof value === "function") {
this.value = new cError(cErrorType.getting_data);
this.queue = true;
this.lazy_value = function () {
formula.lazy_value = null;
formula.queue = value()
.push(function (ret) {
formula.value = ret;
formula.value.numFormat = numFormat;
formula._endCalculate();
// updateOnScreen cell
formula.ws.workbook.handlers.trigger("cleanCellCache",
formula.ws.getId(), {0: opt_bbox},
AscCommonExcel.c_oAscCanChangeColWidth.none);
formula.queue = false;
// formula.lazy_value = null;
return formula.value;
});
return formula.queue
}
} else {
this.value = value;
this.value.numFormat = numFormat;
}
this._endCalculate();
return this.value;
......
......@@ -908,6 +908,11 @@
this.buildDefName = {};
},
calcTree: function() {
var dependency_graph = this,
formula,
i,
tasks = [],
lazy_value;
if (this.lockCounter > 0) {
return;
}
......@@ -924,26 +929,44 @@
this._broadcastCells(notifyData, calcTrack);
}
this._broadcastCellsEnd();
for (var i = 0; i < noCalcTrack.length; ++i) {
var formula = noCalcTrack[i];
for (i = 0; i < noCalcTrack.length; ++i) {
formula = noCalcTrack[i];
//defName recalc when calc formula containing it. no need calc it
formula.setIsDirty(false);
}
for (var i = 0; i < calcTrack.length; ++i) {
var formula = calcTrack[i];
for (i = 0; i < calcTrack.length; ++i) {
formula = calcTrack[i];
if (formula.getIsDirty()) {
formula.calculate();
}
}
//copy cleanCellCache to prevent recursion in trigger("cleanCellCache")
var tmpCellCache = this.cleanCellCache;
this.cleanCellCache = {};
for (var i in tmpCellCache) {
this.wb.handlers.trigger("cleanCellCache", i, {0: tmpCellCache[i]},
AscCommonExcel.c_oAscCanChangeColWidth.none);
for (i = calcTrack.length-1; i >= 0; --i) {
lazy_value = calcTrack[i].lazy_value;
if (lazy_value) {
tasks.push(lazy_value());
}
}
function end () {
//copy cleanCellCache to prevent recursion in trigger("cleanCellCache")
var tmpCellCache = dependency_graph.cleanCellCache;
dependency_graph.cleanCellCache = {};
for (var i in dependency_graph.cleanCellCache) {
dependency_graph.wb.handlers.trigger("cleanCellCache", i, {0: tmpCellCache[i]},
AscCommonExcel.c_oAscCanChangeColWidth.none);
}
AscCommonExcel.g_oVLOOKUPCache.clean();
AscCommonExcel.g_oHLOOKUPCache.clean();
}
if (tasks.length > 0) {
new RSVP.Queue()
.push(function () {
return RSVP.all(tasks);
})
.push(end);
} else {
end();
}
AscCommonExcel.g_oVLOOKUPCache.clean();
AscCommonExcel.g_oHLOOKUPCache.clean();
},
initOpen: function() {
this._foreachDefName(function(defName) {
......@@ -5387,6 +5410,11 @@
this.nCol = -1;
this.formulaParsed = null;
}
Cell.prototype.getId = function () {
return [this.ws.getId(), this.getName()].join(",");
};
Cell.prototype.getStyle=function(){
return this.xfs;
};
......@@ -5945,7 +5973,7 @@
this.setValue("");
};
Cell.prototype._checkDirty = function(){
if(this.formulaParsed && this.formulaParsed.getIsDirty()) {
if(this.formulaParsed && this.formulaParsed.getIsDirty() && !this.formulaParsed.queue) {
this.formulaParsed.calculate();
}
};
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -69,3 +69,5 @@ function MSPointerMove(){}
function MSPointerUp(){}
var editor;
RSVP.Queue = function () {};
(function(globals) {
var define, requireModule;
(function() {
var registry = {}, seen = {};
define = function(name, deps, callback) {
registry[name] = { deps: deps, callback: callback };
};
requireModule = function(name) {
if (seen[name]) { return seen[name]; }
seen[name] = {};
var mod = registry[name];
if (!mod) {
throw new Error("Module '" + name + "' not found.");
}
var deps = mod.deps,
callback = mod.callback,
reified = [],
exports;
for (var i=0, l=deps.length; i<l; i++) {
if (deps[i] === 'exports') {
reified.push(exports = {});
} else {
reified.push(requireModule(deps[i]));
}
}
var value = callback.apply(this, reified);
return seen[name] = exports || value;
};
})();
define("rsvp/all",
["rsvp/promise","exports"],
function(__dependency1__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
/* global toString */
function promiseAtLeast(expected_count, promises) {
if (Object.prototype.toString.call(promises) !== "[object Array]") {
throw new TypeError('You must pass an array to all.');
}
function canceller() {
var promise;
for (var i = 0; i < promises.length; i++) {
promise = promises[i];
if (promise && typeof promise.then === 'function' &&
typeof promise.cancel === 'function') {
promise.cancel();
}
}
}
return new Promise(function(resolve, reject, notify) {
var results = [], remaining = promises.length,
promise, remaining_count = promises.length - expected_count;
if (remaining === 0) {
if (expected_count === 1) {
resolve();
} else {
resolve([]);
}
}
function resolver(index) {
return function(value) {
resolveAll(index, value);
};
}
function resolveAll(index, value) {
results[index] = value;
if (--remaining === remaining_count) {
if (remaining_count === 0) {
resolve(results);
} else {
resolve(value);
canceller();
}
}
}
function notifier(index) {
return function(value) {
notify({"index": index, "value": value});
};
}
function cancelAll(rejectionValue) {
reject(rejectionValue);
canceller();
}
for (var i = 0; i < promises.length; i++) {
promise = promises[i];
if (promise && typeof promise.then === 'function') {
promise.then(resolver(i), cancelAll, notifier(i));
} else {
resolveAll(i, promise);
}
}
}, canceller
);
}
function all(promises) {
return promiseAtLeast(promises.length, promises);
}
function any(promises) {
return promiseAtLeast(1, promises);
}
__exports__.all = all;
__exports__.any = any;
});
define("rsvp/async",
["exports"],
function(__exports__) {
"use strict";
var browserGlobal = (typeof window !== 'undefined') ? window : {};
var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
var async;
var local = (typeof global !== 'undefined') ? global : this;
function checkNativePromise() {
if (typeof Promise === "function" &&
typeof Promise.resolve === "function") {
try {
/* global Promise */
var promise = new Promise(function(){});
if ({}.toString.call(promise) === "[object Promise]") {
return true;
}
} catch (e) {}
}
return false;
}
function useNativePromise() {
var nativePromise = Promise.resolve();
return function(callback, arg) {
nativePromise.then(function () {
callback(arg);
});
};
}
// old node
function useNextTick() {
return function(callback, arg) {
process.nextTick(function() {
callback(arg);
});
};
}
// node >= 0.10.x
function useSetImmediate() {
return function(callback, arg) {
/* global setImmediate */
setImmediate(function(){
callback(arg);
});
};
}
function useMutationObserver() {
var queue = [];
var observer = new BrowserMutationObserver(function() {
var toProcess = queue.slice();
queue = [];
toProcess.forEach(function(tuple) {
var callback = tuple[0], arg= tuple[1];
callback(arg);
});
});
var element = document.createElement('div');
observer.observe(element, { attributes: true });
// Chrome Memory Leak: https://bugs.webkit.org/show_bug.cgi?id=93661
window.addEventListener('unload', function(){
observer.disconnect();
observer = null;
}, false);
return function(callback, arg) {
queue.push([callback, arg]);
element.setAttribute('drainQueue', 'drainQueue');
};
}
function useSetTimeout() {
return function(callback, arg) {
local.setTimeout(function() {
callback(arg);
}, 1);
};
}
if (typeof setImmediate === 'function') {
async = useSetImmediate();
} else if (typeof process !== 'undefined' && {}.toString.call(process) === '[object process]') {
async = useNextTick();
} else if (BrowserMutationObserver) {
async = useMutationObserver();
} else if (checkNativePromise()) {
async = useNativePromise();
} else {
async = useSetTimeout();
}
__exports__.async = async;
});
define("rsvp/cancellation_error",
["exports"],
function(__exports__) {
"use strict";
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
function CancellationError(message) {
this.name = "cancel";
if ((message !== undefined) && (typeof message !== "string")) {
throw new TypeError('You must pass a string.');
}
this.message = message || "Default Message";
}
CancellationError.prototype = new Error();
CancellationError.prototype.constructor = CancellationError;
__exports__.CancellationError = CancellationError;
});
define("rsvp/config",
["rsvp/async","exports"],
function(__dependency1__, __exports__) {
"use strict";
var async = __dependency1__.async;
var config = {};
config.async = async;
__exports__.config = config;
});
define("rsvp/defer",
["rsvp/promise","exports"],
function(__dependency1__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
function defer() {
var deferred = {
// pre-allocate shape
resolve: undefined,
reject: undefined,
promise: undefined
};
deferred.promise = new Promise(function(resolve, reject) {
deferred.resolve = resolve;
deferred.reject = reject;
});
return deferred;
}
__exports__.defer = defer;
});
define("rsvp/events",
["exports"],
function(__exports__) {
"use strict";
var Event = function(type, options) {
this.type = type;
for (var option in options) {
if (!options.hasOwnProperty(option)) { continue; }
this[option] = options[option];
}
};
var indexOf = function(callbacks, callback) {
for (var i=0, l=callbacks.length; i<l; i++) {
if (callbacks[i][0] === callback) { return i; }
}
return -1;
};
var callbacksFor = function(object) {
var callbacks = object._promiseCallbacks;
if (!callbacks) {
callbacks = object._promiseCallbacks = {};
}
return callbacks;
};
var EventTarget = {
mixin: function(object) {
object.on = this.on;
object.off = this.off;
object.trigger = this.trigger;
return object;
},
on: function(eventNames, callback, binding) {
var allCallbacks = callbacksFor(this), callbacks, eventName;
eventNames = eventNames.split(/\s+/);
binding = binding || this;
while (eventName = eventNames.shift()) {
callbacks = allCallbacks[eventName];
if (!callbacks) {
callbacks = allCallbacks[eventName] = [];
}
if (indexOf(callbacks, callback) === -1) {
callbacks.push([callback, binding]);
}
}
},
off: function(eventNames, callback) {
var allCallbacks = callbacksFor(this), callbacks, eventName, index;
eventNames = eventNames.split(/\s+/);
while (eventName = eventNames.shift()) {
if (!callback) {
allCallbacks[eventName] = [];
continue;
}
callbacks = allCallbacks[eventName];
index = indexOf(callbacks, callback);
if (index !== -1) { callbacks.splice(index, 1); }
}
},
trigger: function(eventName, options) {
var allCallbacks = callbacksFor(this),
callbacks, callbackTuple, callback, binding, event;
if (callbacks = allCallbacks[eventName]) {
// Don't cache the callbacks.length since it may grow
for (var i=0; i<callbacks.length; i++) {
callbackTuple = callbacks[i];
callback = callbackTuple[0];
binding = callbackTuple[1];
if (typeof options !== 'object') {
options = { detail: options };
}
event = new Event(eventName, options);
callback.call(binding, event);
}
}
}
};
__exports__.EventTarget = EventTarget;
});
define("rsvp/hash",
["rsvp/defer","exports"],
function(__dependency1__, __exports__) {
"use strict";
var defer = __dependency1__.defer;
function size(object) {
var s = 0;
for (var prop in object) {
s++;
}
return s;
}
function hash(promises) {
var results = {}, deferred = defer(), remaining = size(promises);
if (remaining === 0) {
deferred.resolve({});
}
var resolver = function(prop) {
return function(value) {
resolveAll(prop, value);
};
};
var resolveAll = function(prop, value) {
results[prop] = value;
if (--remaining === 0) {
deferred.resolve(results);
}
};
var rejectAll = function(error) {
deferred.reject(error);
};
for (var prop in promises) {
if (promises[prop] && typeof promises[prop].then === 'function') {
promises[prop].then(resolver(prop), rejectAll);
} else {
resolveAll(prop, promises[prop]);
}
}
return deferred.promise;
}
__exports__.hash = hash;
});
define("rsvp/node",
["rsvp/promise","rsvp/all","exports"],
function(__dependency1__, __dependency2__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
var all = __dependency2__.all;
function makeNodeCallbackFor(resolve, reject) {
return function (error, value) {
if (error) {
reject(error);
} else if (arguments.length > 2) {
resolve(Array.prototype.slice.call(arguments, 1));
} else {
resolve(value);
}
};
}
function denodeify(nodeFunc) {
return function() {
var nodeArgs = Array.prototype.slice.call(arguments), resolve, reject;
var thisArg = this;
var promise = new Promise(function(nodeResolve, nodeReject) {
resolve = nodeResolve;
reject = nodeReject;
});
all(nodeArgs).then(function(nodeArgs) {
nodeArgs.push(makeNodeCallbackFor(resolve, reject));
try {
nodeFunc.apply(thisArg, nodeArgs);
} catch(e) {
reject(e);
}
});
return promise;
};
}
__exports__.denodeify = denodeify;
});
define("rsvp/promise",
["rsvp/config","rsvp/events","rsvp/cancellation_error","exports"],
function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
"use strict";
var config = __dependency1__.config;
var EventTarget = __dependency2__.EventTarget;
var CancellationError = __dependency3__.CancellationError;
function objectOrFunction(x) {
return isFunction(x) || (typeof x === "object" && x !== null);
}
function isFunction(x){
return typeof x === "function";
}
var Promise = function(resolver, canceller) {
var promise = this,
resolved = false;
if (typeof resolver !== 'function') {
throw new TypeError('You must pass a resolver function as the sole argument to the promise constructor');
}
if ((canceller !== undefined) && (typeof canceller !== 'function')) {
throw new TypeError('You can only pass a canceller function' +
' as the second argument to the promise constructor');
}
if (!(promise instanceof Promise)) {
return new Promise(resolver, canceller);
}
var resolvePromise = function(value) {
if (resolved) { return; }
resolved = true;
resolve(promise, value);
};
var rejectPromise = function(value) {
if (resolved) { return; }
resolved = true;
reject(promise, value);
};
var notifyPromise = function(value) {
if (resolved) { return; }
notify(promise, value);
};
this.on('promise:failed', function(event) {
this.trigger('error', { detail: event.detail });
}, this);
this.on('error', onerror);
this.cancel = function () {
// For now, simply reject the promise and does not propagate the cancel
// to parent or children
if (resolved) { return; }
if (canceller !== undefined) {
try {
canceller();
} catch (e) {
rejectPromise(e);
return;
}
}
// Trigger cancel?
rejectPromise(new CancellationError());
};
try {
resolver(resolvePromise, rejectPromise, notifyPromise);
} catch(e) {
rejectPromise(e);
}
};
function onerror(event) {
if (config.onerror) {
config.onerror(event.detail);
}
}
var invokeCallback = function(type, promise, callback, event) {
var hasCallback = isFunction(callback),
value, error, succeeded, failed;
if (promise.isFulfilled) { return; }
if (promise.isRejected) { return; }
if (hasCallback) {
try {
value = callback(event.detail);
succeeded = true;
} catch(e) {
failed = true;
error = e;
}
} else {
value = event.detail;
succeeded = true;
}
if (handleThenable(promise, value)) {
return;
} else if (hasCallback && succeeded) {
resolve(promise, value);
} else if (failed) {
reject(promise, error);
} else if (type === 'resolve') {
resolve(promise, value);
} else if (type === 'reject') {
reject(promise, value);
}
};
var invokeNotifyCallback = function(promise, callback, event) {
var value;
if (typeof callback === 'function') {
try {
value = callback(event.detail);
} catch (e) {
// stop propagating
return;
}
notify(promise, value);
} else {
notify(promise, event.detail);
}
};
Promise.prototype = {
constructor: Promise,
isRejected: undefined,
isFulfilled: undefined,
rejectedReason: undefined,
fulfillmentValue: undefined,
then: function(done, fail, progress) {
this.off('error', onerror);
var thenPromise = new this.constructor(function() {},
function () {
thenPromise.trigger('promise:cancelled', {});
});
if (this.isFulfilled) {
config.async(function(promise) {
invokeCallback('resolve', thenPromise, done, { detail: promise.fulfillmentValue });
}, this);
}
if (this.isRejected) {
config.async(function(promise) {
invokeCallback('reject', thenPromise, fail, { detail: promise.rejectedReason });
}, this);
}
this.on('promise:resolved', function(event) {
invokeCallback('resolve', thenPromise, done, event);
});
this.on('promise:failed', function(event) {
invokeCallback('reject', thenPromise, fail, event);
});
this.on('promise:notified', function (event) {
invokeNotifyCallback(thenPromise, progress, event);
});
return thenPromise;
},
fail: function(fail) {
return this.then(null, fail);
},
always: function(fail) {
return this.then(fail, fail);
}
};
EventTarget.mixin(Promise.prototype);
function resolve(promise, value) {
if (promise === value) {
fulfill(promise, value);
} else if (!handleThenable(promise, value)) {
fulfill(promise, value);
}
}
function handleThenable(promise, value) {
var then = null,
resolved;
try {
if (promise === value) {
throw new TypeError("A promises callback cannot return that same promise.");
}
if (objectOrFunction(value)) {
then = value.then;
if (isFunction(then)) {
if (isFunction(value.on)) {
value.on('promise:notified', function (event) {
notify(promise, event.detail);
});
}
promise.on('promise:cancelled', function(event) {
if (isFunction(value.cancel)) {
value.cancel();
}
});
then.call(value, function(val) {
if (resolved) { return true; }
resolved = true;
if (value !== val) {
resolve(promise, val);
} else {
fulfill(promise, val);
}
}, function(val) {
if (resolved) { return true; }
resolved = true;
reject(promise, val);
});
return true;
}
}
} catch (error) {
reject(promise, error);
return true;
}
return false;
}
function fulfill(promise, value) {
config.async(function() {
if (promise.isFulfilled) { return; }
if (promise.isRejected) { return; }
promise.trigger('promise:resolved', { detail: value });
promise.isFulfilled = true;
promise.fulfillmentValue = value;
});
}
function reject(promise, value) {
config.async(function() {
if (promise.isFulfilled) { return; }
if (promise.isRejected) { return; }
promise.trigger('promise:failed', { detail: value });
promise.isRejected = true;
promise.rejectedReason = value;
});
}
function notify(promise, value) {
config.async(function() {
promise.trigger('promise:notified', { detail: value });
});
}
__exports__.Promise = Promise;
});
define("rsvp/queue",
["rsvp/promise","rsvp/resolve","exports"],
function(__dependency1__, __dependency2__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
var resolve = __dependency2__.resolve;
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
function ResolvedQueueError(message) {
this.name = "resolved";
if ((message !== undefined) && (typeof message !== "string")) {
throw new TypeError('You must pass a string.');
}
this.message = message || "Default Message";
}
ResolvedQueueError.prototype = new Error();
ResolvedQueueError.prototype.constructor = ResolvedQueueError;
var Queue = function() {
var queue = this,
promise_list = [],
promise,
fulfill,
reject,
notify,
resolved;
if (!(this instanceof Queue)) {
return new Queue();
}
function canceller() {
for (var i = 0; i < 2; i++) {
promise_list[i].cancel();
}
}
promise = new Promise(function(done, fail, progress) {
fulfill = function (fulfillmentValue) {
if (resolved) {return;}
queue.isFulfilled = true;
queue.fulfillmentValue = fulfillmentValue;
resolved = true;
return done(fulfillmentValue);
};
reject = function (rejectedReason) {
if (resolved) {return;}
queue.isRejected = true;
queue.rejectedReason = rejectedReason ;
resolved = true;
return fail(rejectedReason);
};
notify = progress;
}, canceller);
promise_list.push(resolve());
promise_list.push(promise_list[0].then(function () {
promise_list.splice(0, 2);
if (promise_list.length === 0) {
fulfill();
}
}));
queue.cancel = function () {
if (resolved) {return;}
resolved = true;
promise.cancel();
promise.fail(function (rejectedReason) {
queue.isRejected = true;
queue.rejectedReason = rejectedReason;
});
};
queue.then = function () {
return promise.then.apply(promise, arguments);
};
queue.push = function(done, fail, progress) {
var last_promise = promise_list[promise_list.length - 1],
next_promise;
if (resolved) {
throw new ResolvedQueueError();
}
next_promise = last_promise.then(done, fail, progress);
promise_list.push(next_promise);
// Handle pop
last_promise = next_promise.then(function (fulfillmentValue) {
promise_list.splice(0, 2);
if (promise_list.length === 0) {
fulfill(fulfillmentValue);
} else {
return fulfillmentValue;
}
}, function (rejectedReason) {
promise_list.splice(0, 2);
if (promise_list.length === 0) {
reject(rejectedReason);
} else {
throw rejectedReason;
}
}, function (notificationValue) {
if (promise_list[promise_list.length - 1] === last_promise) {
notify(notificationValue);
}
return notificationValue;
});
promise_list.push(last_promise);
return this;
};
};
Queue.prototype = Object.create(Promise.prototype);
Queue.prototype.constructor = Queue;
__exports__.Queue = Queue;
__exports__.ResolvedQueueError = ResolvedQueueError;
});
define("rsvp/reject",
["rsvp/promise","exports"],
function(__dependency1__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
function reject(reason) {
return new Promise(function (resolve, reject) {
reject(reason);
});
}
__exports__.reject = reject;
});
define("rsvp/resolve",
["rsvp/promise","exports"],
function(__dependency1__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
function resolve(thenable) {
return new Promise(function(resolve, reject) {
if (typeof thenable === "object" && thenable !== null) {
var then = thenable.then;
if ((then !== undefined) && (typeof then === "function")) {
return then.apply(thenable, [resolve, reject]);
}
}
return resolve(thenable);
}, function () {
if ((thenable !== undefined) && (thenable.cancel !== undefined)) {
thenable.cancel();
}
});
}
__exports__.resolve = resolve;
});
define("rsvp/rethrow",
["exports"],
function(__exports__) {
"use strict";
var local = (typeof global === "undefined") ? this : global;
function rethrow(reason) {
local.setTimeout(function() {
throw reason;
});
throw reason;
}
__exports__.rethrow = rethrow;
});
define("rsvp/timeout",
["rsvp/promise","exports"],
function(__dependency1__, __exports__) {
"use strict";
var Promise = __dependency1__.Promise;
function promiseSetTimeout(millisecond, should_reject, message) {
var timeout_id;
function resolver(resolve, reject) {
timeout_id = setTimeout(function () {
if (should_reject) {
reject(message);
} else {
resolve(message);
}
}, millisecond);
}
function canceller() {
clearTimeout(timeout_id);
}
return new Promise(resolver, canceller);
}
function delay(millisecond, message) {
return promiseSetTimeout(millisecond, false, message);
}
function timeout(millisecond) {
return promiseSetTimeout(millisecond, true,
"Timed out after " + millisecond + " ms");
}
Promise.prototype.delay = function(millisecond) {
return this.then(function (fulfillmentValue) {
return delay(millisecond, fulfillmentValue);
});
};
__exports__.delay = delay;
__exports__.timeout = timeout;
});
define("rsvp",
["rsvp/events","rsvp/cancellation_error","rsvp/promise","rsvp/node","rsvp/all","rsvp/queue","rsvp/timeout","rsvp/hash","rsvp/rethrow","rsvp/defer","rsvp/config","rsvp/resolve","rsvp/reject","exports"],
function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __exports__) {
"use strict";
var EventTarget = __dependency1__.EventTarget;
var CancellationError = __dependency2__.CancellationError;
var Promise = __dependency3__.Promise;
var denodeify = __dependency4__.denodeify;
var all = __dependency5__.all;
var any = __dependency5__.any;
var Queue = __dependency6__.Queue;
var ResolvedQueueError = __dependency6__.ResolvedQueueError;
var delay = __dependency7__.delay;
var timeout = __dependency7__.timeout;
var hash = __dependency8__.hash;
var rethrow = __dependency9__.rethrow;
var defer = __dependency10__.defer;
var config = __dependency11__.config;
var resolve = __dependency12__.resolve;
var reject = __dependency13__.reject;
function configure(name, value) {
config[name] = value;
}
__exports__.CancellationError = CancellationError;
__exports__.Promise = Promise;
__exports__.EventTarget = EventTarget;
__exports__.all = all;
__exports__.any = any;
__exports__.Queue = Queue;
__exports__.ResolvedQueueError = ResolvedQueueError;
__exports__.delay = delay;
__exports__.timeout = timeout;
__exports__.hash = hash;
__exports__.rethrow = rethrow;
__exports__.defer = defer;
__exports__.denodeify = denodeify;
__exports__.configure = configure;
__exports__.resolve = resolve;
__exports__.reject = reject;
});
window.RSVP = requireModule("rsvp");
})(window);
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment