var priv = {}; /* ====================================================================== */ /* ERP5 CONVERTER */ /* ====================================================================== */ // since we are making something generic! /** * map ERP5 field type to HTML elements * @method mapFieldType * @param {string} type ERP5 field type * @return {string} element */ priv.mapFieldType = function (type) { var element; switch (type) { case "StringField": case "RelationStringField": case "IntegerField": element = "input"; break; case "ListField": element = "select"; break; case "TextareaField": element = "textarea"; break; }; return element; }; /** * map ERP5 field type to input type * @method mapFieldType * @param {string} type ERP5 field type * @return {string} element */ priv.mapInputType = function (type) { var field_type = null; switch (type) { case "StringField": case "RelationStringField": field_type = "text"; break; case "IntegerField": field_type = "number"; break; } return field_type; }; /** * Build a string from array * @method: buildValue * @param: {string/array} value String/Array passed * @returns: {string} string */ priv.buildValue = function (value) { var i = 0, setter = "", property; if (typeof value === "string") { setter = value; } else if (typeof value === "object") { for (property in value) { if (value.hasOwnProperty(property)) { setter += (i === 0 ? "" : ", ") + value[property]; i += 1 } } } else { for (i; i < value.length; i += 1) { setter += (i === 0 ? "" : ", ") + value[i]; } } return setter || "could not generate value"; }; /** * Append values in form * @method: setValue * @param: {string} type Type of object * @param: {string} key Key to set * @param: {string/array} value Value to set key to */ priv.setValue = function (type, key, value) { var i, j, k, unit, element, getter, single_option, elements = document.getElementsByName(type + "_" + key), setter = priv.buildValue(value), // ...PFFFFFFFFFFFFFF standards require so much customization... dublin_core_date_time_fields = [ "date", "created", "modified", "effective_date", "expiration_date" ], time_fields = ["year", "month", "day"]; // can't be generic... yet for (i = 0; i < dublin_core_date_time_fields.length; i += 1) { if (key === dublin_core_date_time_fields[i]) { for (k = 0; k < time_fields.length; k += 1) { unit = time_fields[k]; element = document.getElementsByName( type + "_" + key + "_" + unit ); if (element.length > 0) { single_option = element[0].getElementsByTagName("option"); switch(unit) { case "year": getter = new Date(setter).getFullYear(); break; case "month": getter = new Date(setter).getMonth() + 1; break; case "day": getter = new Date(setter).getDate(); break; } if (single_option.length === 1) { single_option[0].setAttribute("value", getter); single_option[0].text = getter; single_option[0].parentNode.parentNode.getElementsByTagName("span")[0].innerHTML = getter; } else { element[0].value = getter; } } } } } for (j = 0; j < elements.length; j += 1) { elements[j].value = setter; } }; /** * Generate input form for an item * @method generateItem * @param {string} mode View Clone/Edit/Add * @param {string} item Element to show */ // NOTE: this should be in another gadget/file priv.generateItem = function (mode, item) { if (item) { // fetch data priv.erp5.get({"_id": item}, function (error, response) { var property, value, abort; if (response) { for (property in response) { if (response.hasOwnProperty(property)) { value = response[property]; priv.setValue(response.type.toLowerCase(), property, value); } } } else { abort = confirm("Error trying to retrieve data! Go back to overview?"); if (abort === true) { $.mobile.changePage("computers.html"); } } }); } }; /** * Create a serialized object from all values in the form * @method serializeObject * @param {object} form Form to serialize * @returns {string} JSON form values */ priv.serializeObject = function(form) { var o = {}; var a = form.serializeArray(); $.each(a, function() { if (o[this.name] !== undefined) { if (!o[this.name].push) { o[this.name] = [o[this.name]]; } o[this.name].push(this.value || ''); } else { o[this.name] = this.value || ''; } }); return o; }; // date conversion object container priv.dates = {}; /** * Create a serialized object from all values in the form * @method validateObject * @param {object} serialized object * @returns {object} object ready to pass to JIO */ // TODO: should be made generic by passing the type and a recipe for // which fields to format how priv.validateObject = function (object) { var validatedObject = {}, property, setter, value, i, j, clean_property, add_property, date_property, date_component, new_date, // NOTE: ... to time to be generic... convertToArray = ["contributor", "category"], seperator_character = ",", convertToDate = ["effective_date", "expiration_date"]; for (property in object) { add_property = true; if (object.hasOwnProperty(property)) { value = object[property]; clean_property = property.replace("computer_", ""); // multiple entries if (typeof value !== "string") { if(value.length > 0) { // this should only happen if a field is in the form multiple times! // NOTE: not nice setter = value[0]; } } else { setter = value; } // convert to array for (i = 0; i < convertToArray.length; i += 1) { if (convertToArray[i] === clean_property ) { setter = object[property].split(seperator_character); } } // set up date conversion for (j = 0; j < convertToDate.length; j += 1) { date_property = convertToDate[j]; if (clean_property.search(date_property) !== -1) { add_property = false; if (priv.dates[date_property] === undefined) { priv.dates[date_property] = {}; } // ... date_component = clean_property.split("_")[2]; priv.dates[date_property][date_component] = value; } } if (add_property) { validatedObject[clean_property] = setter; } } } // timestamp modified validatedObject.modified = new Date().toJSON(); // timestamp create and date if (validatedObject.date === undefined) { validatedObject.date = validatedObject.modified; } if (validatedObject.create === undefined) { validatedObject.create = validatedObject.modified; } // HACK: add missing type! if (validatedObject.type === undefined || validatedObject.type === "") { validatedObject.type = "Computer"; } // build dates for (date in priv.dates) { if (priv.dates.hasOwnProperty(date)) { new_date = priv.dates[date]; validatedObject[date] = new Date( new_date["year"], new_date["month"], new_date["day"] ).toJSON(); // delete this date delete priv.dates[date]; } } return validatedObject; } /** * Store object in EPR5 * @method modifyObject * @param {object} object Validated object * @param {method} string PUT or POST */ priv.modifyObject = function (object, method, callback) { priv.erp5[method](object, function (error, response) { if (error) { alert("oops..., an error occurred trying to store"); } else { alert("worked"); if (callback) { callback(); } } }); }; /** * Create array of URL parameters * @method splitSearchParams * @param {string} url URL to split * @returns {array} array of url parameters */ priv.splitSearchParams = function (url) { var path; if (url === undefined) { path = window.location; } else { path = url; } return $.mobile.path.parseUrl(path).search.slice(1).split("&"); } /** * Replace substrings to another strings * @method recursiveReplace * @param {string} string The string to do replacement * @param {array} list_of_replacement An array of couple * ["substring to select", "selected substring replaced by this string"]. * @return {string} The replaced string */ priv.recursiveReplace = function (string, list_of_replacement) { var i, split_string = string.split(list_of_replacement[0][0]); if (list_of_replacement[1]) { for (i = 0; i < split_string.length; i += 1) { split_string[i] = priv.recursiveReplace( split_string[i], list_of_replacement.slice(1) ); } } return split_string.join(list_of_replacement[0][1]); }; /** * Changes & to %26 * @method convertToUrlParameter * @param {string} parameter The parameter to convert * @return {string} The converted parameter */ priv.convertToUrlParameter = function (parameter) { return priv.recursiveReplace(parameter, [[" ", "%20"], ["&", "%26"]]); }; /** * Create a URL string for authentication (same as ERP5 storage) * @method createEncodedLogin */ priv.createEncodedLogin = function () { return "__ac_name=" + priv.convertToUrlParameter(priv.username) + "&" + (typeof priv.password === "string" ? "__ac_password=" + priv.convertToUrlParameter(priv.password) + "&" : ""); }; /** * Modify an ajax object to add default values * @method makeAjaxObject * @param {object} json The JSON object * @param {object} option The option object * @param {string} method The erp5 request method * @param {object} ajax_object The ajax object to override * @return {object} A new ajax object with default values */ priv.makeAjaxObject = function (key) { var ajax_object = {}; ajax_object.url = priv.url + key + "?" + priv.createEncodedLogin() + "disable_cookie_login__=1"; // exception: ajax_object.url = priv.username + ":" + priv.password + "@" + priv.url + key; ajax_object.dataType = "text/plain"; ajax_object.async = ajax_object.async === false ? false : true; ajax_object.crossdomain = ajax_object.crossdomain === false ? false : true; return ajax_object; }; /** * Runs all ajax requests for propertyLookups * @method getERP5property * @param {string} id The id of the object to query * @param {string} lookup The method to retrieve the property * @return {string} The property value */ // NOTE: need a different way because this triggers a ton of http requests! priv.getERP5property = function (id, lookup) { var key = id + "/" + lookup; // return $.ajax(priv.makeAjaxObject(key)); return {"error":"foo"} }; /* ********************************************************************** */ /* JQM Form Field */ /* ********************************************************************** */ /* Generate a form field (input, select, checkbox...) * @method generateFormField * @param {object} config Default parameters usable for this form field * @param {object} overrides Overrides for this instance * @param {object} data Object with data to fill * @param {boolean} nowrap Flag whether to wrap in a fieldcontain(er) * @param {boolean} nolabel Flag whether to hide the label * @return {object} HTML document fragment */ // TODO: MAKE THIS A ERP5 MAPPER! priv.generateFormField = function (config, overrides, data, nowrap, nolabel) { var wrap, container, element_wrap, validate, value = data || undefined, element = document.createDocumentFragment(); if (config !== undefined || (overrides.enabled || config.validator.enabled) !== true) { // validation string // TODO: check all for all elements = worst-case if (overrides.external || config.validator.external) { validate = overrides.external_validator || config.validator.external_validator; } if (overrides.maximum_length || config.validator.maximum_length) { validate += "|" + "max_length:"+(overrides.maximum_length || config.validator.maximum_length) + "&truncate" + (overrides.truncate || config.validator.truncate); } if (overrides.maximum_lines || config.validator.maximum_lines) { validate += "|" + "max_lines:" + (overrides.maximum_lines || config.validator.maximum_lines); } if (overrides.maximum_lenght_of_line || config.validator.maximum_lenght_of_line) { validate += "|" + "max_length_lines:" + (overrides.maximum_lenght_of_line || config.validator.maximum_lenght_of_line); } if (overrides.preserve_whitespace || config.validator.preserve_whitespace) { validate += "|preserve_whitespace:true"; } if (overrides.start || config.validator.start) { validate += "|start:" + (overrides.start || config.validator.start); } if (overrides.end || config.validator.end) { validate += "|end:" + (overrides.start || config.validator.start); } // wrap if (nowrap) { wrap = document.createDocumentFragment(); } else { wrap = priv.generateElement( "div", {"className":"ui-fieldcontain translate"}, {"title": (overrides.description || config.widget.description || "" ), "data-i18n": (overrides.description_i18n || config.widget.description_i18n || "")} ); } // label wrap.appendChild(priv.generateElement( "label", {"className": "translate" + (nolabel ? " ui-hidden-accesible" : "")}, {"for": overrides.id || config.widget.id, "data-i18n": overrides.title_i18n || config.widget.title_i18n}, {"text": overrides.title || config.widget.title} )); // relationStringField // TODO: crap if (overrides.type === "RelationStringField" || config.type === "RelationStringField") { element_wrap = priv.generateElement("div", {"className": "ui-input-text ui-body-inherit ui-corner-all ui-shadow-inset ui-input-has-clear ui-input-has-action"} ); element_wrap.appendChild(priv.generateElement("input", { "id": overrides.id || config.widget.id, "className": "" }, { "data-enhanced": "true", "data-inset":"false", "data-clear-btn":"true" } )); element_wrap.appendChild(priv.generateElement("a", { "href":"", "className": "ui-input-clear ui-input-clear-hidden ui-btn ui-icon-delete ui-btn-icon-notext ui-corner-all", "title": "Clear input element" }, { "data-role": "button", "data-i18n": "[title]generic.buttons.clear;generic.buttons.clear" }, { "text":"clear input element" } )); element_wrap.appendChild(priv.generateElement("a", { "href":"", "className": "ui-disabled ui-input-action ui-btn ui-icon-plane ui-btn-icon-notext ui-corner-all", "title": "jump to selected object" }, { "data-role": "button", "data-i18n": "[title]generic.buttons.jump;generic.buttons.jump" }, { "text": "jump to selected object" } )); wrap.appendChild(element_wrap); wrap.appendChild(priv.generateElement("ul", { "className":"" },{ "data-role":"listview", "data-inset":"true", "data-enhanced":"true", "data-filter":"true", "data-filter-reveal":"true", "data-input": "#relation-" + portal_type } )); } else { // element wrap.appendChild(priv.generateElement( priv.mapFieldType(overrides.type || config.type), { "className": (overrides.css_class || config.widget.css_class || "") + " " + ((overrides.required || config.validator.required) ? "required" : ""), "id": overrides.id || config.widget.id }, {}, { "data-relation": overrides.portal_type || config.widget.portal_type || null, "size": overrides.display_width || config.widget.display_width || overrides.size || config.widget.size || undefined, "rows": overrides.width || config.widget.width || undefined, "cols": overrides.height || config.widget.height || undefined, "disabled": (overrides.enabled || config.validator.enabled) ? undefined : true, "name": overrides.alternate_name || config.validator.alternate_name || undefined, "value": (value || (overrides.default === 0 ? "0" : overrides.default) || config.widget.default || undefined), "data-vv-validations": validate || null, "type": (overrides.hidden || config.widget.hidden) === true ? "hidden" : priv.mapInputType(overrides.type || config.type), "extra": overrides.extra || config.widget.extra || null, "readonly": (overrides.editable === false || config.validator.editable === false ) ? true : undefined, "options": (overrides.items || config.widget.items) ? ([overrides.items || config.widget.items || null, overrides.extra_per_item || config.widget.extra_per_item || null, overrides.select_first_item || config.widget.select_first_item || null]) : null } )); } element.appendChild(wrap); } return element; }; /* ====================================================================== */ /* CONSTRUCTORS */ /* ====================================================================== */ // Constructors generate gadgets utilizing generators! /* ********************************************************************** */ /* FIELD LIST */ /* ********************************************************************** */ /** Construct a simple field menu * @method constructFieldList * @param {object} element Base element to enhance * @returns {object } HTML fragment */ priv.constructFieldlist = function (element) { var i, k, j, field, config, overrides, content, box, section, fragment, gadget_id = element.getAttribute("data-gadget-id"), settings = priv.gadget_properties[gadget_id], portal_type = settings.portal_type_title, $parent = $(element.parentNode); if (settings !== undefined) { if (settings.form !== undefined) { fragment = priv.generateElement( "form", settings.form.direct, (settings.form.attributes || undefined), (settings.form.logic || undefined) ); } else { fragment = window.document.createDocumentFragment(); } for (i = 0; i < settings.layout.length; i += 1) { section = settings.layout[i]; if (section.blocks) { for (j = 0; j < section.blocks.length; j += 1) { box = section.blocks[j]; content = priv.generateElement( "div", {"className": "content_element " + (box.fullscreen ? "content_element_fullscreen" : "")} ); // fields if (box.fields) { for (k = 0; k < box.fields.length; k += 1) { field = box.fields[k]; config = priv.field_definitions[portal_type][field]; overrides = box.overrides[field] || {}; // all-in-one... content.appendChild(priv.generateFormField(config, overrides)); } fragment.appendChild(content); } // actions if (box.actions) { for (l = 0; l < box.actions.length; l += 1) { content.appendChild(priv.generateControlgroup(box.actions[l])); } } } } } } $parent.empty().append( fragment ).enhanceWithin(); }; /* ********************************************************************** */ /* TAB FIELD LIST */ /* ********************************************************************** */ /** * Create tab menu * @method constructTabs * @param {object} element Base table to enhance * @param {string} mode View to display * @param {string} item Id of item to display * @param {object} properties Properties of item to display * @returns {object} html object / deferred */ priv.constructTabs = function (element, mode, item, properties) { var property, value, abort, i, j, k, set, collapsible, tab, tag, box, content, wrap, field, overrides, config, exist, validate, element_wrap, fragment = window.document.createDocumentFragment(), // tab config gadget_id = element.getAttribute("data-gadget-id"), settings = priv.gadget_properties[gadget_id], portal_type = settings.portal_type_title, // wrapper - could be a slot, too $parent = $(element.parentNode); // TODO: get settings here if not loaded if (settings !== undefined) { // tab container set = priv.generateElement( "div", {"className": "ui-dynamic-tabs"}, {"data-role": "collapsible-set", "data-tabs": settings.layout.length || 1, "data-type": "tabs"} ); // tabs for (i = 0; i < settings.layout.length; i += 1) { tab = settings.layout[i]; collapsible = priv.generateElement( "div", {}, {"data-role":"collapsible", "data-icon":"false", "data-collapsed": i === 0 ? false : true} ); collapsible.appendChild(priv.generateElement( "h1", {"className":"translate"}, {"data-i18n": tab.i18n}, {"text": tab.title} )); if (tab.blocks !== undefined) { for (j = 0; j < tab.blocks.length; j += 1) { box = tab.blocks[j]; // content element content = priv.generateElement( "div", {"className": box.fullscreen ? " content_element_fullscreen content_element" : "content_element"} ); // fields if (box.fields) { for (k = 0; k < box.fields.length; k += 1) { field = box.fields[k]; config = priv.field_definitions[portal_type][field]; overrides = box.overrides[field] || {}; // all-in-one... content.appendChild(priv.generateFormField(config, overrides)); } fragment.appendChild(content); } // actions if (box.actions) { for (l = 0; l < box.actions.length; l += 1) { content.appendChild(priv.generateControlgroup(box.actions[l])); } } // nested gadgets ASYNC if (box.view) { // $.when(priv[box.view.renderWith](content, box.view.gadget_id)).then(function(fragment) { // $(fragment.target).append(fragment.element).enhanceWithin(); // }); } collapsible.appendChild(content); } } set.appendChild(collapsible); } // add dynamic tabs if (settings.configuration.editable) { collapsible = priv.generateElement("div", {"className": "dashed add_tab"}, {"data-role":"collapsible", "data-icon": "plus", "data-expanded-icon":"plus"}, {} ); collapsible.appendChild(priv.generateElement("h1", {"className":"translate"}, {"data-i18n": "generic.layouts.tabs.add"}, {"text":"Add tab"} ) ); set.appendChild(collapsible); } fragment.appendChild(set); if (settings.configuration.editable) { fragment.appendChild( priv.generateElement("p", {"className":"center ui-dynamic-info translate"}, {"data-i18n": "generic.messages.tabs.empty"}, {"text":"Your dashboard is empty. Click above to add tabs and gadgets displaying key information to your dashboard."} ) ); } // la viola - we touch the DOM once!! $parent.empty().append( fragment ).enhanceWithin(); } else { element.appendChild( priv.generateElement("p", {"className":"center translate"}, {"data-i18n": "generic.errors.no_settings"}, {"text": "Error: No configuration available for this type of data!"} ) ); } }; /* ********************************************************************** */ /* action menu */ /* ********************************************************************** */ /* * Creates a controlgroup (just a passthrough method) * @constructActionMenu * @param {object} element Wrapper element * @returns {object} document fragment */ priv.constructActionMenu = function (element) { var gadget_id = element.getAttribute("data-gadget-id"), settings = priv.gadget_properties[gadget_id], portal_type = settings.portal_type_title, $parent = $(element.parentNode); fragment = priv.generateControlgroup(settings.layout); $(element.parentNode).empty().append( fragment ).enhanceWithin(); }; /* ====================================================================== */ /* SETUP */ /* ====================================================================== */ // NOTE: custom, content generation methods = non generic stuff needed to // display custom content. Above this section = generic, below = custom /* ********************************************************************** */ /* table search/config methods */ /* ********************************************************************** */ /** * generates a popup contents * @method generatePopupContents * @param {object} e Event that triggered opening a popup * @param {string} type Pointer to object holding popup config info * @param {string} portal_type To be loaded and referenced * @param {object} view gadget Configuration properties (pass through) * @oaram {string} gadget_id */ priv.generatePopupContents = function (e, type, portal_type, view, gadget_id) { var property, i, field, popup_body, popup_content, popup_message, popup_title, popup_info, popup_method, element, reference, popup = document.getElementById(e.target.href.split("#")[1]), state = popup.getAttribute("data-state"); switch(type) { case "self": reference = e.target.getAttribute("data-reference"); // portal_type should be defined break; case "action": element = e.target.parentNode.getElementsByTagName("input")[0]; reference = element.getAttribute("data-reference"); portal_type = element.getAttribute("data-relation"); break; }; // only regenerate the popup if switching content if (state !== reference) { popup_body = document.createDocumentFragment(); switch (reference) { // NOTE: table wrapper configure menu // TODO: a global dump for reusable button config would be nice! case "configure": popup_title = "Configuration"; popup_info = "Please select columns to display and priority of display on smaller screens (6-1). Drag columns to modify the order of display" popup_methods = ["generatePortalTypeFieldSelector", "generateDefaultColumnConfig"]; popup_footer = { "type": "controlgroup", "direction": "horizontal", "class": "ui-grid ui-table-wrapper-bottom ui-table-wrapper-inset ui-corner-all", "controls_class": "ui-grid-3", "buttons": [ { "type": "a", "direct": {"className": "ui-grid-button ui-link ui-btn ui-icon-remove ui-btn-icon-right ui-shadow ui-corner-all ui-first-child"}, "attributes": {"data-i18n": "", "data-enhanced":"true", "data-role": "button", "data-iconpos": "right", "data-icon":"remove", "data-rel": "back"}, "logic": {"text":"Cancel"} }, { "type": "a", "direct": {"className": "ui-grid-button table_action ui-link ui-btn ui-icon-refresh ui-btn-icon-right ui-shadow ui-corner-all"}, "attributes": {"data-i18n": "", "data-enhanced":"true", "data-role": "button", "data-iconpos": "right", "data-icon":"refresh", "data-action": "rest"}, "logic": {"text":"Reset"} }, { "type": "a", "direct": {"className": "ui-grid-button table_action ui-btn-active ui-link ui-btn ui-icon-save ui-btn-icon-right ui-shadow ui-corner-all ui-last-child"}, "attributes": {"data-i18n": "generic.buttons.save", "data-role": "button", "data-iconpos": "right", "data-icon":"save", "data-action": "save"}, "logic": {"text":"Save"} } ] }; popup_action = "add_criteria_config"; popup_hint = "Add Columns"; break; // NOTE: table wrapper detail search case "details": popup_title = "Detail Search"; popup_info = "Create a multi field search by adding fields below. For faster lookups, you can save your search criteria if needed." popup_methods = ["generatePortalTypeFieldSelector"]; popup_footer = { "type": "controlgroup", "direction": "horizontal", "class": "ui-grid ui-table-wrapper-bottom ui-table-wrapper-inset ui-corner-all", "controls_class": "ui-grid-2", "buttons": [ { "type": "a", "direct": {"className": "ui-grid-button ui-link ui-btn ui-icon-remove ui-btn-icon-right ui-shadow ui-corner-all ui-first-child"}, "attributes": {"data-i18n": "", "data-enhanced":"true", "data-role": "button", "data-iconpos": "right", "data-icon":"remove", "data-rel": "back"}, "logic": {"text":"Cancel"} }, { "type": "a", "direct": {"className": "ui-grid-button table_action ui-btn-active ui-link ui-btn ui-icon-search ui-btn-icon-right ui-shadow ui-corner-all ui-last-child"}, "attributes": {"data-i18n": "", "data-enhanced":"true", "data-role": "button", "data-iconpos": "right", "data-icon":"search", "data-action": "search"}, "logic": {"text":"Search"} } ] }; popup_action = "add_criteria_search"; popup_hint = "Select Search Criteria"; break; } // header if (popup_title) { popup_body.appendChild(priv.generateHeader({"title":popup_title})); } // content popup_content = priv.generateElement("div", {"className":"ui-content"},{"data-role":"content"},{}); if (popup_info) { popup_content.appendChild(priv.generateElement("p", {},{},{"text": popup_info || null})); } // data for (i = 0; i < popup_methods.length; i += 1) { popup_content.appendChild( priv[popup_methods[i]]( view, gadget_id, popup_action, popup_hint ) ); } popup_body.appendChild( popup_content); // footer if (popup_footer) { popup_body.appendChild( priv.generateFooter(popup_footer)); } popup.setAttribute("data-state", reference); popup.setAttribute("data-type", portal_type); // empty popup popup.innerHTML = ""; // add content directly $(popup).append( popup_body ).enhanceWithin(); } }; /** * generates a detail search field for the portal_type * @method generatePortalTypeFieldSelector * @param {object} table The table configuration * @return {object} element The element fragment */ priv.generatePortalTypeFieldSelector = function (table, gadget_id, action, hint) { var controls, property, config, unique_field_name, all_options = []; for (property in table) { if (table.hasOwnProperty(property)) { field = table[property]; unique_field_name = "search_" + gadget_id + "_" + property; all_options.push({ "value":gadget_id + ":" + property, "text": priv.capFirstLetter(property) }); } } controls = priv.generateElement("div", {"className":"ui-controlgoup ui-controlgroup-horizontal ui-popup-menu"}, {"data-role":"controlgroup", "data-type":"horizontal"}, {} ); controls.appendChild(priv.generateElement("label", {"className": "ui-hidden-accessible"},{"for": action + "_" + gadget_id}, {"text": hint} )); controls.appendChild(priv.generateElement("select", {"name": action + "_" + gadget_id, "id": action + "_" + gadget_id }, {}, {"options": all_options} )); controls.appendChild(priv.generateElement("a", {"className": "responsive ui-btn-active table_action"}, {"data-role":"button", "data-iconpos":"right", "data-icon":"plus", "data-action":action}, {"text":"Add"} )); return controls; }; /** * generates a grid configuration entry * @method generateConfigCriteria * @param {object} element clicked element */ priv.generateConfigCriteria = function (element, id, prop) { var field, property, grid, cell, i, unique_field_name, prev, val, portal_type, property; if (typeof element === "object") { prev = element.previousSibling.getElementsByTagName("select")[0], val = prev.options[prev.selectedIndex].value, gadget_id = val.split(":")[0], property = val.split(":")[1]; } else { val = "skip"; gadget_id = id; property = prop; } if (val === "" || val === undefined) { alert("Please select a valid column!"); } else { // look up field in gadget_id, generate grid and add it to form field = priv.gadget_properties[gadget_id].view[property]; if (field) { unique_field_name = "configure_" + gadget_id + "_" + property; grid = priv.generateElement("div", {"className":"ui-grid-d ui-grid-row"}, {}, {"grid":5} ); for (i = 0; i < grid.childNodes.length; i += 1) { cell = grid.childNodes[i]; switch(i) { case 0: cell.className = "ui-block-a ui-grid-title"; cell.appendChild(priv.generateElement("span", {}, {}, {"text": priv.capFirstLetter(property)} )); break; case 1: cell.className = "ui-block-b"; cell.appendChild(priv.generateElement("label", {"className": "ui-hidden-accessible"}, {"for": unique_field_name + "_priority"}, {"text": "Set priority"} )); cell.appendChild(priv.generateElement("input", { "name": unique_field_name + "_priority", "id": unique_field_name + "_priority", "min":1, "max":6, "type":"number", "value": field.priority || "" }, {"data-mini": "true"}, {"disabled": field.priority === undefined ? "disabled" : null} )); break; case 2: cell.className = "ui-block-c"; cell.appendChild(priv.generateElement("label", {"className": "ui-hidden-accessible"}, {"for": unique_field_name + "_toggle"}, {"text": "Set visibility"} )); cell.appendChild(priv.generateElement("select", {"name": unique_field_name + "_toggle", "id": unique_field_name + "_toggle"}, {"data-mini": "true", "data-role":"slider"}, { "disabled": field.priority === undefined ? "disabled" : null, "options": [ {"text": "Show", "value": "on", "selected": field.show ? "selected": null}, {"text": "Hide", "value": "off", "selected": field.show ? "selected": null} ] } )); break; case 3: cell.className = "ui-block-d"; cell.appendChild(priv.generateElement( "label", {"className": "ui-hidden-accessible"}, {"for": unique_field_name + "_sort"}, {"text": "Set sorting"}) ); cell.appendChild(priv.generateElement("select", {"name": unique_field_name + "_sort", "id": unique_field_name + "_sort"}, {"data-mini": "true", "data-role":"slider"}, { "disabled": field.priority === undefined ? "disabled" : null, "options": [ {"text": "Sort", "value": "on", "selected": field.show ? "selected": null}, {"text": "Static", "value": "off", "selected": field.show ? "selected": null} ] } )); break; case 4: cell.className = "ui-block-e ui-grid-action"; cell.appendChild(priv.generateElement("a", {"className":"table_action"}, {"data-role":"button", "data-icon":"delete", "data-iconpos":"notext", "data-action":"delete_criteria"}, {"text":"Delete"} )); break; }; } return grid; } else { alert("Field name does not exist in field defition list!"); } } }; /** * generates a grid search criteria entry * @method generateSearchCriteria * @param {object} element clicked element */ priv.generateSearchCriteria = function (element) { var grid, cell, opts = [], i, j, field, new_form, prev = element.previousSibling.getElementsByTagName("select")[0], val = prev.options[prev.selectedIndex].value, portal_type = val.split(":")[0], property = val.split(":")[1], form = $( element ).closest(".ui-popup").find("form"); // form should be create here instead to keep column selector generic if (form.length === 0) { new_form = document.createElement("form"); } if (val === "" || val === undefined) { alert("Please select a valid criteria!"); } else { // look up field in portal_type, generate grid and add it to form field = priv.gadget_properties[portal_type].view[property]; if (field) { grid = priv.generateElement("div", {"className":"ui-grid-d ui-grid-row"}, {}, {"grid":4} ); for (i = 0; i < grid.childNodes.length; i += 1) { cell = grid.childNodes[i]; switch(i) { case 0: cell.className = "ui-block-a ui-grid-title"; cell.appendChild(priv.generateElement("span", {}, {}, {"text": priv.capFirstLetter(property)} )); break; case 1: cell.className = "ui-block-b ui-grid-search"; if (field.search !== undefined) { cell.appendChild(priv.generateElement("label", {"className": "ui-hidden-accessible"}, {"for": "run_search_" + portal_type + "_" + property}, {"text": "Search Term"}) ); switch(field.search.type) { case "text": cell.appendChild(priv.generateElement("input", { "name": "run_search_" + portal_type + "_" + property, "id": "run_search_" + portal_type + "_" + property, "type": field.search.type }, {"data-clear-btn": "true"} )); break; case "select": for (j = 0; j < field.search.options.length; j += 1) { if (j === 0) { opts.push({"value":"","text":"", "selected":"selected"}) } opts.push({"value":field.search.options[j], "text":field.search.options[j]}) } cell.appendChild(priv.generateElement("select", {"name": "run_search_" + portal_type + "_" + property, "id": "run_search_" + portal_type + "_" + property,}, {}, {"options": opts} )); break; } } break; case 2: cell.className = "ui-block-c ui-grid-search"; if (field.search !== undefined) { cell.appendChild(priv.generateElement("label", {"className": "ui-hidden-accessible"}, {"for": "run_search_finetune_" + portal_type + "_" + property}, {"text": "Specify"}) ); switch(field.search.subsearch) { case "flip": for (i = 0; i < field.search.options.length; i += 1) { opts.push({"value":field.search.options[i], "text":field.search.options[i], "selected": i === 0 ? "selected" : null}) } cell.appendChild(priv.generateElement("select", {"name": "run_search_finetune_" + portal_type + "_" + property, "id": "run_search_finetune_" + portal_type + "_" + property}, {"data-mini":"true", "data-role":"slider"}, {"options": opts} )); break; case "checkbox": cell.lastChild.className = ""; cell.appendChild(priv.generateElement("input", { "name":"run_search_finetune_" + portal_type + "_" + property, "id":"run_search_finetune_" + portal_type + "_" + property, "type":"checkbox", "value": field.search.value }, {"data-mini":"true"}, {} )); break; }; } break; case 3: cell.className = "ui-block-e ui-grid-action"; cell.appendChild(priv.generateElement("a", {"className":"table_action"}, {"data-role":"button", "data-icon":"delete", "data-iconpos":"notext", "data-action":"delete_criteria"}, {"text":"Delete"} )); break; } } // first row if (form.length === 0) { new_form.appendChild(grid); return new_form; } // other rows return grid; } else { alert("Field name does not exist in field defition list!"); } } }; /** * generates a configuration form for the portal_type * @method generateDefaultColumnConfig * @param {object} table The table configuration * @return {object} element The element fragment */ priv.generateDefaultColumnConfig = function (table, gadget_id) { var element = priv.generateElement("form", {"className": "draggable"}, {"data-sortable":"true"}, {} ); for (property in table) { if (table.hasOwnProperty(property)) { field = table[property]; if (field.show) { element.appendChild(priv.generateConfigCriteria("none", gadget_id, property)); } } } return element; }; /* ********************************************************************** */ /* login popup */ /* ********************************************************************** */ // NOTE: This should be loaded as the HTML content of a login gadget // TODO: convert to JSON configuration! /** * Generates the content of the login popup * @method generateLoginPopup * @return {object} HTML fragment */ priv.generateLoginPopup = function () { var popup_element, external, // NOTE: in case we need a classic login (yet again) uncomment below //internal, //form, //note, p, img, content, info, hint = document.createDocumentFragment(), login_form = document.createDocumentFragment(), popup_content = document.createDocumentFragment(), img = priv.generateElement( "div", {"className": "popup_element logo_wrap"} ); img.appendChild(priv.generateElement( "img", {"src":"img/slapos.png", "alt": "slapos logo"} )); popup_content.appendChild(img); login_form.appendChild(priv.generateElement( "p",{},{},{"text":"Sign in using"} )); external = priv.generateElement( "div", {"className":"ui-controlgroup"}, {"data-role":"controlgroup"} ); //internal = external.cloneNode(); external.appendChild(priv.generateElement( "a", {"href":"#", "className":"signin_google ui-link ui-btn ui-icon-google-plus-sign ui-btn-icon-left ui-first-child"}, {"data-role":"button", "data-icon":"google-plus-sign", "data-iconpos":"left", "data-enhanced":"true"}, {"text":"Google"} )); external.appendChild(priv.generateElement( "a", {"href":"#", "className":"signin_fb ui-link ui-btn ui-icon-facebook-sign ui-btn-icon-left"}, {"data-role":"button", "data-icon":"facebook-sign", "data-iconpos":"left", "data-enhanced":"true"}, {"text":"Facebook"} )); external.appendChild(priv.generateElement( "a", {"href":"#", "className":"signin_browser ui-link ui-btn ui-icon-lock ui-btn-icon-left ui-last-child"}, {"data-role":"button", "data-icon":"lock", "data-iconpos":"left", "data-enhanced":"true"}, {"text":"Browser ID"} )); login_form.appendChild(external); // // classic login // login_form.appendChild(priv.generateElement( // "p", {},{},{"text":"Classic Login"} // )); // form = priv.generateElement("form"); // internal.appendChild(priv.generateElement( // "label", // {"className":"ui-hidden-accessible"}, // {"for":"login", "data-i18n":"generic.text.login"}, // {"text":"Login"} // )); // internal.appendChild(priv.generateElement( // "input", // {"name":"login", "id":"login", "type":"text"}, // { // "placeholder":"Login", // "data-icon":"user", // "data-i18n":"[placeholder]generic.text.login;generic.text.login" // } // )); // internal.appendChild(priv.generateElement( // "label", // {"className":"ui-hidden-accessible"}, // {"for":"password","data-i18n":"generic.text.password"}, // {"text":"Password"} // )); // internal.appendChild(priv.generateElement( // "input", // {"name":"password", "id":"password", "type":"password"}, // { // "placeholder":"Password", // "data-icon":"lock" // "data-i18n":"[placeholder]generic.text.password;generic.text.password" // } // )); // internal.appendChild(priv.generateElement( // "label", // {"className":"ui-hidden-accessible"}, // {"for":"submit","data-i18n":"generic.text.go"}, // {"text":"Go"} // )); // internal.appendChild(priv.generateElement( // "input", // { // "className":"submit_form", // "name":"submit", // "id":"submit", // "type":"button", // "value":"submit"}, // {"data-i18n":"[placeholder]generic.text.go;generic.text.go"} // )); // form.appendChild(internal); // login_form.appendChild(form) // note = priv.generateElement( // "span", {"className":"mini right note"} // ); // note.appendChild( // priv.generateElement( // "a", {"href":"#"},{},{"text":"Forgot Password"} // ) // ); // login_form.appendChild(note); content = priv.generateElement( "div", {"className": "popup_element"} ); content.appendChild(login_form); popup_content.appendChild(content); p = priv.generateElement("p", {"className":"mini"}); p.appendChild( priv.generateElement( "span", {"className":"note"},{},{"text":"Please note:"} ) ); p.appendChild( priv.generateElement( "span", { "innerHTML":"To maintain sufficient resources, a minimal fee of 1 " + "EUR will be charged if you use SlapOS services for <strong>more " + " than 24 hours</strong>. By clicking on one of the signup " + "buttons, you agree that you are subscribing to a payable service." + " All services you request will be invoiced to you at the end of" + " the month." } ) ); hint.appendChild(p); hint.appendChild(priv.generateElement( "p", {},{},{"text":"To find out more, please refer to"} )); hint.appendChild(priv.generateElement( "a", { "href":"#", "className":"ui-btn ui-btn-icon-left ui-icon-eur ui-shadow ui-corner-all" }, {"data-i18n":"generic.text.pricing"}, {"text":"SlapOS Pricing"} )); info = priv.generateElement( "div", {"className": "popup_element"} ); info.appendChild(hint); popup_content.appendChild(info); return popup_content; }; // /* ====================================================================== */ // /* TRASH BUT STILL HERE */ // /* ====================================================================== */ // // this invokes all gadgets on a page // // TODO: should be done differently, the data-gadget property is only used // // here to pick the correct function. Later on, the function should be // // called form inside the gadget // $(document).on("pagebeforeshow", "#computer", function (e, data) { // // NOTE: it should not be necessary to fetch this data from the URL // // because JQM should pass it in data, too // var mode, // item, // properties = {}, // parameters = decodeURIComponent( // $.mobile.path.parseUrl(window.location.href).search.split("?")[1] // ).split("&"); // // mode = parameters[0].split("=")[1]; // if (parameters.length > 1) { // item = parameters[1].split("=")[1]; // } // // $(".erp5_single").each(function (index, element) { // // load data // if (mode === "get" || mode === "clone") { // priv.erp5.get({"_id": item}, function (error, response) { // if (response) { // // set to properties, so response // properties = {}; // priv.constructTabs(element, mode, item, properties); // } else { // abort = confirm("Error trying to retrieve data! Go back to overview?"); // if (abort === true) { // $.mobile.changePage("computers.html", {"rel":"back"}); // } // } // }); // } else { // priv.constructTabs(element, mode, item, properties); // } // }); // // // we can set later // // priv.generateItem(mode, item); // }) // /* ====================================================================== */ // /* BINDINGS */ // /* ====================================================================== */ // // NOTE: should also not be done here, but in the respective gadget // // NOTE: still if a form contains 10 relationfields, we should not generate // // 10 popups and handlers, but only use a single popup shared across all // // load item from table // .on("click", "table tbody td a, .navbar li a.new_item", function (e) { // var i, // item, // spec = {}, // url = e.target.getAttribute("href").split("?"), // target = url[0], // parameters = url[1].split("&"); // // e.preventDefault(); // for (i = 0; i < parameters.length; i += 1) { // item = parameters[i].split("="); // spec[item[0]] = item[1]; // } // // $.mobile.changePage(target, { // "transition": "fade", // "data": spec // }); // }) // .on("click", "a.remove_item", function (e) { // var i, // params = priv.splitSearchParams(), // callback = function () { // $.mobile.changePage("computers.html", { // "transition":"fade", // "reverse": "true" // }); // }; // // // item in URL? // for (i = 0; i < params.length; i += 1) { // parameter = params[i].split("="); // if (parameter[0] === "item") { // priv.modifyObject({"_id": decodeURIComponent(parameter[1])}, "remove", callback ); // } // } // }) // // save form // .on("click", "a.save_object", function (e) { // var i, // parameter, // method, // object, // // check the URL for the state we are in // // NOTE: not nice, change later // params = priv.splitSearchParams(), // callback = function () { // $.mobile.changePage("computers.html", { // "transition":"fade", // "reverse":"true" // }); // }; // // for (i = 0; i < params.length; i += 1) { // parameter = params[i].split("="); // if (parameter[0] === "mode") { // switch (parameter[1]) { // case "edit": // method = "put"; // break; // case "clone": // case "add": // method = "post"; // break; // } // if (method !== undefined) { // object = priv.validateObject( // priv.serializeObject($(".display_object")) // ); // // fallback to eliminate _id on clone // // TODO: do somewhere else! // if (method === "post") { // delete object._id; // } // priv.modifyObject(object, method, callback); // } else { // alert("missing command!, cannot store"); // } // } // } // }) // // update navbar depending on item selected // .on("change", "table tbody th input[type=checkbox]", function(e) { // var allChecks = $(e.target).closest("tbody").find("th input[type=checkbox]:checked"), // selected = allChecks.length, // trigger = $(".navbar .new_item"); // // if (selected === 1) { // trigger.addClass("ui-btn-active clone_item").attr("href","computer.html?mode=clone&item=" + e.target.id); // } else { // trigger.removeClass("ui-btn-active clone_item").attr("href","computer.html?mode=add"); // } // // }); // form = document.getElementById(e.target.getAttribute("data-form")); // target = e.target.href; // // e.preventDefault(); // // if (util.testForClass(form.className), "validate") { // valid = $(form).triggerHandler( "submitForm" ); // } else { // valid = $(form).serialize(); // } // // // // TODO: how to update status? // // if (valid !== false) { // // jIO.util.ajax({ // // "url": "data/" + attachment + ".json", "dataType":"json" // // }) // // } // // // // target = e.target.href, // // id = form.id, // // pointer = "response:" + id, // // proceed = function (data, target) { // // // changePage with fragment_cache pointer // // $.mobile.changePage(target, {"transition": "slide", "data": data}); // // }; // // // // // stop // // // fetch and proceed // // if (valid !== false) { // // $.ajax({ // // "type":"POST", // // "url": "foo.php", // // "data": valid // // }).done(function(data) { // // // overwrite cache // // // NOTE: FAKE data until working // // priv.fragment_cache[id] = priv.getFakeRecords["payment"]; // // proceed(pointer, target); // // }).fail(function(jqXHR) { // // // fake it // // priv.fragment_cache[id] = priv.getFakeRecords["payment"]; // // proceed(pointer, target); // // }); // // } // // }); // // } // // }); // PAGE BINDINGS .end() // form validation initializer .find("form") // .filter(function () { // return this.getAttribute("data-bound") !== true; // }) // .each(function (i, element) { // element.setAttribute("data-bound", true); // // TODO: how to add custom validations here? // // TODO: async? // $(element).validVal({ // validate: { // onKeyup: "valid", // onBlur: "valid" // }, // form: { // onInvalid: function() { return;} // } // }); // }) // .end() // //TODO: can't set bindings, because we don't have a parent? // // or always set on document for local and global should be autoset? // // popup content handling // if (search_popup) { // $parent.on("click", "a.extended_search", function (e) { // priv.generatePopupContents(e, "self", portal_type, settings.view, gadget_id); // }); // } // // // table actions // if (search_popup) { // // NOTE: can't bind to $parent, because JQM moves the popup // // outside of $parent // page = $parent.closest("div.ui-page"); // // if (page.data("bindings") === undefined) { // page.data("bindings", {}); // } // if (page.data("bindings")["table_action"] === undefined) { // page.data("bindings")["table_action"] = true; // // $(document).on("click", "a.table_action", function (e) { // var target, // action = e.target.getAttribute("data-action"), // popup = $(e.target).closest(".ui-popup"), // form = popup.find("form"); // // if (form.length === 0) { // target = popup.find(".ui-content"); // } else { // target = form; // } // switch (action) { // case "add_criteria_search": // target.append(priv.generateSearchCriteria(e.target)) // .enhanceWithin(); // break; // // case "add_criteria_config": // target.append(priv.generateConfigCriteria(e.target)) // .enhanceWithin(); // break; // case "delete_criteria": // $(e.target).closest("div.ui-grid-row").remove(); // break; // } // }); // } // } // // });