<?xml version="1.0"?>
<bindings xmlns="http://www.mozilla.org/xbl" xmlns:xbl="http://www.mozilla.org/xbl" xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="rdf_grid_base">
<implementation>
<constructor>
{# ВНИМАНИЕ НАСЛЕДНИКАМ!! Все действия связанные с RDF и формированием контента необходимо выполнять в методе __constructor а не в штатном кострукторе. #}
{# метод __constructor вызывается после получения данных о составе и полях таблицы #}
<![CDATA[
this._chlid_real_constructor="__constructor()"
{#Разрешаем колонку "редактировать"#}
this.enable_edit=true
{#Разрешаем колонку "удалить"#}
this.enable_delete=true
{#Разрешаем колонку "создать"#}
this.enable_create=true
this._fetch_table_info()
]]>
</constructor>
{# Нода корневого НЕ анонимного элемента#}
<property name="root_node" onget="return this"/>
{# Название отображаемой таблицы #}
<property name="table_name" onget="return this.getAttribute('table')"/>
{# УРЛ для данных#}
<property name="data_url" onget="return '/rdf/'+this.table_name+'/list/'"/>
{# РЕФ для данных #}
<property name="data_ref" onget="return 'http://'+(location.host)+'/'+this.table_name+'-list'"/>
{# Заголовок для урлы данных поля #}
<property name="data_value" onget="return 'rdf:http://'+(location.host)+'/'+this.table_name+'/list#'"/>
{# Список редактируемых полей #}
<property name="editable_fields" onget="return this.getAttribute('editable_fields').split(',')"/>
<property name="ok_signal" onget="return 'OK'"/>
<method name="_fetch_table_info">
{# Запрашивает у сервера JSON объект вида Array({name:"ru", related:false|model_of_related_field,col_view:{width:100,caption:"Заголовок"}}) с информацией о полях модели #}
<body>
<![CDATA[
url="/rdf/"+this.table_name+"/descr/"
var req = new XMLHttpRequest();
req.open('POST', url, true);
root_node=this.root_node
req.onreadystatechange = function (aEvt)
{
if (req.readyState == 4)
{
if(req.status == 200)
{
{# Если все ОК - парсим ответ как JSON, заполняем поле fields корневой ноды, после этого вызываем настоящий конструктор корневого грида после этого - настоящий конструктор потомка#}
root_node.fields=req.responseText.parseJSON()
//root_node.pre_render_data_fetched=true
root_node._fetch_related_models()
}
else
{# Если не все ОК - выводим сообщение об ошибке#}
root_node.table_info_not_found()
}
}
req.send(null);
]]>
</body>
</method>
<method name="table_info_not_found">
{#Вызывается если при получении описания модели(таблицы) возникли ошибки #}
<body>
<![CDATA[
alert("Table '"+this.table_name+"' description cannot be retrieved!")
]]>
</body>
</method>
<method name='_related_fetched'>
<body><![CDATA[
fetched=true
for (i in this.related_models)
if (typeof(this.related_models[i])=="object")
{
fetched=fetched&&this.related_models[i]
}
if (fetched) return true
else return false
]]></body>
</method>
<method name="_fetch_related_models">
<body><![CDATA[
root_node=this.root_node
this.related_models={}
for (i in this.fields)
{
if (typeof(this.fields[i])=="object" && this.fields[i]["related_model"]!=false)
{
this.related_models[this.fields[i]["related_model"]]=false
}
}
for (k in this.related_models)
if (typeof(this.related_models[k])=="boolean")
{
url="/rdf/"+k+"/json_list/"
var req = new XMLHttpRequest();
req.open('POST', url, true);
req.onreadystatechange = function (aEvt)
{
if (req.readyState == 4)
{
if(req.status == 200)
{
{# #}
resp=req.responseText.parseJSON()
root_node.related_models[resp['model']]=resp['value']
if (root_node._related_fetched()) root_node._root_constructor()
}
}
}
req.send(null);
}
]]></body>
</method>
<method name="_root_constructor">
{#Настоящий конструктор базы грида#}
<body>
<![CDATA[
{#Вызываем конструктор потомка#}
eval("root_node."+root_node._chlid_real_constructor)
]]>
</body>
</method>
<method name="_refresh_datasources">
{#Вызвать обновление RDF для всех нод, содержащих аттрибут datasources#}
<body>
<![CDATA[
nodes=Array()
nodes_with_ds=getXPathNodes('.//xul:*[@datasources]',document.getAnonymousNodes(this)[0])
refreshable_node=nodes_with_ds.iterateNext()
while(refreshable_node)
{
nodes.push(refreshable_node)
refreshable_node=nodes_with_ds.iterateNext()
}
for (i in nodes)
if (typeof(nodes[i])=="object")
{
//alert(nodes[i].builder)
//nodes[i].builder.refresh()
//nodes[i].builder.rebuild()
}
]]>
</body>
</method>
<method name="_delete_row">
<parameter name="rec_id"/>
{#Удалить строку с заданным id из БД#}
<body>
<![CDATA[
url="/rdf/"+this.table_name+"/delitem/"+rec_id+"/"
root_node=this.root_node
var req = new XMLHttpRequest();
req.open('POST', url, true);
req.onreadystatechange = function (aEvt)
{
if (req.readyState == 4)
{
if(req.status == 200)
{
{#Если все ок - обнявляем датасорсы #}
if (req.responseText==root_node.ok_signal)
root_node._refresh_datasources()
}
}
}
req.send(null);
]]>
</body>
</method>
</implementation>
</binding>
<binding id="grid" extends="#rdf_grid_base">
{# ВНИМАНИЕ!! Все действия связанные с RDF и формированием контента необходимо выполнять в методе __constructor а не в штатном кострукторе. #}
{# данный метод вызывается после получения данных о составе и полях таблицы #}
<implementation>
<constructor>
<![CDATA[
]]>
</constructor>
<method name="__constructor">
{# Настоящий констуктор. Название настоящего конструктора определяется переменной root_node._chlid_real_constructor. По умолчанию = "__constructor()" #}
<body>
<![CDATA[
{# Формируем заголовки #}
cols=getElementByIdFromNodeList(document.getAnonymousNodes(this),"cols")
rows=getElementByIdFromNodeList(document.getAnonymousNodes(this),"rows")
template=rows.firstChild.nextSibling
{# добавляем шаблон строки грида #}
row=template.appendChild(createXULElement("row"))
row.setAttribute("uri","rdf:*")
for (i in this.fields){#И ебашим ячейки#}
{
if (typeof(this.fields[i])=="object" && this.fields[i]['name']!='id') {#Рисуем все ячейки, кроме id#}
{
{#Создаем столбцы#}
column=cols.appendChild(createXULElement("column"))
column_label=column.appendChild(createXULElement("label"))
column_label.setAttribute("value",this.fields[i]["cols_view"]["caption"])
{#И задаем ячейки строк#}
if (this.fields[i]["related_model"]==false) {#Если это не связанное поле - обрабатываем его как текст#}
{
row_value=row.appendChild(createXULElement("textbox"))
row_value.setAttribute("value",this.data_value+this.fields[i]["name"])
row_value.setAttribute("db_field",this.fields[i]["name"])
row_value.setAttribute("onchange","this.setAttribute('style',this.getAttribute('style')+';color:#ff0000');this.setAttribute('edited',true)")
}
else {# Если это связанное поле - подставляем элемент связанного поля #}
{
menupopup=createXULElement("menupopup")
for (k in this.related_models[this.fields[i]["related_model"]])
if (typeof(this.related_models[this.fields[i]["related_model"]][k])=='object')
{
//alert(this.related_models[this.fields[i]["related_model"]][k]['value'])
menuitem=menupopup.appendChild(createXULElement("menuitem"))
menuitem.setAttribute('label',this.related_models[this.fields[i]["related_model"]][k]['value'])
menuitem.setAttribute('pk',this.related_models[this.fields[i]["related_model"]][k]['pk'])
menuitem.setAttribute('value',this.related_models[this.fields[i]["related_model"]][k]['value'])
}
row_value=row.appendChild(createXULElement("menulist"))
row_value.appendChild(menupopup)
{#Задаем элемент, которій выбран в менюдисте - соотв. значению из полученного РДФ-а #}
row_value.setAttribute("value",this.data_value+this.fields[i]["name"])
row_value.setAttribute("db_field",this.fields[i]["name"])
//row_value.
row_value.setAttribute("oncommand","this.setAttribute('style','color:#FF0000');this.setAttribute('edited',true)")
}
{#Определяем - какие ячейки задизаблить в соотв. с аттрибутом "editable_fields"#}
field_disabled=true
for (l in this.editable_fields)
if (typeof(this.editable_fields[l])=="string" && this.editable_fields[l]==this.fields[i]['name'])
{
field_disabled=false
}
if (field_disabled) row_value.setAttribute("disabled",field_disabled)
}
else if (typeof(this.fields[i])=="object") {#Если это id - прибиваем его к строке#}
{
row.setAttribute('db_id',this.data_value+this.fields[i]["name"])
}
}
if (this.enable_edit)
{
column=cols.appendChild(createXULElement("column"))
edit_button=row.appendChild(createXULElement("button"))
edit_button.setAttribute("label","Сохранить")
}
if (this.enable_delete)
{
column=cols.appendChild(createXULElement("column"))
delete_button=row.appendChild(createXULElement("button"))
delete_button.setAttribute("label","Удалить")
delete_button.setAttribute("oncommand","root_node._delete_row(this.parentNode.getAttribute('db_id'));getElementByIdFromNodeList(document.getAnonymousNodes(root_node),'rows').builder.refresh()")
}
root_node=this.root_node {#В конце создаем строку пустых значений в которую заносятся добавляемые данные. Чтобы строка была добавлена ПОСЛЕ рендера данных - добавляем вызываем код через listener#}
//bilder=rows.builder.QueryInterface(Components.interfaces.nsIXULTreeBuilder)
rows.setAttribute("datasources",this.data_url)
//rows.builder.addListener(this.gridBuilderListener)
rows.setAttribute("ref",this.data_ref)
//rows.builder.removeListener(this.gridBuilderListener)
]]>
</body>
</method>
<method name="_before_grid_rebuild">
<body>
<![CDATA[
]]>
</body>
</method>
<field name="gridBuilderListener">
<![CDATA[
({
root_node: this,
willRebuild: function(aBuilder) {
//alert("willRebuild")
rows=getElementByIdFromNodeList(document.getAnonymousNodes(this.root_node),"rows")
row_for_new=getElementByIdFromNodeList(document.getAnonymousNodes(this.root_node),"new_row")
if (row_for_new)
{
//alert("removing row")
rows.removeChild(row_for_new)
}
},
didRebuild: function(aBuilder) {
rows=getElementByIdFromNodeList(document.getAnonymousNodes(this.root_node),"rows")
//if (rows.firstChild.nextSibling.nextSibling)
//{
row=rows.appendChild(createXULElement("row"))
row.setAttribute("id","new_row")
for (i in root_node.fields)
{
if (typeof(this.root_node.fields[i])=="object" && this.root_node.fields[i]['name']!='id')
{
if (this.root_node.fields[i]["related_model"]==false) {#Если это не связанное поле - обрабатываем его как текст#}
{
row_value=row.appendChild(createXULElement("textbox"))
}
else {# Если это связанное поле - подставляем элемент связанного поля #}
{
menupopup=createXULElement("menupopup")
for (k in this.root_node.related_models[this.root_node.fields[i]["related_model"]])
if (typeof(this.root_node.related_models[this.root_node.fields[i]["related_model"]][k])=='object')
{
menuitem=menupopup.appendChild(createXULElement("menuitem"))
menuitem.setAttribute('label',this.root_node.related_models[this.root_node.fields[i]["related_model"]][k]['value'])
menuitem.setAttribute('pk',this.root_node.related_models[this.root_node.fields[i]["related_model"]][k]['pk'])
menuitem.setAttribute('value',this.root_node.related_models[this.root_node.fields[i]["related_model"]][k]['value'])
}
menulist=row.appendChild(createXULElement("menulist"))
row_value=menulist.appendChild(menupopup)
}
}
}
if (root_node.enable_create)
{
create_button=row.appendChild(createXULElement("button"))
create_button.setAttribute("label","Создать")
create_button.setAttribute("oncommand","this.parentNode.parentNode.builder.refresh()")
}
alert(row.nextSibling)
//alert("didRebuild")
//}
//else{
//this.root_node._refresh_datasources()
//}
}
})
]]></field>
</implementation>
<content>
<xul:grid id="grid">
<xul:columns id="cols">
</xul:columns>
<xul:rows id="rows" datasources="" ref="">
<xul:row/>
<xul:template/>
</xul:rows>
</xul:grid>
<xul:button label="обновить" oncommand="this.parentNode._refresh_datasources()"/>
</content>
</binding>
</bindings>
<!--
-->