Commit ba8692e2 authored by Ivan Tyagov's avatar Ivan Tyagov

Initial commit of RenderJS (javascript library to build UI based on gadgets).

parent 46c6c1e4
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Folder" module="OFS.Folder"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>renderjs</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>http_cache</string> </value>
</item>
<item>
<key> <string>_EtagSupport__etag</string> </key>
<value> <string>ts33630760.88</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>renderjs.js</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>application/javascript</string> </value>
</item>
<item>
<key> <string>data</string> </key>
<value> <string encoding="cdata"><![CDATA[
DEFAULT_GADGET_DOM_READY_TIMEOUT = 1000;\n
\n
/*\n
* Generic cache implementation that can fall back to local namespace storage\n
* if no "modern" storage like localStorage is available\n
*/\n
\n
var NameSpaceStorageCachePlugin = {\n
/*\n
* This plugin saves within current page namespace.\n
*/\n
\n
_namespace: {},\n
\n
get: function(cache_id, default_value){\n
/* Get cache key value */\n
//console.log(\'get namespace\');\n
return NameSpaceStorageCachePlugin._namespace[cache_id];\n
},\n
\n
set: function(cache_id, data){\n
/* Set cache key value */\n
//console.log(\'get namespace\');\n
NameSpaceStorageCachePlugin._namespace[cache_id] = data;\n
}\n
\n
}\n
\n
\n
var LocalStorageCachePlugin = {\n
/*\n
* This plugin saves using HTML5 localStorage.\n
*/\n
\n
get: function(cache_id, default_value){\n
/* Get cache key value */\n
//console.log(\'get local\');\n
return $.jStorage.get(cache_id, default_value);\n
},\n
\n
set: function(cache_id, data){\n
/* Set cache key value */\n
//console.log(\'set local\');\n
$.jStorage.set(cache_id, data)\n
}\n
\n
}\n
\n
var Cache = {\n
\n
hasLocalStorage: function() {\n
/*\n
* Feature test if localStorage is supported\n
*/\n
mod = \'localstorage_test_12345678\';\n
try {\n
localStorage.setItem(mod, mod);\n
localStorage.removeItem(mod);\n
return true;}\n
catch(e) {\n
return false;}\n
},\n
\n
get: function(cache_id, default_value){\n
/* Get cache key value */\n
if (Cache.hasLocalStorage()){\n
return LocalStorageCachePlugin.get(cache_id, default_value);}\n
else{\n
return NameSpaceStorageCachePlugin.get(cache_id, default_value);}\n
},\n
\n
set: function(cache_id, data){\n
/* Set cache key value */\n
if (Cache.hasLocalStorage()){\n
LocalStorageCachePlugin.set(cache_id, data);}\n
else{\n
NameSpaceStorageCachePlugin.set(cache_id, data);}\n
}\n
\n
}\n
\n
/*\n
* Generic tabular gadget\n
*/\n
var TabbularGadget = {\n
\n
toggleVisibility: function(visible_dom){\n
/*\n
* Set tab as active visually and mark as not active rest.\n
*/\n
$(".selected").addClass("not_selected"); $(".selected").removeClass("selected");\n
visible_dom.addClass("selected");\n
visible_dom.removeClass("not_selected");\n
},\n
\n
addNewTabGadget: function(form_id, dom_id, gadget_data_handler){\n
// add new gadget and render it\n
tab_container=$(\'#\'+dom_id);\n
tab_container.empty();\n
// XXX: allow add any gadget,gadget:source items within API\n
html_string =[\'<div class="gadget" \',\n
\'gadget="\' + form_id + \'/Form_asRenderJSGadget" \',\n
\'gadget:data-handler="\' + gadget_data_handler + \'" \',\n
\'gadget:data-source="Form_asJSON?form_id=\' + form_id + \'"></div>\'].join(\'\\n\');\n
\n
tab_container.append(html_string);\n
tab_gadget = tab_container.find(".gadget");\n
Form.setCurrentFormId(form_id);\n
\n
// render new gadget\n
RenderJs.loadGadgetFromUrl(tab_gadget);\n
\n
// Update it (XXX: how to know gadget loaded the DOM?)\n
window.setTimeout("RenderJs.updateGadgetData(tab_gadget)", DEFAULT_GADGET_DOM_READY_TIMEOUT);\n
}\n
\n
}\n
\n
/*\n
Form field renderer\n
*/\n
var Form = {\n
\n
// elements marked with this class can be serizlized to server\n
SERIALIZE_ABLE_CLASS_NAME: "serialize-able",\n
\n
CURRENT_FORM_ID: "",\n
\n
getCurrentFormId: function (){\n
/* Get current form ID (return hard coded one for now) */\n
return Form.CURRENT_FORM_ID;\n
},\n
\n
setCurrentFormId: function (form_id){\n
/* Set current form ID (return hard coded one for now) */\n
Form.CURRENT_FORM_ID = form_id;\n
},\n
\n
\n
getFieldId: function(field_id){\n
/* Generate local form field id */\n
return "field_" + field_id;\n
},\n
\n
updateField: function (dom, field_dict){\n
/* General purpose field updater */\n
editable = Boolean(field_dict[\'editable\']);\n
if (editable){\n
dom.val(field_dict["value"]);}\n
else{\n
// if field is not editable just show its value\n
dom.replaceWith(field_dict["value"]);\n
}\n
},\n
\n
addOptionTagList: function (index, value){\n
if(value[1]==field_value){\n
select_dom.append(\'<option selected value="\' + value[1] + \'">\' + value[0] + \'</option>\');}\n
else{\n
select_dom.append(\'<option value="\' + value[1] + \'">\' + value[0] + \'</option>\'); }\n
},\n
\n
BaseInputField: function (field_id, field_dict){\n
/* HTML based input field */\n
dom = $("[name=" + Form.getFieldId(field_id) + "]");\n
Form.updateField(dom, field_dict);\n
display_width = field_dict["display_width"];\n
if (display_width){\n
dom.attr("size", display_width);}\n
return dom;\n
},\n
EditorField: function (field_id, field_dict){\n
/* HTML based input field */\n
dom = $("#" + Form.getFieldId(field_id));\n
Form.updateField(dom, field_dict);\n
return dom;\n
},\n
\n
ListField: function (field_id, field_dict){\n
/* Select field */\n
field_value = field_dict["value"]\n
select_dom = $("select[name=" + Form.getFieldId(field_id) + "]");\n
$.each(field_dict["items"], Form.addOptionTagList);\n
return select_dom; \n
},\n
\n
ParallelListField: function (field_id, field_dict){\n
/* mutiple select fields */\n
// XXX: we render only first value but it can be many how to get them ?\n
field_value = field_dict["value"][0]\n
select_dom = $("select[name=subfield_field_" + field_id + "]");\n
$.each(field_dict["items"], Form.addOptionTagList);\n
return select_dom;\n
},\n
\n
CheckBoxField: function (field_id, field_dict){\n
/* CheckBoxField field */\n
checked = Boolean(field_dict["value"])\n
checkbox_dom = $("input[name=" + Form.getFieldId(field_id) + "]");\n
if (checked){\n
checkbox_dom.attr(\'checked\', true)}\n
return checkbox_dom;\n
},\n
\n
TextAreaField: function (field_id, field_dict){\n
/* TextArea field */\n
return Form.BaseInputField(field_id, field_dict);\n
},\n
\n
StringField: function (field_id, field_dict){\n
/* String field */\n
return Form.BaseInputField(field_id, field_dict);\n
},\n
\n
IntegerField: function (field_id, field_dict){\n
/* Int field */\n
return Form.BaseInputField(field_id, field_dict);\n
},\n
\n
PasswordField: function (field_id, field_dict){\n
/* PasswordField field */\n
return Form.BaseInputField(field_id, field_dict);\n
},\n
\n
DateTimeField: function (field_id, field_dict){\n
/* DateTimeField field */\n
dom = $("[name=" + Form.getFieldId(field_id) + "]");\n
date = field_dict["value"];\n
date = new Date(date);\n
dom.datepicker();\n
dom.datepicker({ dateFormat: \'yyyy/mm/dd\' });\n
dom.datepicker(\'setDate\', date);\n
return dom;\n
},\n
\n
EmailField: function (field_id, field_dict){\n
/* Email field */\n
return Form.BaseInputField(field_id, field_dict);\n
},\n
\n
FormBox: function (field_id, field_dict){\n
/* Email field */\n
return Form.BaseInputField(field_id, field_dict);\n
},\n
\n
RelationStringField: function (field_id, field_dict){\n
/* Relation field */\n
return Form.BaseInputField(field_id, field_dict);\n
},\n
\n
ImageField: function (field_id, field_dict){\n
/* Image field */\n
dom = $("img[name=" + Form.getFieldId(field_id) + "]");\n
// XXX: image field should return details like quality, etc ...\n
dom.attr("src", field_dict["value"]+ "?quality=75.0&display=thumbnail&format=png");\n
},\n
\n
ListBox: function (field_id, field_dict){\n
/* Listbox field */\n
listbox_id = "field_" + field_id;\n
navigation_id = listbox_id + "_pager"; \n
listbox_table = jQuery("#"+listbox_id);\n
current_form_id = Form.getCurrentFormId();\n
\n
listbox_dict = field_dict[\'listbox\']\n
listbox_data_url = listbox_dict["listbox_data_url"]\n
colModel = []\n
column_title_list = [];\n
$.each(listbox_dict[\'columns\'],\n
function(i, value){\n
index = value[0];\n
title = value[1];\n
column_title_list.push(title);\n
column = {\'name\': index,\n
\'index\': index,\n
\'width\': 150,\n
\'align\': \'left\'}\n
colModel.push(column);\n
});\n
\n
listbox_table.jqGrid( {url:listbox_data_url + \'?form_id=\' + current_form_id + \'&amps;listbox_id=\' + field_id,\n
datatype: "json",\n
colNames: column_title_list,\n
colModel: colModel,\n
rowNum:listbox_dict[\'lines\'],\n
pager: \'#\'+navigation_id,\n
sortname: \'id\',\n
viewrecords: true,\n
sortorder: "desc",\n
caption: field_dict["title"] });\n
listbox_table.jqGrid(\'navGrid\', \'#\'+navigation_id, {edit:false,add:false,del:false});\n
return listbox_table;\n
}\n
\n
}\n
\n
/* Generic form updater */\n
var FormUpdater = {\n
\n
\n
update: function(data){\n
/* Update form values */\n
$.each(data[\'form_data\'],\n
function(field_id, field_dict){\n
type = field_dict["type"];\n
dom = undefined;\n
if(Form.hasOwnProperty(type)){\n
dom = Form[type](field_id, field_dict);\n
}\n
\n
// add a class that these fields are editable so asJSON\n
// can serialize for for sending to server\n
if (dom!=undefined&&dom!=null&&field_dict["editable"]){\n
dom.addClass(Form.SERIALIZE_ABLE_CLASS_NAME);\n
}\n
\n
// mark required fields visually\n
if (field_dict["required"]){\n
dom.parent().parent().children("label").css("font-weight", "bold");}\n
\n
});\n
},\n
\n
save: function(){\n
/* save form to server*/\n
form_value_dict = {}\n
$("." + Form.SERIALIZE_ABLE_CLASS_NAME).each(function(index){\n
// DOM can change values, i.e. alter checkbox (on / off)\n
element = $(this); \n
name = element.attr("name");\n
value = element.val();\n
type = element.attr("type");\n
if (type=="checkbox"){\n
value = element.is(":checked");\n
if(value==true){converted_value=1;}\n
if(value==false){converted_value=0;}\n
//value = {true:1, false:0}[value];\n
value = converted_value; \n
}\n
// XXX: how to handle file uploads ?\n
form_value_dict[name] = value;\n
});\n
//console.log(form_value_dict);\n
\n
// add form_id as we need to know structure we\'re saving at server side\n
form_value_dict["form_id"] = Form.getCurrentFormId();\n
\n
// validation happens at server side\n
$.ajax({url:\'Form_save\',\n
data: form_value_dict,\n
dataType: "json",\n
success: function (data) {\n
field_errors = data.field_errors;\n
if (field_errors!=undefined){\n
//console.log(field_errors);\n
$.each(field_errors, function(index, value){\n
dom = $("[name=" + Form.getFieldId(index) + "]");\n
dom.css("border", "1px solid red"); // XXX: use class / css\n
field = dom.parent().parent();\n
if (field.children("span.error").length > 0){\n
// just update message\n
field.children("span.error").html(value);}\n
else{\n
// no validation error message exists\n
field.append(\'<span class="error">\' + value + \'</span>\');}\n
}\n
);}\n
else{\n
// validation OK at server side\n
$("span.error").each(function(index) {\n
// delete validation messages\n
element = $(this);\n
element.parent().children("div.input").children("." +Form.SERIALIZE_ABLE_CLASS_NAME).css("border", "none");\n
element.remove();\n
});\n
// show a fading portal_status_message\n
$("#portal_status_message").toggle();\n
$("#portal_status_message p").html("Saved");\n
window.setTimeout( \'$("#portal_status_message").toggle()\', 4000);\n
}\n
}});\n
}\n
}\n
\n
/*\n
* Generic Gadget library renderer\n
*/\n
\n
var RenderJs = {\n
\n
\n
bootstrap: function (root){\n
/* initial load application gadget */\n
RenderJs.loadGadgetFromUrl(root);\n
RenderJs.load(root);\n
},\n
\n
load: function (root) {\n
/* Load gadget layout by traversing DOM */\n
gadget_list = root.find("[gadget]");\n
// Load chilren\n
gadget_list.each(function(i,v){RenderJs.loadGadgetFromUrl($(this));});\n
},\n
\n
updateAndRecurse: function(gadget, data){\n
/* Update current gadget and recurse down */\n
gadget.append(data);\n
// a gadget may contain sub gadgets\n
RenderJs.load(gadget);\n
},\n
\n
loadGadgetFromUrl: function(gadget) {\n
/* Load gadget\'s SPECs from URL */\n
url = gadget.attr("gadget");\n
\n
// XXX: based on URL and more ? generate gadget uid?\n
\n
// XXX: How to know how long a form should be cached locally\n
// i.e. what happens if it changes at server side ?\n
\n
// handle caching\n
gadget_property = gadget.attr("gadget:property");\n
\n
cacheable = false;\n
if (gadget_property!=undefined){\n
gadget_property = $.parseJSON(gadget_property)\n
cacheable = Boolean(gadget_property.cacheable);}\n
//cacheable = false ; // to develop faster\n
if (cacheable){\n
// get from cache if possible, use last part from URL as cache_key\n
cache_id = gadget_property.cache_id\n
app_cache = Cache.get(cache_id, undefined);\n
\n
if(app_cache==undefined){\n
// not in cache so we pull from network and cache\n
//console.log("not in cache: " + cache_id + " " + url);\n
$.ajax({url:url,\n
yourCustomData: {"cache_id": cache_id},\n
success: function (data) {\n
cache_id = this.yourCustomData.cache_id;\n
//console.log("set in cache: " + cache_id);\n
Cache.set(cache_id, data);\n
RenderJs.updateAndRecurse(gadget, data);\n
}});\n
}\n
else{\n
// get from cache\n
data = app_cache;\n
RenderJs.updateAndRecurse(gadget, data);}\n
}\n
else{\n
// not to be cached\n
//console.log("Not to be cached " + url);\n
$.ajax({url:url,\n
success: function (data) {\n
RenderJs.updateAndRecurse(gadget, data);}});\n
}\n
\n
},\n
\n
update: function (root) {\n
/* update gadget with data from remote source */\n
root.find("[gadget]").each(function(i,v){RenderJs.updateGadgetData($(this));});\n
},\n
\n
traverse: function (method_name){\n
// not working with ie7\n
method = undefined;\n
parent = window;\n
$.each(method_name.split(\'.\'),\n
function(index,value){\n
method = parent[value];\n
//console.log(value + obj);\n
if (method!=undefined){\n
parent=method;}\n
else{\n
return undefined;}});\n
return method;\n
},\n
\n
updateGadgetWithDataHandler: function (result) {\n
data_handler = this.yourCustomData.data_handler;\n
if (data_handler!=undefined){\n
eval(data_handler+ "(result)");\n
}\n
},\n
\n
updateGadgetData: function(gadget) {\n
/* Do real gagdet update here */\n
data_source = gadget.attr("gadget:data-source");\n
data_handler = gadget.attr("gadget:data-handler");\n
// acquire data and pass it to method handler\n
if (data_source!=undefined){\n
$.ajax({url:data_source,\n
dataType: "json",\n
yourCustomData: {"data_handler": data_handler},\n
success: RenderJs.updateGadgetWithDataHandler});}\n
}\n
\n
}\n
]]></string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>size</string> </key>
<value> <int>18539</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>renderjs.js</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_EtagSupport__etag</string> </key>
<value> <string>ts33622213.73</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>renderjs_test.js</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>application/javascript</string> </value>
</item>
<item>
<key> <string>data</string> </key>
<value> <string>function setupRenderJSTest(){\n
/*\n
* Main RenderJS test entry point\n
*/\n
module("Cache");\n
test(\'Cache\', function(){\n
cache_id = \'my_test\';\n
data = {\'gg\':1};\n
Cache.set(cache_id, data);\n
deepEqual(data, Cache.get(cache_id));\n
});\n
\n
module("TabularGadget");\n
test(\'addNewTabGadget\', function(){\n
TabbularGadget.addNewTabGadget("Person_view", "qunit-fixture", "FormUpdater.update");\n
equal($("#qunit-fixture").children(".gadget").length, 1);\n
});\n
\n
};\n
\n
</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>size</string> </key>
<value> <int>545</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Folder" module="OFS.Folder"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>erp5_renderjs</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_text</string> </key>
<value> <unicode>\074!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"\n
"http://www.w3.org/TR/html4/loose.dtd"\076\n
\074html\076\n
\074head\076\n
\074link rel="stylesheet" href="jquery/plugin/qunit/qunit.css" type="text/css"/\076\n
\074script src="jquery/core/jquery.js"\076 \074/script\076\n
\074script src="jquery/plugin/qunit/qunit.js" type="text/javascript"\076\074/script\076\n
\074script type="text/javascript" src="jquery/plugin/renderjs/renderjs.js"\076\074/script\076\n
\074!--[if IE]\076\n
\074script type="text/javascript" src="jquery/plugin/jstorage/jquery.json-2.3.js"\076\074/script\076\n
\074!--\074![endif]--\076\n
\074script type="text/javascript" src="jquery/plugin/jstorage/jstorage.js"\076\074/script\076\n
\074script type="text/javascript" src="jquery/plugin/renderjs/renderjs_test.js"\076\074/script\076\n
\n
\074/head\076\n
\074body\076\n
\074h1 id="qunit-header"\076QUnit RenderJS test suite\074/h1\076\n
\074h2 id="qunit-banner"\076\074/h2\076\n
\074h2 id="qunit-userAgent"\076\074/h2\076\n
\074ol id="qunit-tests"\076\n
\074/ol\076\n
\074div id="qunit-fixture"\076 \074/div\076\n
\n
\074script type="text/javascript"\076\n
//\074![CDATA[\n
$(document).ready(setupRenderJSTest());\n
//]]\076\n
\074/script\076\n
\n
\n
\074/body\076\n
\074/html\076</unicode> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>expand</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ERP5Site_viewRenderJSQunitTestRunner</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
<value> <string>iso-8859-15</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode></unicode> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
2012-04-06 Ivan
* Initial commit
\ No newline at end of file
2012 (c) Nexedi SA
\ No newline at end of file
erp5_jquery
erp5_jquery_plugin_jstorage
\ No newline at end of file
This Business Template contains only static files of RenderJS library.
\ No newline at end of file
GPL
\ No newline at end of file
portal_skins/erp5_jquery/jquery/plugin/renderjs
portal_skins/erp5_jquery/jquery/plugin/renderjs/**
\ No newline at end of file
erp5_renderjs
\ No newline at end of file
erp5_jquery_plugin_renderjs
\ No newline at end of file
5.4.7
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment