Commit 9f009acf authored by Kazuhiko Shiozaki's avatar Kazuhiko Shiozaki

update renderjs : rev b30668ded565234de3e72bf0f4f9919c4ccf1d5b.

* add minified version
* now depend on jquery on erp5_jquery_plugin_json instead of erp5_jquery_plugin_jstorage.
parent d22d7a11
......@@ -12,7 +12,7 @@
</item>
<item>
<key> <string>_EtagSupport__etag</string> </key>
<value> <string>ts49355317.03</string> </value>
<value> <string>ts58177410.64</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
......@@ -38,16 +38,10 @@
// still it\'s possible to override this and use explicit gadget rendering\n
var RENDERJS_ENABLE_IMPLICIT_GADGET_RENDERING = true;\n
\n
// by default RenderJs will examin and bind all interaction gadgets\n
// available\n
// by default RenderJs will examine and bind all interaction gadgets\n
// available \n
var RENDERJS_ENABLE_IMPLICIT_INTERACTION_BIND = true;\n
\n
if (typeof require !== \'undefined\') {\n
// example of how we can use requirejs to load external libraries\n
//require(["../../../../lib/jstorage/jstorage.js"], function (util) {\n
//});\n
}\n
\n
// fallback for IE\n
if (typeof console === "undefined" || typeof console.log === "undefined") {\n
console = {};\n
......@@ -59,19 +53,52 @@ var RenderJs = (function () {\n
var is_ready = false;\n
\n
return {\n
\n
init: function () {\n
/*\n
* Do all initialization\n
*/\n
if (RENDERJS_ENABLE_IMPLICIT_GADGET_RENDERING) {\n
RenderJs.bootstrap($(\'body\'));\n
}\n
if (RENDERJS_ENABLE_IMPLICIT_INTERACTION_BIND) {\n
var root_gadget = RenderJs.GadgetIndex.getRootGadget();\n
// We might have a page without gadgets.\n
// Be careful, right now we can be in this case because\n
// asynchronous gadget loading is not finished\n
if (root_gadget !== undefined) {\n
RenderJs.bindReady(\n
function () {\n
// examine all Intaction Gadgets and bind accordingly\n
$("div[data-gadget-connection]").each( function (index, element) {\n
RenderJs.InteractionGadget.bind($(element));\n
})\n
});\n
}\n
};\n
},\n
\n
bootstrap: function (root) {\n
/* initial load application gadget */\n
var gadget_id;\n
/*\n
* Load all gadgets for this DOM element\n
* (including recursively contained ones)\n
*/\n
var gadget_id, is_gadget;\n
gadget_id = root.attr("id");\n
if (gadget_id!==undefined) {\n
is_gadget = root.attr("data-gadget") !== undefined;\n
// this will make RenderJs fire "ready" event when all gadgets are loaded.\n
RenderJs.setReady(false);\n
if (is_gadget && gadget_id !== undefined ) {\n
// bootstart root gadget only if it is indeed a gadget\n
RenderJs.loadGadgetFromUrl(root);\n
RenderJs.loadGadget(root);\n
}\n
RenderJs.load(root);\n
RenderJs.loadRecursiveGadget(root);\n
},\n
\n
load: function (root) {\n
/* Load gadget layout by traversing DOM */\n
loadRecursiveGadget: function (root) {\n
/*\n
* Load all contained gadgets inside passed DOM element.\n
*/\n
var gadget_list, gadget, gadget_id, gadget_js;\n
gadget_list = root.find("[data-gadget]");\n
\n
......@@ -86,25 +113,30 @@ var RenderJs = (function () {\n
\n
// Load chilren\n
gadget_list.each(function () {\n
RenderJs.loadGadgetFromUrl($(this));\n
RenderJs.loadGadget($(this));\n
});\n
},\n
\n
updateAndRecurse: function (gadget, data) {\n
/* Update current gadget and recurse down */\n
setGadgetAndRecurse: function (gadget, data) {\n
/*\n
* Set gadget data and recursively load it in case it holds another\n
* gadgets.\n
*/\n
gadget.append(data);\n
// a gadget may contain sub gadgets\n
this.load(gadget);\n
RenderJs.loadRecursiveGadget(gadget);\n
},\n
\n
loadGadgetFromUrl: function (gadget) {\n
/* Load gadget\'s SPECs from URL */\n
loadGadget: function (gadget) {\n
/*\n
* Load gadget\'s SPECs from URL\n
*/\n
var url, gadget_id, gadget_property, cacheable, cache_id,\n
app_cache, data, gadget_js;\n
app_cache, data, gadget_js, is_update_gadget_data_running;\n
url = gadget.attr("data-gadget");\n
gadget_id = gadget.attr("id");\n
gadget_js = RenderJs.GadgetIndex.getGadgetById(gadget_id);\n
if (gadget_js===undefined) {\n
if (gadget_js === undefined) {\n
// register gadget in javascript namespace if not already registered\n
gadget_js = new RenderJs.Gadget(gadget_id, gadget);\n
RenderJs.GadgetIndex.registerGadget(gadget_js);\n
......@@ -144,16 +176,18 @@ var RenderJs = (function () {\n
RenderJs.Cache.set(cache_id, data);\n
RenderJs.GadgetIndex.getGadgetById(gadget_id).\n
setReady();\n
RenderJs.updateAndRecurse(gadget, data);\n
RenderJs.setGadgetAndRecurse(gadget, data);\n
RenderJs.checkAndTriggerReady();\n
RenderJs.updateGadgetData(gadget);\n
}\n
});\n
} else {\n
// get from cache\n
data = app_cache;\n
gadget_js.setReady();\n
this.updateAndRecurse(gadget, data);\n
this.setGadgetAndRecurse(gadget, data);\n
this.checkAndTriggerReady();\n
RenderJs.updateGadgetData(gadget);\n
}\n
} else {\n
// not to be cached\n
......@@ -164,19 +198,28 @@ var RenderJs = (function () {\n
gadget_id = this.yourCustomData.gadget_id;\n
RenderJs.GadgetIndex.getGadgetById(gadget_id).\n
setReady();\n
RenderJs.updateAndRecurse(gadget, data);\n
RenderJs.setGadgetAndRecurse(gadget, data);\n
RenderJs.checkAndTriggerReady();\n
RenderJs.updateGadgetData(gadget);\n
}\n
});\n
}\n
} else {\n
// gadget is an inline one so no need to load it from network\n
gadget_js.setReady();\n
}\n
else {\n
// gadget is an inline (InteractorGadget or one using\n
// data-gadget-source / data-gadget-handler) so no need\n
// to load it from network\n
is_update_gadget_data_running = RenderJs.updateGadgetData(gadget);\n
if (!is_update_gadget_data_running) {\n
// no update is running so gadget is basically ready\n
// if update is running then it should take care and set status\n
gadget_js.setReady();\n
}\n
RenderJs.checkAndTriggerReady();\n
}\n
},\n
\n
isReady: function (value) {\n
isReady: function () {\n
/*\n
* Get rendering status\n
*/\n
......@@ -210,29 +253,18 @@ var RenderJs = (function () {\n
trigger("ready");\n
// trigger ready on root body element\n
$("body").trigger("ready");\n
// this set will make sure we fire this event only once\n
RenderJs.setReady(true);\n
}\n
}\n
return is_gadget_list_loaded;\n
},\n
\n
update: function (root) {\n
/* update gadget with data from remote source */\n
root.find("[gadget]").each(function (i, v) {\n
RenderJs.updateGadgetData($(this));\n
});\n
},\n
\n
updateGadgetWithDataHandler: function (result) {\n
var data_handler;\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
/*\n
* Gadget can be updated from "data-gadget-source" (i.e. a json)\n
* and "data-gadget-handler" attributes (i.e. a namespace Javascript) \n
*/\n
var data_source, data_handler;\n
data_source = gadget.attr("data-gadget-source");\n
data_handler = gadget.attr("data-gadget-handler");\n
......@@ -241,13 +273,30 @@ var RenderJs = (function () {\n
$.ajax({\n
url: data_source,\n
dataType: "json",\n
yourCustomData: {"data_handler": data_handler},\n
success: RenderJs.updateGadgetWithDataHandler\n
yourCustomData: {"data_handler": data_handler,\n
"gadget_id": gadget.attr("id")},\n
success: function (result) {\n
var data_handler, gadget_id;\n
data_handler = this.yourCustomData.data_handler;\n
gadget_id = this.yourCustomData.gadget_id;\n
if (data_handler !== undefined) {\n
// eval is not nice to use\n
eval(data_handler + "(result)");\n
gadget = RenderJs.GadgetIndex.getGadgetById(gadget_id);\n
// mark gadget as loaded and fire a check\n
// to see if all gadgets are loaded\n
gadget.setReady();\n
RenderJs.checkAndTriggerReady();\n
}\n
}\n
});\n
// asynchronous update happens and respective thread will update status\n
return true;\n
}\n
return false;\n
},\n
\n
addGadget: function (dom_id, gadget, gadget_data_handler,\n
addGadget: function (dom_id, gadget_id, gadget, gadget_data_handler,\n
gadget_data_source) {\n
/*\n
* add new gadget and render it\n
......@@ -256,7 +305,7 @@ var RenderJs = (function () {\n
tab_container = $(\'#\' + dom_id);\n
tab_container.empty();\n
html_string = [\n
\'<div class="gadget" \',\n
\'<div id="\' + gadget_id + \'"\',\n
\'data-gadget="\' + gadget + \'"\',\n
\'data-gadget-handler="\' + gadget_data_handler + \'" \',\n
\'data-gadget-source="\' + gadget_data_source + \'"></div>\'\n
......@@ -264,19 +313,10 @@ var RenderJs = (function () {\n
\n
tab_container.append(html_string);\n
tab_gadget = tab_container.find(".gadget");\n
\n
// render new gadget\n
RenderJs.setReady(false);\n
RenderJs.loadGadgetFromUrl(tab_gadget);\n
// clear previous events\n
RenderJs.GadgetIndex.getRootGadget().getDom().bind(\n
"ready",\n
function () {\n
if (!is_ready) {\n
RenderJs.updateGadgetData(tab_gadget);\n
is_ready = true;\n
}\n
}\n
);\n
RenderJs.bootstrap(tab_container);\n
\n
return tab_gadget;\n
},\n
\n
......@@ -406,6 +446,16 @@ var RenderJs = (function () {\n
*/\n
this.is_ready = true;\n
};\n
\n
this.remove = function () {\n
/*\n
* Remove gadget (including its DOM element).\n
*/\n
// unregister from GadgetIndex\n
RenderJs.GadgetIndex.unregisterGadget(this);\n
// remove its DOM element\n
$(this.getDom()).remove();\n
};\n
}),\n
\n
TabbularGadget: (function () {\n
......@@ -423,14 +473,14 @@ var RenderJs = (function () {\n
visible_dom.removeClass("not_selected");\n
},\n
\n
addNewTabGadget: function (dom_id, gadget, gadget_data_handler,\n
addNewTabGadget: function (dom_id, gadget_id, gadget, gadget_data_handler,\n
gadget_data_source) {\n
/*\n
* add new gadget and render it\n
*/\n
var tab_gadget;\n
tab_gadget = RenderJs.addGadget(\n
dom_id, gadget, gadget_data_handler, gadget_data_source\n
dom_id, gadget_id, gadget, gadget_data_handler, gadget_data_source\n
);\n
// XXX: we should unregister all gadgets (if any we replace now in DOM)\n
}\n
......@@ -444,6 +494,26 @@ var RenderJs = (function () {\n
var gadget_list = [];\n
\n
return {\n
\n
getGadgetIdListFromDom: function (dom) {\n
/*\n
* Get list of all gadget\'s ID from DOM\n
*/\n
var gadget_id_list = [];\n
$.each(dom.find(\'[data-gadget]\'),\n
function (index, value) {\n
gadget_id_list.push($(value).attr("id"));}\n
);\n
return gadget_id_list;\n
},\n
\n
setGadgetList: function (gadget_list_value) {\n
/*\n
* Set list of registered gadgets\n
*/\n
gadget_list = gadget_list_value;\n
},\n
\n
getGadgetList: function () {\n
/*\n
* Return list of registered gadgets\n
......@@ -519,18 +589,19 @@ var RenderJs = (function () {\n
/*\n
* Bind event between gadgets.\n
*/\n
var gadget_id, gadget_connection_list,\n
var gadget_id, gadget_connection_list, \n
createMethodInteraction = function (\n
original_source_method_id, source_gadget_id,\n
source_method_id, destination_gadget_id,\n
destination_method_id) {\n
var interaction = function () {\n
// execute source method\n
RenderJs.GadgetIndex.getGadgetById(\n
source_gadget_id)[original_source_method_id].\n
apply(null, arguments);\n
// call trigger so bind can be asynchronously called\n
RenderJs.GadgetIndex.getGadgetById(\n
destination_gadget_id)[destination_method_id].\n
apply(null, arguments);\n
destination_gadget_id).dom.trigger(source_method_id);\n
};\n
return interaction;\n
},\n
......@@ -577,6 +648,14 @@ var RenderJs = (function () {\n
destination_gadget_id,\n
destination_method_id\n
);\n
// we use html custom events for asyncronous method call so\n
// bind destination_gadget to respective event\n
destination_gadget.dom.bind(\n
source_method_id,\n
createTriggerInteraction(\n
destination_gadget_id, destination_method_id\n
)\n
);\n
}\n
else {\n
// this is a custom event attached to HTML gadget\n
......@@ -596,26 +675,10 @@ var RenderJs = (function () {\n
}());\n
\n
// impliticly call RenderJs bootstrap\n
$(document).ready(function () {\n
if (RENDERJS_ENABLE_IMPLICIT_GADGET_RENDERING) {\n
RenderJs.bootstrap($(\'body\'));\n
}\n
if (RENDERJS_ENABLE_IMPLICIT_INTERACTION_BIND) {\n
var root_gadget = RenderJs.GadgetIndex.getRootGadget();\n
// We might have a page without gadgets.\n
// Be careful, right now we can be in this case because\n
// asynchronous gadget loading is not finished\n
if (root_gadget !== undefined) {\n
RenderJs.bindReady(\n
function () {\n
// examine all Intaction Gadgets and bind accordingly\n
$("div[data-gadget-connection]").each( function(index, element) {\n
RenderJs.InteractionGadget.bind($(element));\n
})\n
});\n
}\n
}\n
});
// $(document).ready(function () {\n
// RenderJs.init();\n
// });\n
]]></string> </value>
</item>
......@@ -625,7 +688,7 @@ $(document).ready(function () {\n
</item>
<item>
<key> <string>size</string> </key>
<value> <int>22925</int> </value>
<value> <int>26111</int> </value>
</item>
<item>
<key> <string>title</string> </key>
......
<?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>ts58177449.44</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>renderjs.min.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[
/*global console, require, $, localStorage, document */"use strict";var RENDERJS_ENABLE_IMPLICIT_GADGET_RENDERING=!0,RENDERJS_ENABLE_IMPLICIT_INTERACTION_BIND=!0;if(typeof console=="undefined"||typeof console.log=="undefined")console={},console.log=function(){};var RenderJs=function(){var is_ready=!1;return{init:function(){RENDERJS_ENABLE_IMPLICIT_GADGET_RENDERING&&RenderJs.bootstrap($("body"));if(RENDERJS_ENABLE_IMPLICIT_INTERACTION_BIND){var a=RenderJs.GadgetIndex.getRootGadget();a!==undefined&&RenderJs.bindReady(function(){$("div[data-gadget-connection]").each(function(a,b){RenderJs.InteractionGadget.bind($(b))})})}},bootstrap:function(a){var b,c;b=a.attr("id"),c=a.attr("data-gadget")!==undefined,RenderJs.setReady(!1),c&&b!==undefined&&RenderJs.loadGadget(a),RenderJs.loadRecursiveGadget(a)},loadRecursiveGadget:function(a){var b,c,d,e;b=a.find("[data-gadget]"),b.each(function(){c=$(this),d=c.attr("id"),e=new RenderJs.Gadget(d,c),RenderJs.GadgetIndex.registerGadget(e)}),b.each(function(){RenderJs.loadGadget($(this))})},setGadgetAndRecurse:function(a,b){a.append(b),RenderJs.loadRecursiveGadget(a)},loadGadget:function(a){var b,c,d,e,f,g,h,i,j;b=a.attr("data-gadget"),c=a.attr("id"),i=RenderJs.GadgetIndex.getGadgetById(c),i===undefined&&(i=new RenderJs.Gadget(c,a),RenderJs.GadgetIndex.registerGadget(i)),d=a.attr("data-gadget-property"),d!==undefined&&(d=$.parseJSON(d),$.each(d,function(a,b){i[a]=b})),b!==undefined&&b!==""?(e=a.attr("data-gadget-cacheable"),f=a.attr("data-gadget-cache-id"),e!==undefined&&f!==undefined&&(e=Boolean(parseInt(e,10))),e?(g=RenderJs.Cache.get(f,undefined),g===undefined||g===null?$.ajax({url:b,yourCustomData:{gadget_id:c,cache_id:f},success:function(b){f=this.yourCustomData.cache_id,c=this.yourCustomData.gadget_id,RenderJs.Cache.set(f,b),RenderJs.GadgetIndex.getGadgetById(c).setReady(),RenderJs.setGadgetAndRecurse(a,b),RenderJs.checkAndTriggerReady(),RenderJs.updateGadgetData(a)}}):(h=g,i.setReady(),this.setGadgetAndRecurse(a,h),this.checkAndTriggerReady(),RenderJs.updateGadgetData(a))):$.ajax({url:b,yourCustomData:{gadget_id:c},success:function(b){c=this.yourCustomData.gadget_id,RenderJs.GadgetIndex.getGadgetById(c).setReady(),RenderJs.setGadgetAndRecurse(a,b),RenderJs.checkAndTriggerReady(),RenderJs.updateGadgetData(a)}})):(j=RenderJs.updateGadgetData(a),j||i.setReady(),RenderJs.checkAndTriggerReady())},isReady:function(){return is_ready},setReady:function(a){is_ready=a},bindReady:function(a){$("body").one("ready",a)},checkAndTriggerReady:function(){var a;return a=RenderJs.GadgetIndex.isGadgetListLoaded(),a&&(RenderJs.isReady()||(RenderJs.GadgetIndex.getRootGadget().getDom().trigger("ready"),$("body").trigger("ready"),RenderJs.setReady(!0))),a},updateGadgetData:function(gadget){var data_source,data_handler;return data_source=gadget.attr("data-gadget-source"),data_handler=gadget.attr("data-gadget-handler"),data_source!==undefined&&data_source!==""?($.ajax({url:data_source,dataType:"json",yourCustomData:{data_handler:data_handler,gadget_id:gadget.attr("id")},success:function(result){var data_handler,gadget_id;data_handler=this.yourCustomData.data_handler,gadget_id=this.yourCustomData.gadget_id,data_handler!==undefined&&(eval(data_handler+"(result)"),gadget=RenderJs.GadgetIndex.getGadgetById(gadget_id),gadget.setReady(),RenderJs.checkAndTriggerReady())}}),!0):!1},addGadget:function(a,b,c,d,e){var f,g,h;return g=$("#"+a),g.empty(),f=[\'<div id="\'+b+\'"\',\'data-gadget="\'+c+\'"\',\'data-gadget-handler="\'+d+\'" \',\'data-gadget-source="\'+e+\'"></div>\'].join("\\n"),g.append(f),h=g.find(".gadget"),RenderJs.bootstrap(g),h},Cache:function(){return{ROOT_CACHE_ID:"APP_CACHE",getCacheId:function(a){return this.ROOT_CACHE_ID+a},hasLocalStorage:function(){var a;a="localstorage_test_12345678";try{return localStorage.setItem(a,a),localStorage.removeItem(a),!0}catch(b){return!1}},get:function(a,b){return a=this.getCacheId(a),this.hasLocalStorage()?this.LocalStorageCachePlugin.get(a,b):this.NameSpaceStorageCachePlugin.get(a,b)},set:function(a,b){a=this.getCacheId(a),this.hasLocalStorage()?this.LocalStorageCachePlugin.set(a,b):this.NameSpaceStorageCachePlugin.set(a,b)},LocalStorageCachePlugin:function(){return{get:function(a,b){return a in localStorage?JSON.parse(localStorage.getItem(a)):b},set:function(a,b){localStorage.setItem(a,JSON.stringify(b))}}}(),NameSpaceStorageCachePlugin:function(){var a={};return{get:function(b,c){return a[b]},set:function(b,c){a[b]=c}}}()}}(),Gadget:function(a,b){this.id=a,this.dom=b,this.is_ready=!1,this.getId=function(){return this.id},this.getDom=function(){return this.dom},this.isReady=function(){return this.is_ready},this.setReady=function(){this.is_ready=!0},this.remove=function(){RenderJs.GadgetIndex.unregisterGadget(this),$(this.getDom()).remove()}},TabbularGadget:function(){return{toggleVisibility:function(a){$(".selected").addClass("not_selected"),$(".selected").removeClass("selected"),a.addClass("selected"),a.removeClass("not_selected")},addNewTabGadget:function(a,b,c,d,e){var f;f=RenderJs.addGadget(a,b,c,d,e)}}}(),GadgetIndex:function(){var a=[];return{getGadgetIdListFromDom:function(a){var b=[];return $.each(a.find("[data-gadget]"),function(a,c){b.push($(c).attr("id"))}),b},setGadgetList:function(b){a=b},getGadgetList:function(){return a},registerGadget:function(b){a.push(b)},unregisterGadget:function(b){var c=$.inArray(b,a);c!==-1&&a.splice(c,1)},getGadgetById:function(a){var b;return b=undefined,$(RenderJs.GadgetIndex.getGadgetList()).each(function(c,d){d.getId()===a&&(b=d)}),b},getRootGadget:function(){return this.getGadgetList()[0]},isGadgetListLoaded:function(){var a;return a=!0,$(this.getGadgetList()).each(function(b,c){c.isReady()===!1&&(a=!1)}),a}}}(),InteractionGadget:function(){return{bind:function(a){var b,c,d=function(a,b,c,d,e){var f=function(){RenderJs.GadgetIndex.getGadgetById(b)[a].apply(null,arguments),RenderJs.GadgetIndex.getGadgetById(d).dom.trigger(c)};return f},e=function(a,b){var c=function(){RenderJs.GadgetIndex.getGadgetById(a)[b].apply(null,arguments)};return c};b=a.attr("id"),c=a.attr("data-gadget-connection"),c=$.parseJSON(c),$.each(c,function(a,b){var c,f,g,h,i,j,k,l,m;c=b.source.split("."),f=c[0],g=c[1],h=RenderJs.GadgetIndex.getGadgetById(f),i=b.destination.split("."),j=i[0],k=i[1],l=RenderJs.GadgetIndex.getGadgetById(j),h.hasOwnProperty(g)?(m="original_"+g,h[m]=h[g],h[g]=d(m,f,g,j,k),l.dom.bind(g,e(j,k))):h.dom.bind(g,e(j,k))})}}}()}}();
]]></string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>size</string> </key>
<value> <int>6425</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>renderjs.min.js</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
2012 (c) Nexedi SA
\ No newline at end of file
2012-2013 © Nexedi SA
\ No newline at end of file
erp5_jquery
erp5_jquery_plugin_jstorage
\ No newline at end of file
erp5_jquery_plugin_json
\ No newline at end of file
This Business Template contains only static files of RenderJS library.
\ No newline at end of file
This Business Template contains only static files of RenderJS library.
* http://www.renderjs.org/
\ No newline at end of file
21
\ No newline at end of file
22
\ 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