Commit e4c0347e authored by Sven Franck's avatar Sven Franck

WIP modularizing global config/removing hacks for homogenous storage access

parent a0e03508
[
{
"type": "StateMachine",
"set_on": "state_dict",
"scheme": [{
"set_name": "sync_status",
"property_dict": {
......@@ -36,6 +37,7 @@
},
{
"type": "i18n",
"set_on": "lang_dict",
"initializer": "init",
"handler": "language",
"property_dict": {
......@@ -48,45 +50,24 @@
"fallbackLng": "en-EN",
"resGetPath": "lang/__lng__/__ns__.json",
"ns": "dict"
}
}
}],
"children": []
},
{
"set_on": "storage",
"initializer": "createJIO",
"property_dict": {
"status_dict": {
"type" : "loader",
"theme": "slapos-black"
},
"no_content": true,
"path_dict": {
"type": "status_dict",
"property_dict": {
"loader": true,
"loader_theme": "slapos-black"
}
},
{
"type": "path_dict",
"property_dict": {
"data": "data/",
"home": "#dashboard"
},
"i18n": "i18next",
"i18n_force_browser_language": false,
"i18n_dict": {
"lng": "en-EN",
"load": "current",
"fallbackLng": "en-EN",
"resGetPath": "lang/__lng__/__ns__.json",
"ns": "dict"
},
"state_dict": {
"type": "StateMachine",
"machine_list": [
]
}
},
"children": []
}
{
"children": [{
"generate": "widget",
......
......@@ -2,7 +2,6 @@
"portal_type_source": "Person",
"portal_type_title": "person",
"portal_type_fields": "person_fieldlist",
"portal_type_mapper": "person",
"initial_query": {"include_docs": true, "limit":[0,1]},
"form": true,
"view": "web_view",
......
[{
"type": "jIO",
"set_on": "storage",
"set_on": "storage_dict",
"initializer": "createJIO",
"modernizr": ["blobconstructor", "filereader"],
"property_dict": {
"storage": true,
"force_field_definitions": true,
"skip_total_records": true
},
......
......@@ -544,13 +544,15 @@
* @param {object} response Object object passed on to next element
**/
"installed_services": function (reply) {
var config, property, query, fetch, href, pass = reply.pass;
var config, property, query, fetch, href, store, pass = reply.pass;
if (storage) {
store = app.default_dict.storage_dict;
if (store.property_dict.storage) {
// access storage
fetch = reply.pass.value || reply.pass.state.query.force;
return storage.items.get({"_id": window.decodeURIComponent(fetch)})
return store.items.get({"_id": window.decodeURIComponent(fetch)})
.then(function(answer) {
query = util.parse(answer).data._links.slapos_jump._query;
......@@ -585,13 +587,15 @@
* @param {object} response Object object passed on to next element
**/
"release_list": function (reply) {
var config, property, query, fetch, href, pass = reply.pass;
var config, property, query, fetch, href, store, pass = reply.pass;
store = app.default_dict.storage_dict;
if (storage) {
if (store.property_dict.storage) {
// access storage
fetch = reply.pass.value || reply.pass.state.query.force;
return storage.items.get({"_id": window.decodeURIComponent(fetch)})
return store.items.get({"_id": window.decodeURIComponent(fetch)})
.then(function(answer) {
query = util.parse(answer).data._links.slapos_jump._query;
......@@ -626,11 +630,13 @@
* @param {object} response Object object passed on to next element
**/
"software_list": function (reply) {
var config, property, query, props, pass = reply.pass;
var config, property, query, props, store, pass = reply.pass;
store = app.default_dict.storage_dict;
if (storage) {
if (store.property_dict.storage) {
config = {
"url": storage.items.dict.url,
"url": store.items.definition_dict.url,
"type": "GET",
"xhrFields": {
"withCredentials": true
......@@ -675,13 +681,15 @@
* @param {object} response Object object passed on to next element
**/
"subscription_details": function (reply) {
var config, property, query, fetch, href, pass = reply.pass;
var config, property, query, fetch, href, store, pass = reply.pass;
if (storage) {
store = app.default_dict.storage_dict;
if (store.property_dict.storage) {
// access storage
fetch = reply.pass.value || reply.pass.state.query.force;
return storage.items.get({"_id": window.decodeURIComponent(fetch)})
return store.items.get({"_id": window.decodeURIComponent(fetch)})
.then(function(answer) {
query = util.parse(answer).data._links.slapos_jump._query;
......@@ -716,11 +724,13 @@
* @param {object} response Object object passed on to next element
**/
"ticketing": function (reply) {
var config, property, query, href, i, reply, pass = reply.pass;
var config, property, query, href, i, reply, store, pass = reply.pass;
store = app.default_dict.storage_dict;
if (storage) {
if (store.property_dict.storage) {
config = {
"url": storage.items.dict.url,
"url": store.items.definition_dict.url,
"type": "GET",
"xhrFields": {
"withCredentials": true
......@@ -772,13 +782,15 @@
* @param {object} response Object object passed on to next element
**/
"ticket_status": function (reply) {
var fetch, config, property, query, href, i, reply, pass = reply.pass;
var fetch, config, property, query, href, i, store, reply, pass = reply.pass;
store = app.default_dict.storage_dict;
if (storage) {
if (store.property_dict.storage) {
// access storage
fetch = reply.pass.value || reply.pass.state.query.force;
return storage.items.get({"_id": window.decodeURIComponent(fetch)})
return store.items.get({"_id": window.decodeURIComponent(fetch)})
.then(function(answer) {
query = util.parse(answer).data._links.slapos_jump._query;
......@@ -807,6 +819,177 @@
return (reply);
},
/**
* @method subscriptions
* @param {object} reply Object received from previous chain element
* @param {object} response Object object passed on to next element
**/
"servers": function (reply) {
var config, property, query, href, i, store, reply, pass = reply.pass;
store = app.default_dict.storage_dict;
if (store.property_dict) {
config = {
"url": store.items.definition_dict.url,
"type": "GET",
"xhrFields": {
"withCredentials": true
}
};
// access storage
return jIO.util.ajax(config)
.then(function(response) {
return jIO.util.ajax({
"url": util.parse(response.target.responseText)._links.me.href,
"xhrFields": {
"withCredentials": true
}
});
}).then(function (answer) {
reply = util.parse(answer.target.responseText)._links.slapos_jump;
for (i = 0; i < reply.length; i += 1) {
if (reply[i].name === "current_computer") {
query = reply[i]._query;
}
}
if (query === undefined) {
query = reply.href;
}
if (pass.config.initial_query === undefined) {
pass.config.initial_query = {};
}
pass.config.initial_query.query = query;
// pass hacked query back into chain
if (reply.response) {
return {
"response": util.parse(reply.response),
"pass": pass
};
}
return {
"pass": pass
}
}).fail(util.error);
}
return (reply);
},
/**
* @method subscriptions
* @param {object} reply Object received from previous chain element
* @param {object} response Object object passed on to next element
**/
"invoices": function (reply) {
var config, property, query, href, i, reply, store, pass = reply.pass;
store = app.default_dict.storage_dict;
if (store.property_dict.storage) {
config = {
"url": store.items.definition_dict.url,
"type": "GET",
"xhrFields": {
"withCredentials": true
}
};
// access storage
return jIO.util.ajax(config)
.then(function(response) {
return jIO.util.ajax({
"url": util.parse(response.target.responseText)._links.me.href,
"xhrFields": {
"withCredentials": true
}
});
}).then(function (answer) {
reply = util.parse(answer.target.responseText)._links.slapos_jump;
for (i = 0; i < reply.length; i += 1) {
if (reply[i].name === "current_invoice") {
query = reply[i]._query;
}
}
if (query === undefined) {
query = reply.href;
}
if (pass.config.initial_query === undefined) {
pass.config.initial_query = {};
}
pass.config.initial_query.query = query;
// pass hacked query back into chain
if (reply.response) {
return {
"response": util.parse(reply.response),
"pass": pass
};
}
return {
"pass": pass
}
}).fail(util.error);
}
return (reply);
},
/**
* @method subscriptions
* @param {object} reply Object received from previous chain element
* @param {object} response Object object passed on to next element
**/
"networks": function (reply) {
var config, property, query, href, i, reply, store, pass = reply.pass;
store = app.default_dict.storage_dict;
if (store.property_dict.storage) {
config = {
"url": store.items.definition_dict.url,
"type": "GET",
"xhrFields": {
"withCredentials": true
}
};
// access storage
return jIO.util.ajax(config)
.then(function(response) {
return jIO.util.ajax({
"url": util.parse(response.target.responseText)._links.me.href,
"xhrFields": {
"withCredentials": true
}
});
}).then(function (answer) {
reply = util.parse(answer.target.responseText)._links.slapos_jump;
for (i = 0; i < reply.length; i += 1) {
if (reply[i].name === "current_network") {
query = reply[i]._query;
}
}
if (query === undefined) {
query = reply.href;
}
if (pass.config.initial_query === undefined) {
pass.config.initial_query = {};
}
pass.config.initial_query.query = query;
// pass hacked query back into chain
if (reply.response) {
return {
"response": util.parse(reply.response),
"pass": pass
};
}
return {
"pass": pass
}
}).fail(util.error);
}
return (reply);
},
/**
* @method subscriptions
* @param {object} reply Object received from previous chain element
......@@ -814,11 +997,13 @@
**/
// TODO: should be renamed to overview
"subscriptions": function (reply) {
var config, property, query, href, i, reply, pass = reply.pass;
var config, property, query, href, i, reply, store, pass = reply.pass;
if (storage) {
store = app.default_dict.storage_dict;
if (store.property_dict.storage) {
config = {
"url": storage.items.dict.url,
"url": store.items.definition_dict.url,
"type": "GET",
"xhrFields": {
"withCredentials": true
......@@ -931,10 +1116,11 @@
**/
"update_scope": function (obj) {
var element = obj.element, formData, valid, replace, property, value, id,
decode;
decode, store;
id = obj.state.fragment_list[1];
formData = new FormData();
store = app.default_dict.storage_dict;
valid = storage.validate(obj.gadget);
decode = /^[^\/]*%2[^\/]*$/.test(id);
......@@ -959,7 +1145,7 @@
}
util.loader("", "status_dict.updating");
storage["items"].get({"_id": window.decodeURIComponent(obj.state.fragment_list[1])},{"_view": "web_view"})
store.items.get({"_id": window.decodeURIComponent(obj.state.fragment_list[1])},{"_view": "web_view"})
.then(function(response) {
return jIO.util.ajax({
"url": util.parse(response).data._actions.update_allocation_scope.href,
......@@ -989,15 +1175,16 @@
* @param {object} obj Action Object
**/
"request_ssl": function (obj) {
var element, formData, decode, id;
var element, formData, decode, id, store;
id = obj.state.fragment_list[1];
element = obj.element;
store = app.default_dict.storage_dict;
formData = new FormData();
decode = /^[^\/]*%2[^\/]*$/.test(id);
util.loader("", "status_dict.updating");
storage["items"].get({"_id": window.decodeURIComponent(obj.state.fragment_list[1])},{"_view": "web_view"})
store.items.get({"_id": window.decodeURIComponent(obj.state.fragment_list[1])},{"_view": "web_view"})
.then(function(response) {
return jIO.util.ajax({
"url": util.parse(response).data._actions.generate_certificate.href,
......@@ -1023,15 +1210,16 @@
},
"revoke_ssl": function (obj) {
var element, formData, decode, id;
var element, formData, decode, id, store;
id = obj.state.fragment_list[1];
element = obj.element;
store = app.default_dict.storage_dict;
formData = new FormData();
decode = /^[^\/]*%2[^\/]*$/.test(id);
util.loader("", "status_dict.updating");
storage["items"].get({"_id": window.decodeURIComponent(obj.state.fragment_list[1])},{"_view": "web_view"})
store.items.get({"_id": window.decodeURIComponent(obj.state.fragment_list[1])},{"_view": "web_view"})
.then(function(response) {
return jIO.util.ajax({
"url": util.parse(response).data._actions.revoke_certificate.href,
......@@ -1104,9 +1292,12 @@
* @param {string} identifier
**/
"download": function (obj) {
var store;
store = app.default_dict.storage_dict;
util.loader("", "status_dict.downloading");
storage["items"].get({"_id": window.decodeURIComponent(obj.element.href.split("/").pop())},{"_view": "web_view"})
store.items.get({"_id": window.decodeURIComponent(obj.element.href.split("/").pop())},{"_view": "web_view"})
.then(function(response) {
util.loader("", "status_dict.rendering");
window.location.href = util.parse(response).data._links.slapos_jump.href;
......@@ -1442,10 +1633,11 @@
**/
"update_ticket": function (obj) {
var element = obj.element, formData, valid, replace, property, value, id,
decode;
decode, store;
id = obj.state.fragment_list[1];
formData = new FormData();
store = app.default_dict.storage_dict;
valid = storage.validate(obj.gadget);
decode = /^[^\/]*%2[^\/]*$/.test(id);
......@@ -1470,7 +1662,7 @@
}
util.loader("", "status_dict.updating");
storage["items"].get({"_id": window.decodeURIComponent(obj.state.fragment_list[1])},{"_view": "web_view"})
store.items.get({"_id": window.decodeURIComponent(obj.state.fragment_list[1])},{"_view": "web_view"})
.then(function(response) {
return jIO.util.ajax({
"url": util.parse(response).data._actions.update.href,
......@@ -2279,8 +2471,8 @@
if (status_flag) {
switch (status_flag.type) {
case "loader":
app.default_dict.loader = true;
app.default_dict.loader_theme = status_flag.theme;
app.default_dict.property_dict.loader = true;
app.default_dict.property_dict.loader_theme = status_flag.theme;
break;
}
}
......@@ -2289,7 +2481,7 @@
if (path_flag) {
for (single_path in path_flag) {
if (path_flag.hasOwnProperty(single_path)) {
app.default_dict.path_dict[single_path] = path_flag[single_path];
app.default_dict.property_dict[single_path] = path_flag[single_path];
}
}
} else {
......@@ -3028,7 +3220,7 @@
};
// only add last button, if total is available
if (storage.property_dict.skip_total === undefined) {
if (!app.default_dict.storage_dict.property_dict.skip_total_records) {
config.children[0].children.push({
"type": "a",
"direct":{"className":"action", "href":"#"},
......@@ -5696,7 +5888,6 @@
var property, replace, obj, value, form_to_submit, formData,
valid, pass_id, validate_portal_type_fields, form_data_set, decode;
form_to_submit = document.getElementById(config.id);
formData = new FormData();
valid = storage.validate(config);
......@@ -5744,7 +5935,7 @@
}
util.loader("", "status_dict.saving");
console.log("mh")
// TODO: a post should always include all required fields
// TODO: find a way to set default fields, like timestamp etc
app.store({
......@@ -5760,6 +5951,7 @@
}
})
.then(function (answer) {
console.log("do we get here=")
if (answer.response && answer.response.result === "success") {
pass_id = answer.response.id;
switch (config.gadget.getAttribute("data-reset")) {
......@@ -5824,15 +6016,11 @@
app.default_dict = {
"storage_dict": {
"settings": "settings",
"items": "items",
"configuration": "configuration",
"gadgets": "gagdets",
"gadgets": "gadgets",
"data_type": "portal_types"
},
"path_dict": {
"data": "data",
"home": "home"
},
"lang_dict": {},
"state_dict": {},
"empty_array": []
};
......@@ -6439,7 +6627,7 @@
info_field_list = element.querySelectorAll(".info");
info = "";
no_total = total === undefined && storage.property_dict.skip_total;
no_total = total === undefined && app.default_dict.storage_dict.property_dict.skip_total_records;
generateInfo = function (text, text_i18n) {
return factory.element(
"span",
......@@ -6696,8 +6884,8 @@
if (default_query.include_docs || value !== undefined) {
obj.include_docs = true;
}
if (field_list && field_list.length > 0 && storage.property_dict.force_fields) {
if (field_list && field_list.length > 0 &&
app.default_dict.storage_dict.property_dict.force_field_definitions) {
delete obj.include_docs;
obj.select_list = field_list;
}
......@@ -6854,7 +7042,7 @@
// home
breadcrumb.appendChild(
makeLink(app.default_dict.path_dict.home, undefined, true)
makeLink(app.default_dict.property_dict.home, undefined, true)
);
// fragments
......@@ -7047,7 +7235,9 @@
* @return {object} response object/promise
*/
app.fetchDataQuery = function (reply) {
var parsed, pass = reply.pass, key, value;
var parsed, pass = reply.pass, key, value, force_fields;
force_fields = app.default_dict.storage_dict.property_dict.force_field_definitions;
if (pass.skip === undefined &&
(pass.no_auth || (pass.auth && pass.active_login))) {
......@@ -7062,14 +7252,13 @@
// make sure include_docs is set, to fetch data. DON'T if force_fields
// is set, because then we fetch by columns only (not whole docs)
if (storage === undefined ||
(storage && storage.property_dict.force_fields === undefined)) {
if (storage === undefined || (storage && !force_fields)) {
pass.state.query.include_docs = true;
}
pass.state.query.limit = pass.store_limit;
} else {
// get the columns required based on the gadget config!
if (pass.fields && storage && storage.property_dict.force_fields) {
if (pass.fields && storage && force_fields) {
if (pass.config.scheme) {
pass.field_list = storage.makeSelectList(pass.config.scheme);
}
......@@ -7113,23 +7302,23 @@
app.mapToStorage = function (reply) {
var pass = reply.pass, answer;
// if (reply.response) {
// // mapping required for this portal_type
// if (pass.config && pass.config.portal_type_mapper) {
// return map.handlers[pass.config.portal_type_mapper](reply);
// }
// // standard procedure
// answer = util.parse(answer);
// return {
// "response": answer,
// "pass": pass
// };
//
// }
// // mapping
// if (pass.config && pass.config.portal_type_mapper) {
// return map.handlers[pass.config.portal_type_mapper](reply);
// }
if (reply.response) {
// mapping required for this portal_type
if (pass.config && pass.config.portal_type_mapper) {
return map.handlers[pass.config.portal_type_mapper](reply);
}
// standard procedure
answer = util.parse(answer);
return {
"response": answer,
"pass": pass
};
}
// mapping
if (pass.config && pass.config.portal_type_mapper) {
return map.handlers[pass.config.portal_type_mapper](reply);
}
// standard
return {
"pass": pass
......@@ -7238,7 +7427,7 @@
pass.fields = util.parse(reply.response);
}
store = storage.items;
store = app.default_dict.storage_dict.items;
items = util.parse(reply.response);
if (store && items.length > 0) {
......@@ -7307,12 +7496,16 @@
// obj.portal_type = pass.type;
// if an object identifier was set in the form, we PUT, else POST
console.log("so.... where is the identifier")
console.log(obj)
console.log(obj.identifier)
if (obj.identifier) {
method = "put";
hacked_view = {"_view": pass.view};
obj._id = obj.identifier;
delete obj.identifier;
}
// store
promises[i] = store[method || "post"](obj, hacked_view || {})
.then(function (answer) {
......@@ -7321,9 +7514,10 @@
.fail(util.error);
}
} else {
console.log("here?")
// do it again, Sam...
promises[0] = jIO.util.ajax({
"url": storage.items.dict.url,
"url": app.default_dict.storage_dict.items.dict.url,
"type": "GET",
"xhrFields": {
"withCredentials": true
......@@ -7631,7 +7825,7 @@
return RSVP.resolve({"pass": parcel.pass});
}
store = storage[parcel.storage];
store = parcel.storage;
return store.getAttachment({
"_id": parcel.file,
......@@ -7678,7 +7872,8 @@
pass = parcel.pass;
query = parcel.query;
skip = query && query.limit && query.limit.length === 0 && storage.property_dict.skip_total;
skip = query && query.limit && query.limit.length === 0 &&
app.default_dict.storage_dict.property_dict.skip_total_records;
// return here, if skipping total query
......@@ -7704,7 +7899,7 @@
convert = "values";
}
return storage[parcel.storage][method || "allDocs"](parcel.query, hacked_view)
return app.default_dict.storage_dict[parcel.storage][method || "allDocs"](parcel.query, hacked_view)
.then(function (response) {
// TODO: best way?
if (convert !== undefined && response.status === 200) {
......@@ -7731,7 +7926,7 @@
app.loadFile = function (property_dict) {
var response, storage_location, url, hacked_view;
url = property_dict.url || app.default_dict.path_dict.data +
url = property_dict.url || app.default_dict.property_dict.data +
property_dict.attachment + ".json";
// no url... throw error unless optional is set!
......@@ -7754,8 +7949,7 @@
// store file
storage_location = property_dict.store ||
(storage ? storage[app.default_dict.storage_dict.settings] :
undefined);
(storage ? app.default_dict.storage_dict.settings : undefined);
// hacked view?
if (property_dict && property_dict.pass && property_dict.pass.config) {
......@@ -7768,13 +7962,12 @@
}
return storage_location.put(
{"_id": (property_dict.file || app.default_dict.storage_dict.settings)},
{"_id": (property_dict.file || "settings")},
/*hacked_view || */{}
)
.then(function () {
return storage_location.putAttachment({
"_id": (property_dict.file ||
app.default_dict.storage_dict.settings),
"_id": (property_dict.file || "settings"),
"_attachment": (property_dict.attachment ||
app.default_dict.storage_dict.configuration),
"_data": JSON.stringify(response),
......@@ -8234,13 +8427,13 @@
// hm... jQuery... hm string building
// TODO: better way?
if (app.default_dict.loader) {
if (app.default_dict.property_dict.loader) {
$.mobile.loading(
"show",
{
"text": "",
"textVisible": true,
"theme": app.default_dict.loader_theme,
"theme": app.default_dict.property_dict.loader_theme,
"html": "<span class='ui-icon-" +
(icon ? (icon + " loader_icon") : "loading") + "'> </span>" +
"<h1 class='loader_message'>" + text_to_display + "</h1>"
......@@ -8558,7 +8751,7 @@
"config" : function (content_dict) {
var i, j, k, arr, promise_list, len, feature, feature_len, name, dict,
set, child_len, type, nav, language;
set, child_len, type, nav, language, target, initializer, solver;
set = app.default_dict;
arr = set.empty_array;
......@@ -8567,53 +8760,82 @@
for (i = 0; i < len; i += 1) {
feature = content_dict[i];
feature_len = (feature.scheme || arr).length;
type = feature.type;
// support
// test support
if (app.init.testSupport(feature.modernizr || [])) {
feature_len = (feature.scheme || arr).length;
type = feature.type;
target = set[feature.set_on] || set;
initializer = feature.initializer;
// merge properties on target
target.property_dict = util.mergeObject(
feature.property_dict || {},
target.property_dict || {}
);
// TODO: would be nice if generic, without eval()...
switch (feature.set_on) {
case "storage":
set = storage;
util.mergeObject(feature.property_dict, set.property_dict || {});
break;
}
// NOTE: defaulting to setting the global on app.default_dict, so
// there is no need to query against window.xyz
// NOTE: Set all globals on app.default_dict and reference from there
for (j = 0; j < feature_len; j += 1) {
dict = feature.scheme[j];
name = dict.set_name ||
dict.property_dict.application_name || type;
name = dict.set_name || dict.property_dict.application_name || type;
// preserve definition
target.property_dict[name] = dict.property_dict;
// TODO: not generic, try to remove
// TODO: Force browser language for i18n - not generic, remove
switch (feature.handle) {
case "language":
// force browser language in i18n
nav = window.navigator;
language = nav.userLanguage || nav.language;
if (language && feature.property_dict.use_browser_language) {
if (language && set.property_dic.use_browser_language) {
dict.fallbackLng = dict.lng;
dict.lng = language;
}
break;
}
// set promise
set[name] = promise_list[j] =
window[type][feature.initializer](dict.property_dict) || window[type];
// set promise and access to this module
target[name] = promise_list[j] = initializer ?
window[type][feature.initializer](dict.property_dict) : window[type];
}
}
child_len = (feature.children || arr).length;
for (k = 0; k < child_len; k += 1) {
promise_list[j + k] = app.setContent(content_dict.children[k]);
promise_list[j + k] = app.setContent(feature.children[k]);
}
}
return RSVP.all(promise_list)
// TODO: remove all this, should be handled by setContent
.then (function (response_list) {
var target, l, container, i18n;
return RSVP.all(promise_list);
// create a fragment for promises
target = document.createDocumentFragment();
for (l = 0; l < response_list.length; l += 1) {
container = response_list[l];
// exclude undefined promises (i18n)
if (container && container.nodeName) {
// translate
if (i18n) {
map.actions.translateNodeList(container);
}
// insert panel as first child into DOM
if (util.testForString("ui-panel", container.className)) {
document.body.insertBefore(
container,
document.body.children[0]
);
} else {
target.appendChild(container);
}
}
document.body.appendChild(target);
}
});
}
};
......@@ -8639,7 +8861,8 @@
"url": util.fetchPath("data-config")
});
})
.then(app.setContent)
.then(app.init.config)
.then(app.setGlobalBindings)
.then(app.parsePage)
.fail(util.error);
});
......
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