Commit 700be286 authored by Sven Franck's avatar Sven Franck

app: switch to current version of app

parent 2a89df83
......@@ -77,6 +77,7 @@
case "type":
case "rel":
case "alt":
case "src":
case "readonly":
case "size":
case "colspan":
......@@ -178,7 +179,7 @@
theme, icon_string, input_type, need_text_node, container_class_list,
label_class_list, index, disabled, active, text, addLabel, readonly,
mask_set, mask, star, hidden_field, icon, no_validate, has_opts,
push_label, no_text;
push_label, no_text, label_set;
// exit early with a span tag
// TODO: make this with label et al
......@@ -269,7 +270,7 @@
// optionally wrap input in a div-fieldcontain
// NOTE: route is for custom HTML elements... service_instance_status
if (!spec.logic.route && (spec.logic.wrap !== true || hidden_field)) {
if (!spec.logic.route && (spec.logic.wrap !== true && spec.wrap !== true) || hidden_field) {
wrapper = document.createDocumentFragment();
} else {
wrapper = factory.element({
......@@ -437,12 +438,14 @@
// This saves doing "invalid" handler with javascript
if (spec.logic.add_label !== false && ((need_text_node === undefined &&
(push_label || !label_inside) && !hidden_field) || spec.logic.route)) {
label_set = true;
label_target.appendChild(addLabel(spec, label_class_list));
}
// add <span> which contains JQM current select
if (element_reverse) {
element_target.appendChild(factory.element({"type": "span"}));
element_target.appendChild(factory.element({
"type": "span"}));
} else {
element_target = container;
}
......@@ -456,7 +459,7 @@
element_target.appendChild(factory.element(spec));
// checkbox radio need label after input
if (label_inside) {
if (label_inside && !label_set) {
label_target.appendChild(addLabel(spec, label_class_list));
}
......@@ -466,9 +469,8 @@
if (!no_validate && !spec.logic.route) {
element_target.appendChild(factory.element({
"type": "span",
"direct": {"className": "ui-invalid-label"},
"attributes": {"data-i18n":"validation_dict.required_field"},
"logic": {"text": "Ce champ est obligatoire."}
"direct": {"className": "ui-invalid-label translate"},
"attributes": {"data-i18n":"validation_dict.required_field"}
}));
}
......@@ -540,7 +542,8 @@
* "title": "",
* "title_i18n": null,
* "theme": null,
* "fix": true,
* "fix_footer": null,
* "fix_header": null
* "create": null (added automatically)
* "data_url": null (added automatically)
* "fragment_list": [] (added automatically)
......@@ -560,14 +563,13 @@
// new page
if (container === null || spec.create === true) {
container = factory.element({
"type": "div",
"direct": {
"className": " ui-page " + ("ui-page-theme-" +
spec.theme || "") + " " + ((spec.fix &&
spec.fix === false) ? "" :
" ui-page-header-fixed ui-page-footer-fixed")
spec.theme || "") + " " +
(spec.fix_header ? " ui-page-header-fixed" : " ") +
(spec.fix_footer ? " ui-page-footer-fixed" : " ")
},
"attributes": {
"data-role": "page",
......@@ -927,7 +929,7 @@
"direct": {
"id": id,
"className": (spec.class_list || " ") +
(spec.fixed ? " ui-header-fixed " : " ") + " bar ui-header foo " +
(spec.fixed ? " ui-header-fixed " : " ") + " ui-header " +
" slidedown ui-bar-" + (spec.theme || "inherit")
},
"attributes": {
......@@ -995,6 +997,7 @@
direction = spec.direction || "vertical";
// NOTE: will only be used for dynamic element creation
// TODO: what does this do here?
generator = function (generator_spec) {
var element = generator_spec.item;
// TODO: no!
......@@ -1011,6 +1014,7 @@
// controlgroup label
if (spec.label) {
// TODO: how to get required on the legend? or off?
// NOTE: this will always wrap controlgroups!!!
i18n = spec.label.title_i18n || spec.label.text_i18n;
contain = factory.element({
......@@ -1028,9 +1032,11 @@
});
label.appendChild(factory.element({
"type": "legend",
"logic": {
"text": spec.label.text,
"data-i18n": i18n ? (spec.label.text_i18n) : null
"direct": {
"className": "translate required_label"
},
"attributes": {
"data-i18n": spec.label.text_i18n
}
}));
contain.appendChild(label);
......@@ -1049,7 +1055,10 @@
"data-enhanced": "true",
"data-type": direction
},
"logic": {"id": spec.id || null}
"logic": {
"id": spec.id || null,
"data-persist": spec.persist || null
}
});
// controls
......@@ -1335,8 +1344,8 @@
"direct": {
"className": class_list
},
"attributes": {},
"logic": {
"data-persist": spec.persist || null,
"data-wrap": slot ? null : true,
"data-slot": slot ? true : null,
"data-slot-id": slot || null,
......@@ -1477,6 +1486,9 @@
container = document.createDocumentFragment();
form_id = "form_" + util.uuid();
item_id = spec.data ? (spec.data._id || undefined) : undefined;
if (spec.update) {
form = document.createDocumentFragment();
} else {
form = factory.element({
"type": "form",
"direct": {
......@@ -1485,13 +1497,17 @@
"action": "#",
"className": (spec.class_list || "")
},
"attributes": {"data-ajax": false, "autocomplete": "off"},
"attributes": {
"data-ajax": false,
"autocomplete": "off",
"data-widget": "form"
},
"logic": {
"data-reference": spec.reference || null,
"data-depend": spec.depend || null,
"data-reset": spec.reset || null
"data-map": spec.map_children || null
}
});
}
// NOTE: needed to differentiate between PUT/POST
// TODO: block_identifier is only used for release_install, find better!
......@@ -1826,6 +1842,7 @@
*/
// TODO: add collapsible support if needed
// TODO: dividers? will not be in spec, so can only be listview option!
// TODO: find better way to set mapper/widget
factory.widget.listview = function (spec) {
var fragment, has_filter, generator;
......@@ -1916,25 +1933,17 @@
// search filter
if (spec.update !== true) {
// NOTE: if input provided, the filter is already there!
if (spec.filter && spec.input === undefined) {
// THIS MAKES B
if (spec.filter && !spec.input) {
has_filter = true;
fragment.appendChild(factory.widget.formElement({
"type": "input",
"direct": {
"id": "filter_" + (spec.id || "items"),
"className": "action"
},
"attributes": {
"data-action": "search",
"data-enhanced": true,
"data-i18n": "",
"placeholder": "Search",
"data-icon": "search"
},
"logic": {
"clear": "true"
}
}));
fragment.appendChild(factory.widget.formElement(
factory.util.searchBarTemplate({
"search_id": spec.id || "search",
"text": spec.text || "",
"text_i18n": spec.text_i18n || null
})
));
}
fragment.appendChild(factory.element({
......@@ -1944,18 +1953,22 @@
(spec.inset ?
" ui-listview-inset ui-corner-all ui-shadow " : "")
},
"attributes": {"data-role": "listview", "data-enhanced": true},
"attributes": {
"data-role": "listview",
"data-enhanced": true,
// NOTE: on updates, we need wrapper with generator and mapper???
"data-widget": "listview"
},
"logic": {
"id": spec.id || null,
"data-update": spec.dynamic ? true : null,
// NOTE: on updates, we need wrapper with generator...
"data-widget": "listview",
"data-reference": spec.reference || null,
"data-filter": spec.filter || null,
"data-input": spec.input || null,
"data-filter-theme": spec.filter_theme || null,
"data-filter-placeholder": spec.placeholder || null,
"data-divider-theme": spec.divider_theme || null
"data-divider-theme": spec.divider_theme || null,
"data-map": spec.map_children || null
}
}));
}
......@@ -2037,8 +2050,7 @@
*/
// TODO: pre-enhance!
factory.widget.table = function (spec) {
var fragment, container, generator, counter, target, section, i, tag,
image;
var fragment, container, generator, counter, section, i, tag, image;
counter = function (scheme) {
var m, field, k;
......@@ -2060,13 +2072,6 @@
return config.text;
};
target = function () {
return factory.widget.controlbar({
"wrap": "section",
"class_list": "span_1"
});
};
generator = function (generator_spec) {
var quirk_dict, row, temp, cell, j, field, link, logic, faux_id,
attributes, property, snippet, title, k, button, group, dict, set,
......@@ -2291,9 +2296,12 @@
case "header":
tag = factory.element({"type": "thead"});
tag.appendChild(
generator(section.field_list, {
"wrap": true,
"property_dict": spec
generator({
"item":section.field_list,
"wrapper": {
"property_dict": spec,
"wrap": true
}
}).content
);
container.appendChild(tag);
......@@ -2304,22 +2312,21 @@
"attributes": {
"data-update": "true",
"data-widget": "table"
}
},
"logic": {"data-map": spec.map_children || null}
}));
break;
}
}
fragment.appendChild(container);
}
return {
"fragment": fragment,
"child_selector": fragment.querySelector("tbody"),
"child_constructor": generator,
"child_mapper": spec.map_children,
"base": "tr",
"count": counter,
"target": target
"count": counter
};
};
......@@ -2411,9 +2418,9 @@
for (section in config) {
if (config.hasOwnProperty(section)) {
arr = config[section];
if (typeof arr === "object" && arr !== null) {
len = arr.length;
for (k = 0; k < len; k += 1) {
for (k = 0, len = arr.length; k < len; k += 1) {
block = arr[k];
content = factory.util.generateContentElement(
block.type,
......@@ -2431,6 +2438,7 @@
}
}
}
// TODO: return an object...
return [fragment, appendix, class_string];
};
......@@ -2493,10 +2501,10 @@
"type": "span",
"direct": {
"className": "ui-li-icon ui-li-icon-custom ui-icon-" +
spec.icon + " ui-icon"
(spec.icon || spec.value) + " ui-icon"
},
"logic": {"text": "\u00A0"}
}), "ui-li-has-icon"];
}), " ui-li-has-icon"];
// main link, split button
// TODO: make icon generic! what about util.class_string
......@@ -2683,7 +2691,9 @@
string = "";
// NOTE: we only set on <a>
if (element.type === "a") {
// TODO: find a better way than to run everything through here...!
if (element.type === "a" ||
(element.attributes && element.attributes.type === "submit")) {
icon = (element.attributes || {})["data-icon"] || default_icon;
// because of custom a in service_instance_status!
if (!(element.logic || {}).plain_link) {
......@@ -2702,38 +2712,52 @@
return string;
};
/** Generate a seach bar (controlbar)
/** Generate a configuration object for a search input
* @method searchBar
* @param {object} spec Pagination configuration
* @return {object} finished config object
* @param {object} spec Search bar configuration
* @return {object} finished search bar config
**/
factory.util.searchBar = function (spec) {
var i, element_list, search_id, props, class_list;
search_id = "search_" + util.uuid();
class_list = spec.class_list || "";
props = spec.slot ? {"slot": spec.slot} : {};
element_list = [{
factory.util.searchBarTemplate = function (spec) {
return {
"type": "input",
"direct": {"id": search_id, "className": "translate"},
"direct": {"id": spec.search_id, "className": "translate"},
"attributes": {
"data-action": "search",
"data-enhanced": "true",
"data-i18n": "[placeholder]" + spec.text_i18n,
"placeholder": spec.text,
"data-icon": "search",
"data-action-btn": "true",
"data-type": "search",
"type": "search"
},
"logic": {"clear": "true", "action": "search"}
}];
"logic": {
"clear": "true",
"action": "search",
"data-i18n": spec.text_i18n ?
("[placeholder]" + spec.text_i18n) : null
}
};
};
for (i = 0; i < spec.info_list.length; i += 1) {
/** Generate a seach bar (controlbar)
* @method searchBar
* @param {object} spec Pagination configuration
* @return {object} finished config object
**/
factory.util.searchBar = function (spec) {
var i, element_list, search_id, props, class_list, spec_list;
search_id = spec.search_id = "search_" + util.uuid();
props = spec.slot ? {"slot": spec.slot} : {};
props.class_list = spec.class_list || "";
element_list = [factory.util.searchBarTemplate(spec)];
spec_list = spec.info_list || [];
for (i = 0; i < spec_list.length; i += 1) {
element_list.push({
"type": "div",
"direct": {"className": "info"},
"attributes": {"data-info": spec.info_list[i]}
"attributes": {"data-info": spec_list[i]}
});
}
......@@ -2741,7 +2765,6 @@
"config": {
"generate": "widget",
"type": "controlbar",
"class_list": class_list,
"property_dict": props,
"children": element_list
},
......@@ -2750,19 +2773,21 @@
};
/** Generate a pagination toolbar (controlbar)
* @method paginate
* @method paginationBar
* @param {object} spec Pagination configuration
* @return {object} finished config object
**/
// TODO: make this a real widget with methods!
factory.util.paginationBar = function (spec) {
var n, option, props, option_list, class_list, config;
var n, option, props, option_list, config, len, opts, pointer;
option_list = [{"value": "", "text": "", "text_i18n": ""}];
opts = spec.option_list || [];
props = spec.slot ? {"slot": spec.slot} : {};
class_list = spec.class_list || "";
props.class_list = spec.class_list || "";
for (n = 0; n < spec.option_list.length; n += 1) {
option = spec.option_list[n];
for (n = 0, len = opts.length; n < len; n += 1) {
option = opts[n];
option_list.push({
"value": option.value,
"text": option.text,
......@@ -2773,13 +2798,20 @@
config = {
"generate": "widget",
"type": "controlbar",
"class_list": class_list,
"property_dict": props,
"children": [{
"generate": "widget",
"type": "controlgroup",
"property_dict": {"direction": "horizontal"},
"children": [{
"children": []
}]
};
pointer = config.children[0].children;
// first
if (!spec.single) {
config.children[0].property_dict.class_list = "ui-pagination-single";
pointer.push({
"type": "a",
"direct": {"className": "action", "href": "#"},
"attributes": {
......@@ -2789,7 +2821,11 @@
"data-iconpos": "notext"
},
"logic": {"text": "First"}
}, {
});
}
// always add previous
pointer.push({
"type": "a",
"direct": {"className": "action", "href": "#"},
"attributes": {
......@@ -2799,7 +2835,11 @@
"data-iconpos": "notext"
},
"logic": {"text": "Previous"}
}, {
});
// select
if (len > 0) {
pointer.push({
"type": "select",
"direct": {"id": "select_" + util.uuid(), "className": "action"},
"attributes": {
......@@ -2808,7 +2848,11 @@
"data-iconpos": "notext"
},
"logic": {"options": option_list}
}, {
});
}
// always add next
pointer.push({
"type": "a",
"direct": {"className": "action", "href": "#"},
"attributes": {
......@@ -2818,13 +2862,11 @@
"data-iconpos": "notext"
},
"logic": {"text": "Next"}
}]
}]
};
});
// only add last button, if total is available
if (!app.storage_dict.property_dict.skip_total_records) {
config.children[0].children.push({
if (!app.storage_dict.property_dict.skip_total_records && !spec.single) {
pointer.push({
"type": "a",
"direct": {"className": "action", "href": "#"},
"attributes": {
......@@ -2941,20 +2983,113 @@
// content.set..., no need for dynamic content and pointer
map.actions = {
"set_search": function (obj) {
factory.util.setDynamicPointer(obj, "ui_panel_detail_search");
/**
* POST an object
* @method new
* @param {object} obj Action Object
**/
"new": function (obj) {
storage.write(obj)
.then(function (response) {
app.util.loader("", "status_dict.saved", "check");
app.navigate(obj, response);
})
.fail(function (error) {
switch (error.status) {
case 408: app.util.loader("", "status_dict.timeout", "clock-o"); break;
case 400: app.util.loader("", "validation_dict.general", "ban"); break;
default: app.util.loader("", "status_dict.error", "ban"); break;
}
});
},
"set_filter": function (obj) {
factory.util.setDynamicPointer(obj, "ui_panel_groups");
/**
* GET an object
* @method get
* @param {object} obj Action Object
**/
"get": function (obj) {
storage.fetch(obj);
},
"set_sorting": function (obj) {
factory.util.setDynamicPointer(obj, "ui_panel_sort");
/**
* PUT an object
* @method update
* @param {object} obj Action Object
**/
"update": function (obj) {
storage.write(obj)
.then(function (response) {
app.util.loader("", "status_dict.saved", "check");
app.navigate(obj, response);
})
.fail(function (error) {
switch (error.status) {
case 408: app.util.loader("", "status_dict.timeout", "clock-o"); break;
case 400: app.util.loader("", "validation_dict.general", "ban"); break;
default: app.util.loader("", "status_dict.error", "ban"); break;
}
});
},
"set_sales": function (obj) {
factory.util.setDynamicPointer(obj, "ui_panel_sales");
/**
* Same as put but since there is no time to make dyno interaction, we cheat!
* @method update
* @param {object} obj Action Object
**/
"update_custom": function (obj) {
storage.write(obj)
.then(function (response) {
var i, len, dyno_list, dyno, promise_list, dump;
// clear active page, because we need to reload
dump = document.querySelector("div.ui-content");
util.deleteChildren(dump);
// refresh dynos that are left!
dyno_list = document.querySelectorAll("div.dyno");
// set first page to reload and clean it up!
delete app.deeplink_flag;
promise_list = [];
for (i = 0, len = dyno_list.length; i < len; i += 1) {
dyno = dyno_list[i];
// update gadgets
promise_list[i] = app.content.set(
{
"portal_type_source": dyno.state.type,
"portal_type_title": dyno.state.title,
"property_dict": util.mergeObject(
{"dynamic": true},
dyno.state.dyno_dict),
"scheme": dyno.state.scheme
},
{
"reference": dyno.id,
"href": dyno.state.href,
"fragment_list": dyno.state.fragment_list,
"layout_level": dyno.state.layout_level
},
false
)
.fail(app.util.error);
}
return RSVP.all(promise_list)
.then(function (response_list) {
app.util.loader("", "status_dict.saved", "check");
//app.navigate(obj, response);
})
.fail(app.util.error);
})
.then(app.setPageBindings)
.fail(function (error) {
switch (error.status) {
case 408: app.util.loader("", "status_dict.timeout", "clock-o"); break;
case 400: app.util.loader("", "validation_dict.general", "ban"); break;
default: app.util.loader("", "status_dict.error", "ban"); break;
}
});
},
/**
......@@ -2979,14 +3114,14 @@
* @param {object} nodeList Nodelist to translate
* @param {boolean} single Single element
**/
// TODO: no solution yet for selectMenu-refesh and input (submit/reset/btn)
// TODO: don't call this so often on init
"translateNodeList": function (nodeList, single) {
var i, l, element, lookup, targets, target, route_text, elements;
var i, l, element, lookup, targets, target, route_text, elements, len;
elements = single ? [nodeList] : nodeList.querySelectorAll(".translate");
if (i18n) {
for (i = 0; i < elements.length; i += 1) {
for (i = 0, len = elements.length; i < len; i += 1) {
element = elements[i];
lookup = element.getAttribute("data-i18n");
......@@ -3032,14 +3167,19 @@
break;
default:
// NOTE: empty - setting .translate on a wrapper will empty it
while (element.hasChildNodes()) {
element.removeChild(element.firstChild);
}
util.deleteChildren(element);
element.appendChild(document.createTextNode(i18n.t(target[0])));
break;
}
}
}
// handle select > span!
if (element.tagName === "OPTION") {
if (element.selected && element.parentNode.previousSibling) {
element.parentNode.previousSibling.textContent =
element.textContent;
}
}
}
} else {
app.util.error("Translate NodeList - i18n not defined");
......@@ -3063,8 +3203,8 @@
current_language = $.i18n.lng(),
shortcut = function (language) {
switch (language) {
case "zh-CN":
return "flag-cn";
case "fr-FR":
return "flag-fr";
case "en-EN":
return "flag-en";
}
......@@ -3099,42 +3239,6 @@
}
},
/**
* Show jumps popup
* @method jump
* @param {object} obj Action Object
**/
"browse": function (obj) {
factory.util.setDynamicPointer(obj, "browse");
},
/**
* Show task popup
* @method tasks
* @param {object} obj Action Object
**/
"tasks": function (obj) {
factory.util.setDynamicPointer(obj, "tasks");
},
/**
* Show application popup
* @method login
* @param {object} obj Action Object
**/
"login": function (obj) {
factory.util.setDynamicPointer(obj, "login");
},
/**
* Show export popup
* @method export
* @param {object} obj Action Object
**/
"export": function (obj) {
factory.util.setDynamicPointer(obj, "export");
},
/**
* Generic pagination method changing number of records displayed
* @method limit
......@@ -3316,14 +3420,19 @@
})
.fail(app.util.error);
}
return RSVP.resolve(wrapper.child_constructor({
"item": element,
"wrapper": wrapper,
"count": i
}));
}
// NOTE: when loading content via href, element is set to undefined
// TODO: Why? ^^^^^^
// NOTE: fetch subordinate fields
if (element && element.needs_subordination) {
return app.util.setSubordinate(element, wrapper);
}
return RSVP.resolve(element || wrapper.inherit);
};
......@@ -3339,12 +3448,14 @@
// TODO: multiple header rows!
// TODO: action_menu input + form?
// TODO: how to handle translations of record texts?
// TODO: subordinate handling
// TODO: pick translations from field_dict for header!
map.tableItem = function (spec) {
var j, k, segment, row, cell, id, quirk_dict, record, new_item, item;
quirk_dict = spec.parent.property_dict;
quirk_dict = spec.wrapper.property_dict;
item = spec.item;
record = item.doc;
record = item.doc || {};
id = record[quirk_dict.link_identifier] || record._id || item._id;
new_item = {};
......@@ -3396,17 +3507,20 @@
**/
// TODO: move radio/check into property_dict and item? or do same on tableItem
// TODO: not nice & find better handling for text elements and i18n tags
// TODO: subordinated fields should have links, not id only!!!
// NOTE: make sure there is no "id" flying through here!!! only _id
map.listItem = function (spec) {
var quirk_dict, section, pos, label, j, k, field, setter, record, new_item,
id, key, obj, translation_fields, item;
var quirk_dict, section, label, j, k, field, setter, record, new_item,
id, key, translation_fields, item, relation, promise_list, pass, count;
promise_list = [];
translation_fields = "titletextlabel";
quirk_dict = spec.wrapper.property_dict;
item = spec.item;
record = item.doc;
id = record[quirk_dict.link_identifier] || record._id || item._id;
new_item = {};
count = 0;
// radio
if (quirk_dict.radio) {
......@@ -3435,27 +3549,58 @@
// loop scheme sections
for (j = 0; j < item.scheme.length; j += 1) {
section = item.scheme[j];
pos = section.position;
new_item[pos] = new_item[pos] || [];
// loop scheme fields
for (k = 0; k < section.field_list.length; k += 1) {
field = section.field_list[k];
key = field.value || field.field || "";
relation = key.split("subordinate_");
pass = {
"record": record,
"position": section.position,
"key": key,
"field": field,
"text_element": translation_fields.indexOf(key) > -1
};
// fetch values of subordinate fields via query or flux/memory
if (relation.length > 1) {
pass.relation = record[key];
pass.subordinate = relation[1];
promise_list[count + k] = storage.subordinate(pass);
} else {
pass.value = util.generateText(record[key], field, record);
promise_list[count + k] = RSVP.resolve(pass);
}
}
count += k;
}
return RSVP.all(promise_list)
.then(function(response_list) {
var l, len, response, obj, setter, pos;
for (l = 0, len = response_list.length; l < len; l += 1) {
response = response_list[l];
pos = response.position;
obj = {};
key = field.value || field.field;
new_item[pos] = new_item[pos] || [];
if (translation_fields.indexOf(key) > -1) {
// text/label/... all stuff translateable
if (response.text_element) {
obj[response.key + "_18n"] = record[response.key + "_i18n"];
setter = key;
obj[setter + "_i18n"] = record[key + "_i18n"];
} else {
setter = "value";
}
obj[setter] = util.generateText(record[key], field, record);
new_item[pos].push(util.mergeObject(field, obj));
}
}
return RSVP.resolve(new_item);
obj[setter] = response.value;
new_item[pos].push(util.mergeObject(response.field, obj));
}
return new_item;
})
.fail(app.util.error);
};
/* ********************************************************************** */
......@@ -3469,6 +3614,7 @@
**/
// TODO: should a span still have full structure (label/container)?
// TODO: should we wrap in span_12 here. why do we wrap at all, do with CSS
// TODO: subordinate value
map.formItem = function (spec) {
var helper, getHelp, j, k, segment, section, field, setValue, root, veto,
textarea_value, override_value, value, field_value, validation_list,
......@@ -3488,23 +3634,25 @@
// TODO: remove this crap or make it generic...
// set = setter_list ... JSLINT
setValue = function (setter) {
var n, len, set_value, set_flux, set, val, set_reference;
var n, len, set_value, set_flux, set, val, set_reference, logic,
selection, split;
if (setter.logic) {
set_reference = setter.logic.setReference;
set_value = setter.logic.setValue;
val = setter.logic.lookupValue;
set = setter.logic.setters;
set_flux = setter.logic.setFlux;
logic = setter.logic;
if (logic) {
set_reference = logic.setReference;
set_value = logic.setValue;
val = logic.lookupValue;
set = logic.setters;
set_flux = logic.setFlux;
if (set) {
len = set.length;
for (n = 0; n < len; n += 1) {
if (val) {
// FU§$%&/NG HAL
setter.logic[set[n]] = item.doc[val[n][0]][val[n][1]][val[n][2]];
// FU§$%&/NG HAL
logic[set[n]] = item.doc[val[n][0]][val[n][1]][val[n][2]];
} else {
setter.logic[set[n]] = item.doc[set_value];
logic[set[n]] = item.doc[set_value];
}
}
} else if (set_value) {
......@@ -3522,7 +3670,7 @@
delete flux._temp[set_flux];
} else {
// translation!?
setter.direct.value = setter.logic.noFlux;
setter.direct.value = logic.noFlux;
}
}
}
......@@ -3534,15 +3682,24 @@
if (!!item.direct) {
setValue(item);
} else if (!!item.generate) {
// we must declare wrap for non-form elements here, because they
// NOTE: we must declare wrap for non-form elements here, because they
// don't belong into any widget
// NOTE: persist if set!
container = getHelp({
"wrap": "section",
"persist": item.property_dict.persist,
"class_list": "span_" + (item.property_dict.wrap || 1)
});
container.children.push(item);
} else {
helper = getHelp({"wrap": "fragment"});
// NOTE: since we are creating 2 levels in the DOM tree (fragment &
// sections), the normal inheritance will break, because a wrapper
// will be missing state. Therefore we add the state manually here,
// TODO: don't do 2 level stuff.... and complain later when debugging
helper = getHelp({
"wrap": "fragment",
"state": spec.wrapper.property_dict.state
});
}
// nothing to do, pass back directly
......@@ -3580,6 +3737,7 @@
field.overrides || {},
{"properties": {}, "widget": {}}
);
value = item.doc[field.setValue || field.field];
// flag false/missing field_dict
......@@ -3727,8 +3885,8 @@
pass = {
"push": {
"type": el,
"wrap": true,
"label": true,
"wrap": veto.wrap || true,
"label": veto.label || true,
"direct": {
"id": item.reference + "_" +
(veto.widget.id || root.widget.id),
......@@ -3768,7 +3926,7 @@
}
// need to set position here to also include custom fields
pass.position = segment.position === "center" ? 2 : 1;
pass.position = segment.position === "center" ? 1 : 2;
// fetch dynamic options
if (fetch_items === null) {
......@@ -3857,6 +4015,8 @@
.fail(app.util.error);
}
// NOTE: must set property_dict on the controlbar in order to inherit
// dynamic data to the contained field elements
section_list[j] = RSVP.all(field_list)
.then(function (field_response_list) {
section = getHelp({
......@@ -3888,49 +4048,86 @@
// generate storage object with placeholders
storage = {
"obj": {},
"empty_obj": {},
"arr": [],
"span": document.createElement("span")
};
/**
* Get a subordinate record and store in flux in case multiple properties
* of this object are requested
* @method subordinate
* @param {object} spec Current record
* @return {promise}
*/
storage.subordinate = function (spec) {
var output;
// fetch from cache or query
if (flux.active_record[spec.relation]) {
output = flux.active_record[spec.relation];
} else {
output = flux.active_record[spec.relation] = storage.fetch({
"pass": spec,
"query": {"_id": spec.relation}
});
}
return output.then(function (answer) {
var data = answer.response.data;
// set subordinate value
if (data.total_rows > 0) {
spec.value = data.rows[0].doc[spec.subordinate]
}
// NOTE: delete here, otherwise impossible to track. This will still
// work cause subsequent requests will fire async and be set from flux
delete flux.active_record[spec.relation];
return spec;
}).fail(app.util.error);
};
/**
* Fetch data from storage = GET or ALLDOCS
* @method fetch
* @param {object} obj Action object
*/
// TODO: merge pass.x or pass.state.x
// TODO: set pointer directly inside data-action, remove pointer!
// TODO: state needs to have url_pointer set earlier, so
// there is no need to look it up in pass.config.property_dict...
// TODO: convert config/url_dict into state and save 50000 assignments
// TODO: set pointer directly inside data-action.... remove pointer!
// TODO: jump is bad, it is the lookup for the data to display. rename!
// TODO: the whole assignments are crap
// TODO: action_id > this should be triggered via form submit not links!!!
// TODO: does _id always include portal_type? if link, yes, else no.
storage.fetch = function (obj) {
var pass, query, method, mapper, config, answer, lookup, pointer, action_id,
quirk;
var pass, query, method, mapper, config, answer, lookup, pointer, empty,
action_id, quirk;
empty = storage.empty_obj;
pass = obj.pass;
quirk = obj.state || pass.config_dict;
lookup = obj.state || quirk.property_dict || storage.obj;
query = obj.query || lookup.query || storage.obj;
pointer = (lookup.url_pointer || storage.obj).jump;
// TODO: action > id, this should be triggered via form submit not link!
action_id = ((obj.element || storage.obj).href || "").split("/").pop();
quirk = obj.state || pass.config_dict || empty;
lookup = obj.state || quirk.property_dict || empty;
query = obj.query || lookup.query || empty;
pointer = (lookup.url_pointer || empty).jump;
action_id = ((obj.element || empty).href || "").split("/").pop();
if (action_id) {
pointer = undefined;
query = {"_id": window.decodeURIComponent(action_id)};
}
// F%&/() JSLINT...
if (!!pointer) {
// JSLINT my ...
// TODO: "hack" for forcing get into allDocs, don't overwrite criteria!
if (!!pointer) {
mapper = "values";
query.select_list = query.select_list || pass.data_dict.field_list;
query.limit = query.limit || pass.config_dict.initial_query.limit;
} else if ((query.select_list || storage.arr).length > 0 &&
!query.include_docs
) {
mapper = "values";
} else if (!!query._id) {
mapper = "single_item";
method = "get";
......@@ -3983,11 +4180,11 @@
// TODO: storage.add included prefetch of field_items and validation
// TODO: storage.add deleted identifier if create_new was set
storage.write = function (obj) {
var form, data, valid, prefix, config, method, action, pointer;
var form, data, valid, prefix, config, method, action, pointer, sample;
form = obj.form;
pointer = (obj.element || storage.span).getAttribute("data-action");
action = (obj.state.url_pointer || storage.obj)[pointer];
action = (obj.state.url_pointer || storage.empty_obj)[pointer];
config = {};
prefix = obj.id + "_";
valid = obj.sample_store || storage.validate(form);
......@@ -3999,9 +4196,9 @@
config._force_data = true;
}
if (obj.sample_data._id || form.identifier) {
if (data._id || form.identifier) {
// PUT > set id, method and view
data._id = obj.sample_data._id || form.identifier.value;
data._id = data._id || form.identifier.value;
method = "put";
config._view = obj.state.view;
}
......@@ -4377,6 +4574,7 @@
}
}
}
return answer || response;
};
......@@ -4487,8 +4685,9 @@
/**
* @object flux Maintain flux in memory.
**/
// TODO: secure?
flux = {};
flux = {
"active_record": {}
};
/* ====================================================================== */
/* APP */
......@@ -4523,7 +4722,6 @@
id = answer.id;
decode = /%[0-9a-f]{2}/i.test(id);
goto_page = decode ? id : window.encodeURIComponent(id);
$.mobile.changePage(obj.state.callback.replace("__id__", goto_page));
}
};
......@@ -4547,7 +4745,14 @@
// update gadget
app.content.set(
{},
{
"portal_type_source": config.state.type,
"portal_type_title": config.state.title,
"property_dict": util.mergeObject(
{"dynamic": true},
config.state.dyno_dict),
"scheme": config.state.scheme
},
{
"reference": config.id,
"href": config.state.href,
......@@ -4570,10 +4775,11 @@
*/
// TODO: overwrite direction on single state sorting
app.sort = function (config, direction, prev, next, single) {
var i, in_array, sort_by, delay;
var i, in_array, sort_by, delay, state;
// NOTE: if column title is not set, we might be sortling a list!
sort_by = config.element.getAttribute("data-column-title");
state = config.state;
if (sort_by) {
// sorting button update
......@@ -4592,8 +4798,8 @@
util.clearTimer();
// check if we are already sorting by this sort_by value
for (i = 0; i < config.state.query.sort_on.length; i += 1) {
if (config.state.query.sort_on[i][0] === sort_by) {
for (i = 0; i < state.query.sort_on.length; i += 1) {
if (state.query.sort_on[i][0] === sort_by) {
in_array = true;
break;
}
......@@ -4605,9 +4811,9 @@
case "desc_abc":
if (sort_by) {
if (in_array === undefined) {
config.state.query.sort_on.push([sort_by, "descending"]);
state.query.sort_on.push([sort_by, "descending"]);
} else {
config.state.query.sort_on.splice(i, 1)
state.query.sort_on.splice(i, 1)
.push([sort_by, "descending"]);
}
}
......@@ -4616,9 +4822,9 @@
case "asc_abc":
if (sort_by) {
if (in_array === undefined) {
config.state.query.sort_on.push([sort_by, "ascending"]);
state.query.sort_on.push([sort_by, "ascending"]);
} else {
config.state.query.sort_on.splice(i, 1)
state.query.sort_on.splice(i, 1)
.push([sort_by, "ascending"]);
}
}
......@@ -4626,7 +4832,7 @@
default:
// need to remove a column from sorting when set to undefined
if (sort_by && in_array) {
config.state.query.sort_on.splice(i, 1);
state.query.sort_on.splice(i, 1);
}
break;
}
......@@ -4637,9 +4843,9 @@
{},
{
"reference": config.id,
"href": config.state.href,
"fragment_list": config.state.fragment_list,
"layout_level": config.state.layout_level
"href": state.href,
"fragment_list": state.fragment_list,
"layout_level": state.layout_level
},
false
)
......@@ -4749,31 +4955,33 @@
* @param {string} value New limit when changing number of records
*/
app.paginate = function (config, type, value) {
var start, records, total;
var start, records, total, state, current_limit;
total = config.state.total;
state = config.state;
total = state.total;
current_limit = state.query.limit || state.initial_query.limit;
if (config.gadget) {
if (config.state) {
if (state && current_limit) {
switch (type) {
case "first":
start = 0;
records = config.state.query.limit[1];
records = current_limit[1];
break;
case "next":
start = config.state.query.limit[0] + config.state.query.limit[1];
records = config.state.query.limit[1];
start = current_limit[0] + current_limit[1];
records = current_limit[1];
break;
case "prev":
start = config.state.query.limit[0] - config.state.query.limit[1];
records = config.state.query.limit[1];
start = current_limit[0] - current_limit[1];
records = current_limit[1];
break;
case "last":
start = total - config.state.query.limit[1];
records = config.state.query.limit[1];
start = total - current_limit[1];
records = current_limit[1];
break;
case "limit":
start = config.state.query.limit[0];
start = current_limit[0];
records = parseInt(value, null);
break;
}
......@@ -4783,37 +4991,35 @@
}
// set new limits
config.state.query.limit = [start, records];
state.query.limit = [start, records];
// reset selected
if (config.state.selected) {
config.state.selected = [];
if (state.selected) {
state.selected = [];
}
// update gadget
app.content.set(
{
"portal_type_source": config.state.type,
"portal_type_title": config.state.title,
"property_dict": util.mergeObject({
"dynamic": true,
"map_children": "listItem"
},
config.state.dyno_dict),
"scheme": config.state.scheme
"portal_type_source": state.type,
"portal_type_title": state.title,
"property_dict": util.mergeObject(
{"dynamic": true},
state.dyno_dict),
"scheme": state.scheme
},
{
"reference": config.id,
"href": config.state.href,
"fragment_list": config.state.fragment_list,
"layout_level": config.state.layout_level
"href": state.href,
"fragment_list": state.fragment_list,
"layout_level": state.layout_level
},
false
)
.fail(app.util.error);
} else {
app.util.error("No state information stored for gadget");
app.util.error("No state/limit information stored for gadget");
}
} else {
app.util.error("Action is missing reference gadget");
......@@ -4841,11 +5047,12 @@
info = "";
no_total = total === undefined &&
app.storage_dict.property_dict.skip_total_records;
generateInfo = function (text, text_i18n) {
return factory.element(
"span",
{"className": "translate"},
{"data-i18n": "global_dict.info_dict." + text_i18n},
{"data-i18n": "global_dict." + text_i18n},
{"text": text}
);
};
......@@ -4937,7 +5144,7 @@
}
// initial fill or update
if (slot !== undefined) {
if (!slot) {
if (info_field.children.length === 0) {
first = document.createTextNode(info);
last = generateInfo(text_snippet, i18n_snippet);
......@@ -5029,6 +5236,10 @@
"gadget": document.getElementById(id)
};
if (response.gadget === null) {
return response;
}
has_form = response.gadget.getElementsByTagName("form");
return util.mergeObject(
{
......@@ -5069,16 +5280,27 @@
* @param {object} data Data passed along with this (JQM) event
*/
app.parsePage = function (e, data) {
var page, base, config, raw_url, handle, clean_url, parsed_url, first;
var page, base, config, raw_url, handle, clean_url, parsed_url, first,
skipper, base_page;
if (data) {
// update page title on backwards transition to the first page (still
// is in DOM, so no new page created, so no title update...)
// TODO: not bulletproof i18n handling... we expect i18n to be defined
// TODO: blocker is not nice
if (typeof data.toPage === "object") {
base_page = data.toPage[0];
if ($.mobile.firstPage[0].getAttribute("data-url") ===
data.toPage[0].getAttribute("data-url")) {
base_page.getAttribute("data-url")) {
app.setPageTitle("global_dict.home");
// NOTE: this will allow going back, but only if we are not
// coming from root
if (!app.deeplink_flag) {
skipper = true;
app.deeplink_flag = true;
}
}
}
......@@ -5089,25 +5311,35 @@
} else {
raw_url = data.toPage;
}
} else {
raw_url = window.location.href;
}
if (typeof raw_url === "string") {
// decode
if (data && data.options.reverse) {
raw_url = window.decodeURIComponent(raw_url);
}
config = app.util.parseLink(raw_url);
// NOTE: if we start from home we must prevent reloading home
// because it will be kept in the DOM
if (!config.deeplink) {
// we start from home, so we must not allow going back
// and reloading
app.deeplink_flag = true;
}
if (e) {
page = util.getPage(raw_url.split("#").pop());
base = page ? page.getAttribute("data-external-page") : null;
first = $.mobile.firstPage[0].getAttribute("data-url") ===
config.data_url;
if (first || (page && base) || raw_url === $.mobile.getDocumentUrl() ||
data.options.role === "popup") {
if ((first || (page && base) || raw_url === $.mobile.getDocumentUrl() ||
data.options.role === "popup") && !skipper) {
// stop us, JQM can go
return RSVP.resolve(undefined);
}
......@@ -5301,17 +5533,24 @@
// generateFormElement
$(form_element).validVal({
validate: {
onKeyup: "valid",
onBlur: "valid"
"validate": {
"onKeyup": "valid",
"onBlu": "valid"
},
"fields": {
"hidden": true
},
//customValidations: util.declareJS(),
form: {
onInvalid: function () {
"form": {
"onInvalid": function () {
util.return_out();
}
}
});
} else {
// TODO: AAAARGH! so much jquery...
// TODO: AND IT DOES NOT WORK... §$%&/()=!
$(form_element).trigger("addField", $(form_element).find(".required"));
}
}
};
......@@ -5575,13 +5814,14 @@
* Get the active JQM page in JavaScript-only
* @method getPage
* @param {string} url url of the page to fetch
* @param {string} straight If not set, we start searching from behind
* @return {string} id of active page
*/
util.getPage = function (url) {
var i, kid, kids = document.body.children;
util.getPage = function (url, straight) {
var i, kid, len, kids = document.body.children;
// reverse, because in JQM last page is the active page!
for (i = kids.length - 1; i >= 0; i -= 1) {
for (len = kids.length, i = len - 1; i >= 0; i -= 1) {
kid = kids[i];
if (util.testForString("ui-page", kid.className)) {
......@@ -5593,6 +5833,17 @@
return undefined;
};
/**
* Remove all children of an element
* @method deleteChildren
* @param {object} el Element to remove children from
*/
util.deleteChildren = function (el) {
while (el.hasChildNodes()) {
el.removeChild(el.lastChild);
}
};
/**
* Reverse an array
* @method reverseArray
......@@ -5647,6 +5898,22 @@
return new RegExp("(?:^|\\s)" + className + "(?!\\S)", "g");
};
// /**
// * Property to set whether classlist is supported
// * @property support_classList
// */
// // TODO: add to support module/Modernizr
// util.no_support_classList = document.documentElement.classList === undefined;
//
// util.testForString = util.no_support_classList ?
// function(el, clss) {
// return el.className && new RegExp("(^|\\s)" +
// clss + "(\\s|$)").test(el.className);
// } :
// function(el, clss) {
// return el.classList.contains(clss);
// };
/**
* Test for a class name
* @method testForString
......@@ -5655,7 +5922,6 @@
* @param {boolean} nowrap Whether to wrap in space
* @return {boolean} result True/False
*/
// WARNING: requires IE8- requires shim
util.testForString = function (search_string, full_string, nowrap) {
var s = nowrap ? "" : " ";
return (s + full_string + s).indexOf(s + search_string + s) > -1;
......@@ -5757,17 +6023,37 @@
* @return {object} field defintions and pass through
*/
app.content.fields = function (reply) {
var pass = reply.pass;
var pass = reply.pass, query, crop;
if (!pass.skip) {
// (dynamic element) config_dict
// NOTE: so config_dict will always be present and we don't need to
// ever test for it again after here.... because if we fetch by URL
// whatever was fetched is here and if we just render it will be
// on content_dict.
pass.config_dict = util.parse(reply.response) || pass.content_dict;
delete pass.no_config;
// test for auth based access
pass.grant = true;
// update initial query with urlQuery if one was passed
if (pass.url_dict.url_query) {
crop = pass.config_dict.property_dict.url_crop;
query = util.mergeObject(
pass.url_dict.url_query,
pass.config_dict.initial_query || {}
);
// TODO: TOTALLY UGLY hack to allow 2 gadgets who require different urls
// to share... remove ASAP
if (crop) {
query.query = query.query.replace(crop, "");
}
pass.config_dict.initial_query = query;
}
// fetch field definitions - why make a query without fieldlist?
if (pass.grant || pass.mode === "new") {
app.util.loader("", "status_dict.loading_config");
......@@ -5856,14 +6142,8 @@
return RSVP.all(promise_list)
.then(function (response_list) {
if (!response_list[0]) {
app.util.loader(
"",
"status_dict.internal_error",
"ban-circle"
);
} else {
app.util.loader("", "status_dict.success", "check");
if (!response_list[0] && promise_list.length !== 0) {
app.util.loader("", "status_dict.internal_error", "ban");
}
// just return pass and continue
return {
......@@ -5902,7 +6182,7 @@
if (pass.create === false) {
pass.state = document.getElementById(pass.url_dict.reference).state;
pass.store_limit = pass.state.query.limit;
pass.state.query.lfimit = [];
pass.state.query.limit = [];
// create state object
} else {
......@@ -5951,7 +6231,8 @@
pass.config_dict.skip_total_records !== true) &&
((pass.grant || pass.url_dict.mode === "new") &&
(pass.state && pass.state.query._id === undefined) &&
pass.config_dict.initial_query)
(pass.config_dict.initial_query || pass.create === false) &&
!pass.config_dict.property_dict.force_new)
) {
// reset limit from sampling [0,1] and store query
pass.state.query.limit = [];
......@@ -5978,7 +6259,6 @@
var pass = reply.pass;
if (!pass.skip) {
// set total rows
if (reply.response) {
pass.state.total = util.parse(reply.response).data.total_rows;
......@@ -6014,7 +6294,7 @@
}
// get items/item
if (pass.grant &&
if (pass.grant && !pass.config_dict.property_dict.force_new &&
(pass.url_dict.mode !== "new" || !pass.config_dict.initial_query)
) {
app.util.loader("", "status_dict.loading_set");
......@@ -6037,11 +6317,15 @@
* @return {object} response object/promise
**/
// TODO: refactor... appocalyptic
// TODO: determine what should be inherit and make inherit object, that
// will be passed down the rendering tree
app.content.make = function (reply) {
var pass, method, type, kids, promise_list, route,
search, search_id, kid, is_html, is_dynamic, is_id, i, j, last,
search, search_id, kid, is_html, is_dynamic, is_id, i, j, k, last,
encoded, wrapper, active, selector, update_target, target, widget,
grant_child, quirk_dict, generator, pointer_results, data_total_rows;
quirk_dict, generator, pointer_results, data_total_rows, is_parameter,
param_len, parameter, no_item, dyno, container, has_props, widget_dyno,
set_reference;
pass = reply.pass;
pass.config_dict = pass.config_dict || {};
......@@ -6049,44 +6333,25 @@
method = factory.widget[type] || undefined;
promise_list = [];
// set results, here, so dynamic kids are correct!
if (pass.grant && reply.response) {
pointer_results = util.parse(reply.response);
// NOTE:
// set results here, so kids.length is available in wrapper generation.
// Also, if dynamic and no response (eg for "new" record, we set an empty
// placeholder here which will generate an empty form
if (!pass.skip) {
pointer_results = util.parse(reply.response) ||
{"data": {"total_rows": 0, "rows": []}};
data_total_rows = pointer_results.data.total_rows;
}
// kids (must be declared before wrapper, because some wrapper need length
if (pass.content_dict.view_dict) {
kids = pass.content_dict.view_dict[pass.url_dict.mode || "default"];
} else {
kids = pass.content_dict.children || pass.config_dict.children || [];
}
// ============= TODO: improve property_dict generation ==============
// TODO: no all elements need them?
// wrapper object and properties
quirk_dict = util.mergeObject(
(pass[method ? "content_dict" : "config_dict"].property_dict),
{
"create": pass.create,
"update": pass.create === false ? true : null,
"total_rows": data_total_rows,
"length": kids.length,
"layout_level": pass.url_dict.layout_level,
"fragment_list": pass.url_dict.fragment_list,
"data_url": pass.url_dict.data_url,
"scheme": pass.config_dict.scheme,
"field_dict": (pass.data_dict || {}).field_dict,
"skip": pass.skip
}
);
// set generator
if (quirk_dict.skip) {
if (pass.skip) {
if (!pass.content_dict.generate) {
route = type;
// force custom elements into form layout...
// NOTE: only used in service_instance_status to make a link go
// through formElement, not sure this must be a viable option,
// maybe better with custom, question is were do wrapper, label
// come from.
if (pass.content_dict.logic && pass.content_dict.logic.route) {
route = pass.content_dict.logic.route;
}
......@@ -6114,6 +6379,31 @@
}
}
// kids (must be declared before wrapper, because some wrapper need length
if (pass.content_dict.view_dict) {
kids = pass.content_dict.view_dict[pass.url_dict.mode] ||
pass.content_dict.view_dict["default"];
} else {
kids = pass.content_dict.children || pass.config_dict.children || [];
}
// wrapper object and properties
quirk_dict = util.mergeObject(
(pass[method ? "content_dict" : "config_dict"].property_dict),
{
"create": pass.create,
"update": pass.create === false ? true : null,
"total_rows": data_total_rows,
"length": kids.length,
"layout_level": pass.url_dict.layout_level,
"fragment_list": pass.url_dict.fragment_list,
"data_url": pass.url_dict.data_url,
"scheme": pass.config_dict.scheme,
"field_dict": (pass.data_dict || {}).field_dict,
"skip": pass.skip
}
);
if (!generator) {
if (method) {
generator = method(quirk_dict);
......@@ -6124,48 +6414,84 @@
// switch back
wrapper = generator;
// make properties available to children in need
wrapper.property_dict = quirk_dict;
// =============================
// no records returned, no_show set below on dyno
// TODO: find better way to set dynamic flag
if (quirk_dict.no_show) {
kids.push(factory.util.noItems(
no_item = factory.util.noItems(
quirk_dict.no_show,
wrapper.base,
wrapper.count ? wrapper.count(quirk_dict.scheme[0]) : null
));
);
kids = util.inherit(
no_item,
{"scheme": quirk_dict.scheme, "field_dict": quirk_dict.field_dict}
);
}
// dynamic content
// TODO: 10 if-else...
if (!pass.skip) {
// =====================================================================
// NOTE: store state on wrapper property_dict to inherit!
wrapper.property_dict.state = pass.state
// =====================================================================
// inherit item id, so it's available in form as indentifier
if (pointer_results.data.total_rows === 1) {
quirk_dict.data = {"_id": pointer_results.data.rows[0].doc._id};
}
// no items, need no show! (default to empty {} if none provided
if (pointer_results.data.total_rows === 0) {
if (quirk_dict.allow_new) {
pointer_results = {"data": {"total_rows": 1, "rows": [{"doc": {}}]}};
if (quirk_dict.allow_new || quirk_dict.force_new) {
// NOTE: on updates with force_new, reference must be passed to not
// have undefined form-id values
if (quirk_dict.update) {
set_reference = {
"doc": {},
"reference": pass.url_dict.reference
};
}
pointer_results = {
"data": {
"total_rows": 1,
"rows": [set_reference || {"doc": {}}]
}
};
} else {
quirk_dict.no_show = quirk_dict.no_items || {};
}
}
// set up fragment and child_selector
if (quirk_dict.update !== true) {
if (!quirk_dict.no_content) {
// set reference for children in need
quirk_dict.reference = "dyno_" + util.uuid();
// setup fragment and child_selector
wrapper.fragment.appendChild(factory.element({
dyno = factory.element({
"type": "div",
"direct": {
"id": quirk_dict.reference,
"className": "dyno"
}
}));
});
// setup fragment and child_selector
if (quirk_dict.wrap_gadget) {
container = factory.element({
"type": "div",
"direct": {
"className": "span_" + quirk_dict.wrap_gadget
}
});
container.appendChild(dyno);
}
wrapper.fragment.appendChild(container || dyno);
wrapper.child_selector = wrapper.fragment.querySelector(
"#" + quirk_dict.reference
);
......@@ -6210,13 +6536,19 @@
}
break;
}
}
} else {
widget = pass.state.gadget.querySelector("[data-update]")
.getAttribute("data-widget");
// TODO: can this be done without storing on and querying DOM???!!!
// TODO: THIS MAKES ME INSANE... qsa || widget does not work!
widget = pass.state.gadget;
widget_dyno = widget.querySelector("[data-update]") || widget.getElementsByTagName("form")[0];
wrapper.property_dict.map_children = widget_dyno.getAttribute("data-map");
wrapper = util.mergeObject(
factory.widget[widget](wrapper.property_dict),
factory.widget[widget_dyno.getAttribute("data-widget")](wrapper.property_dict),
wrapper
);
// add scheme and field_dict to list
kids = util.inherit(
pointer_results.data.rows,
......@@ -6240,7 +6572,6 @@
is_dynamic = kid.property_dict && kid.property_dict.dynamic;
is_html = kid.direct;
// inherit item id to child elements (see above)
if (is_id) {
if (is_html && is_html.href) {
......@@ -6260,27 +6591,36 @@
kid.property_dict.input = "#" + search_id;
}
// inherit properties of parent (> dynamic!) and add data to kid
// map on kid directly and inherit data down (only single items)
if (quirk_dict.direct_map) {
kid.property_dict.data = pointer_results.data.rows[0];
// NOTE: we may be setting on a wrapper!
} else {
kid.property_dict = util.mergeObject(kid.property_dict, quirk_dict);
kid.children = util.inherit(
pointer_results.data.rows.concat(kid.children || []),
{"scheme": quirk_dict.scheme, "field_dict": quirk_dict.field_dict}
);
}
}
// HACK for setParam in login window
if (kid.logic && kid.logic.setParam) {
kid.direct.href += kid.direct.href.indexOf("?") > 0 ? "&" : "?";
switch (kid.logic.setParam[1]) {
case "location":
target = window.location.origin +
window.location.pathname +
window.location.hash.split("?")[0];
break;
// ===================================================================
is_parameter = (kid.logic || {}).setParam;
has_props = kid.property_dict;
// inherit dynamic state to all children with property_dict
if (has_props && wrapper.property_dict.state) {
kid.property_dict.state = wrapper.property_dict.state;
}
kid.direct.href += window.encodeURIComponent(kid.logic.setParam[0]) +
"=" + window.encodeURIComponent(target);
// set dynamic Param(s)
if (is_parameter) {
kid = app.util.setParam(kid, wrapper);
}
// ===================================================================
if (quirk_dict.update !== true || is_dynamic || quirk_dict.dynamic) {
// for content loaded via href, generate URL dict and undefine kid
......@@ -6288,6 +6628,7 @@
if (wrapper.is_page && kid.href) {
wrapper.inherit = {
"href": kid.href,
"url_query": pass.url_dict.url_query,
"fragment_list": pass.url_dict.fragment_list,
"layout_level": pass.url_dict.layout_level,
"inherit": true,
......@@ -6329,12 +6670,13 @@
return RSVP.all(promise_list)
.then(function (response_list) {
var k, l, done_target, wrapper_selector, response, content,
content_target;
var k, l, m, len, done_target, wrapper_selector, response, content,
content_target, last_child, detach, child_list;
for (k = 0, l = response_list.length; k < l; k += 1) {
response = response_list[k];
done_target = undefined;
if (response) {
// generate target for and append response
// NOTE: wrapper.spec is used for header only.
......@@ -6347,6 +6689,7 @@
"len": kids.length,
"config": wrapper.spec
});
// NOTE: wrapper_selector will return a fragment (set to
// first/last-ElementChild or a DOM node
done_target =
......@@ -6395,21 +6738,39 @@
}
// only for dynamic content
if (!pass.skip) {
// UPDATES
if (!pass.skip && !quirk_dict.no_content) {
// UPDATES - same %&/( as above
if (quirk_dict.update) {
selector = pass.state.gadget;
update_target = selector.querySelector("[data-update]");
// dump
while (update_target.hasChildNodes()) {
update_target.removeChild(update_target.lastChild);
update_target = widget.querySelector("[data-update]") || widget.getElementsByTagName("form")[0];
// TODO: generic method!
// TODO: or inherit reference to controlgroup without being child of form?
// rescue persistent elements from dump
detach = document.createDocumentFragment();
child_list = update_target.children;
for (m = 0, len = child_list.length; m < len; m += 1) {
last_child = child_list[m];
if (last_child.getAttribute("data-persist")) {
detach.appendChild(last_child.cloneNode(true));
}
}
//and add new dynamic content
//empty target, add new dynamic content and detached elements
util.deleteChildren(update_target);
update_target.appendChild(wrapper.fragment);
update_target.appendChild(detach);
// CREATE
} else {
selector = wrapper.fragment.firstElementChild || wrapper.fragment;
// TODO: find way to dig down into tree
if (util.testForString("dyno", selector.className) === false) {
selector = selector.firstChild;
}
pass.state.gadget = selector;
}
......@@ -6419,6 +6780,8 @@
}
// if a callback is provided, set it on state
// TODO: once containers work, submit_to should change the container
// vs the whole page?
if (pass.config_dict.property_dict.submit_to) {
pass.state.callback = pass.config_dict.property_dict.submit_to;
}
......@@ -6432,7 +6795,8 @@
app.setInfo(
pass.create ? wrapper.fragment : selector,
pass.state.query,
pass.state.total
pass.state.total,
pass.state.selected
);
}
}
......@@ -6479,6 +6843,167 @@
/* ********************************************************************** */
app.util = {};
/** Fetch a subordinate record to set values of an object
* @method setSubordinate
* @method setParam
* @param {object} kid Element configuration object
* @param {object} wrapper Wrapping document
* @returns {object} kid
*/
// TODO: change setParams into scheme like structure to use same handler?
app.util.setSubordinate = function (element, wrapper) {
var i, len, param_list, param, promise_list, field, pass, data;
param_list = element.logic.setParam;
promise_list = [];
data = wrapper.property_dict.data;
// NOTE: data may be undefined - no records because of missing link
// NOTE: throw?
if (data) {
for (i = 0, len = param_list.length; i < len; i += 1) {
param = param_list[i];
// NOTE: all regular params should be deleted by now!
field = param[1];
pass = {
"field": field,
"relation": data.doc[field],
"subordinate": field.split("subordinate_")[1]
};
promise_list[i] = storage.subordinate(pass);
}
}
return RSVP.all(promise_list)
.then(function (response_list) {
var j, k, res_len, response, param_len, out_param, val;
// TODO: CUSTOM CODE, wrap in callback and make generic handler
for (j = 0, res_len = response_list.length; j < res_len; j += 1) {
response = response_list[j];
for (k = 0, param_len = param_list.length; k < param_len; k += 1) {
out_param = param_list[k];
if (response.field === out_param[1]) {
val = out_param[0];
element.logic[val] = element.logic[val] || "";
element.logic[val] += response.value;
}
}
}
return element;
})
.fail(app.util.error);
};
/** Set parameters on an object or flag object for subordination =
* fetch related record to get value
* @method setParam
* @param {object} kid Element configuration object
* @param {object} wrapper Wrapping document
* @returns {object} kid
*/
// TODO: clean up, no exceptions (location), clear key + val!
// TODO: make lookup parsing robust!
app.util.setParam = function (kid, wrapper) {
var i, len, param_list, param, href, loc, splitter, key, val, lookup, new_val;
param_list = kid.logic.setParam;
for (i = 0, len = param_list.length; i < len; i += 1) {
param = param_list[i];
val = param[1];
key = param[0];
switch (val) {
// NOTE: hacked for oauth redirect url
// TODO: find way to include encoding when setting a href
case "location":
href = kid.direct.href;
splitter = href.indexOf("?") > 0 ? "&" : "?";
loc = window.location;
kid.direct.href += splitter + window.encodeURIComponent(key) +
"=" + window.encodeURIComponent(
loc.origin + loc.pathname + loc.hash.split("?")[0]
);
break;
// set values or flag for subordination inside map.element
default:
kid.logic[key] = kid.logic[key] || "";
if (val.split("subordinate_").length > 1) {
// NOTE: try to get value from state/URL before flagging
// NOTE: must check for equality, otherwise id foo > foofoo
// TODO: this is still super-not-robust... redo!
lookup = wrapper.property_dict.state.query.query;
if (lookup && lookup.indexOf(val) > -1) {
new_val = lookup.split(val)[1].split("+")[0]
.replace(":=", "").replace(")","");
if (kid.logic[key] !== new_val) {
kid.logic[key] += new_val;
}
} else {
kid.needs_subordination = true;
}
} else {
kid.logic[key] += wrapper.property_dict.data.doc[val];
delete kid.logic.setParam[i];
}
}
}
return kid;
};
/**
* Parse url query parameter into storage query object
* Expects the following URL format:
*
* #foo&
* query:id=bar+foo=baz&
* limit:start=0+items=5&
* sort:id=ascending+foo=descending&
* select:id+foo+baz+cous
*
* @method parseUrlQuery
* @param {string} str String to parse
* @returns {object} Query to run based on parameters
*/
app.util.parseQueryParameter = function (str) {
var i, param_list, param_len, param, initial_query, indicator,
value_list;
param_list = str.split("&");
initial_query = {};
for (i = 0, param_len = param_list.length; i < param_len; i += 1) {
param = param_list[i].split(":");
indicator = param[0];
value_list = param[1];
switch (indicator) {
case "query":
initial_query.query = param[1].replace("=", ":=", "g")
.replace("+", " AND ", "g");
break;
case "limit":
// NOTE: Thanks Tristan!
initial_query.limit = param[1].split("+")
.map(function (part) { return part.split("=")[1]; });
break;
case "sort":
initial_query.sort_on = param[1].split("+").map(function (part) {
var s = part.split("=");
return [s[0], s[1]];
});
break;
case "select":
initial_query.select_list = param[1].replace("+", ",", "g");
break;
}
}
return initial_query;
};
/**
* Parse a clicked link to determine which page to load
* @method parseLink
......@@ -6486,14 +7011,14 @@
* @return {object} navigation object
**/
app.util.parseLink = function (url) {
var i, hash, path, clean_hash, backup, decode, mode, root;
var i, hash, path, clean_hash, decode, root, last, stripped, url_query,
strip;
hash = $.mobile.path.parseUrl(
url.replace($.mobile.dialogHashKey, "")
).hash.replace("#", "");
// decode = /^[^\/]*%2[^\/]*$/.test(hash);
decode = /%[0-9a-f]{2}/i.test(hash);
backup = 0;
// decode (allowing URI encoded identifiers)
if (decode) {
......@@ -6502,7 +7027,15 @@
clean_hash = hash;
}
if (clean_hash === "") {
// query parameters
strip = clean_hash.split("&");
stripped = strip[0];
if (strip.length > 1) {
url_query = app.util.parseQueryParameter(strip[1]);
}
if (stripped === "") {
root = util.getPage().getAttribute("data-url");
return {
"data_url": root,
......@@ -6510,26 +7043,15 @@
};
}
// check for mode
path = clean_hash.split("/");
// TODO: this should be generic and without a backup....
// TODO: REFACTOR!!!
for (i = 0; i < path.length; i += 1) {
switch (path[i]) {
case "plan":
case "personal":
case "order":
backup = 1;
mode = path[i];
break;
}
}
path = stripped.split("/");
last = path.length -1;
return {
"mode": mode,
"mode": path[last],
"fragment_list": path,
"url_query": url_query,
"data_url": clean_hash,
"layout_level": path.length - 1 - backup,
"layout_level": last,
"deeplink": true,
"root": path[0]
};
......@@ -6543,7 +7065,9 @@
* @return {string} href
*/
app.util.generateLink = function (spec, id) {
var level, core, separator;
var level, core, separator, empty_string;
empty_string = "";
// external link
if (spec.source) {
......@@ -6551,15 +7075,15 @@
}
// link current segment parameter vs segment
if (spec.link_core.split("::").length > 1) {
separator = "";
if ((spec.link_core || empty_string).split("%26").length > 1) {
separator = empty_string;
} else {
separator = "/";
}
level = spec.layout_level || 0;
core = spec.link_core || (spec.fragment_list ?
spec.fragment_list.slice(0, level + 1).join("/") : "");
spec.fragment_list.slice(0, level + 1).join("/") : empty_string);
return ("#" + core + separator + window.encodeURIComponent(id));
};
......@@ -6678,11 +7202,6 @@
* @param {Object} spec The configuration for the module to load
* @param {Promise} The promise
*/
/**
* Generate storages if specified in storage recipe/definition
* @method setStorage
* @param {object} content_dict JSON configuration for storage
**/
app.init.config = function (content_dict) {
var i, j, arr, promise_list, len, feature, feature_len, name, dict,
set, type, nav, language, target, initializer;
......
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