mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
FEATURE Switched TreeDropdownField to use jquery.jstree instead of custom tree.js library. Rewrote TreeDropdownField.js from behaviour.js to jquery.entwine.js
This commit is contained in:
parent
a00ccead8f
commit
4b9ab5c8aa
@ -1,78 +1,43 @@
|
|||||||
div.TreeDropdownField {
|
div.TreeDropdownField {
|
||||||
width: 35em;
|
width: 200px;
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
html>body div.TreeDropdownField {
|
|
||||||
position:relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.TreeDropdownField {
|
|
||||||
width: 27.7em;
|
|
||||||
background: #fff;
|
|
||||||
font-size: 12px;
|
|
||||||
height: 21px;
|
height: 21px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.TreeDropdownField span.items {
|
div.TreeDropdownField span.items {
|
||||||
|
float: left;
|
||||||
|
overflow:hidden;
|
||||||
|
width: 174px;
|
||||||
height: 14px;
|
height: 14px;
|
||||||
border: 1px #7f9db9 solid;
|
border: 1px #7f9db9 solid;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: 25.4em;
|
padding: 2px 0 2px 4px;
|
||||||
float: left;
|
|
||||||
padding: 2px 0 2.5px 4px;
|
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
margin:0;
|
|
||||||
font-size: 12px;
|
|
||||||
overflow:hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
div.TreeDropdownField div.tree_holder {
|
div.TreeDropdownField .panel {
|
||||||
clear: left;
|
position:relative;
|
||||||
|
overflow: auto;
|
||||||
|
display: none;
|
||||||
|
z-index: 1000;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
border: 1px black solid;
|
border: 1px black solid;
|
||||||
margin: 0;
|
width: 200px;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
overflow: auto;
|
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
/**
|
|
||||||
* HACK IE6, see http://www.hedgerwow.com/360/bugs/css-select-free.html
|
|
||||||
*/
|
|
||||||
position:absolute;
|
|
||||||
z-index:10;
|
|
||||||
width:33em;/*must have for any value*/;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
div.TreeDropdownField div.tree_holder ul.tree a {
|
div.TreeDropdownField .panel.loading {
|
||||||
|
background: #f00;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.TreeDropdownField .panel ul.tree a {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.TreeDropdownField div.tree_holder ul.tree {
|
div.TreeDropdownField .panel ul.tree {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
html>body div.TreeDropdownField div.tree_holder {
|
|
||||||
top: 20px;
|
|
||||||
left: 0px;
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HACK IE6, see http://www.hedgerwow.com/360/bugs/css-select-free.html
|
|
||||||
*/
|
|
||||||
div.TreeDropdownField div.tree_holder iframe {
|
|
||||||
display:none;/* IE5*/
|
|
||||||
display/**/:block;/* IE5*/
|
|
||||||
position:absolute;
|
|
||||||
border-style: none;
|
|
||||||
top:0;
|
|
||||||
left:0;
|
|
||||||
z-index:-1;
|
|
||||||
filter:mask();
|
|
||||||
width:31em;/*must have for any big value*/
|
|
||||||
height:200px/*must have for any big value*/;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.TreeDropdownField a.editLink {
|
div.TreeDropdownField a.editLink {
|
||||||
border:none;
|
border:none;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
@ -115,16 +115,13 @@ class TreeDropdownField extends FormField {
|
|||||||
public function Field() {
|
public function Field() {
|
||||||
Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang');
|
Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang');
|
||||||
|
|
||||||
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/prototype/prototype.js');
|
|
||||||
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/behaviour/behaviour.js');
|
|
||||||
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery/jquery.js');
|
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery/jquery.js');
|
||||||
Requirements::javascript(SAPPHIRE_DIR . '/javascript/jquery_improvements.js');
|
Requirements::javascript(SAPPHIRE_DIR . '/javascript/jquery_improvements.js');
|
||||||
Requirements::javascript(SAPPHIRE_DIR . '/javascript/tree/tree.js');
|
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-entwine/dist/jquery.entwine-dist.js');
|
||||||
// needed for errorMessage()
|
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jstree/jquery.jstree.js');
|
||||||
Requirements::javascript(SAPPHIRE_DIR . '/javascript/LeftAndMain.js');
|
Requirements::javascript(SAPPHIRE_DIR . '/javascript/TreeDropdownField.js');
|
||||||
Requirements::javascript(SAPPHIRE_DIR . '/javascript/TreeSelectorField.js');
|
|
||||||
|
|
||||||
Requirements::css(SAPPHIRE_DIR . '/javascript/tree/tree.css');
|
Requirements::css(SAPPHIRE_DIR . '/thirdparty/jquery-ui-themes/smoothness/jquery.ui.all.css');
|
||||||
Requirements::css(SAPPHIRE_DIR . '/css/TreeDropdownField.css');
|
Requirements::css(SAPPHIRE_DIR . '/css/TreeDropdownField.css');
|
||||||
|
|
||||||
if($this->Value() && $record = $this->objectForKey($this->Value())) {
|
if($this->Value() && $record = $this->objectForKey($this->Value())) {
|
||||||
@ -138,7 +135,7 @@ class TreeDropdownField extends FormField {
|
|||||||
array (
|
array (
|
||||||
'id' => "TreeDropdownField_{$this->id()}",
|
'id' => "TreeDropdownField_{$this->id()}",
|
||||||
'class' => 'TreeDropdownField single' . ($this->extraClass() ? " {$this->extraClass()}" : ''),
|
'class' => 'TreeDropdownField single' . ($this->extraClass() ? " {$this->extraClass()}" : ''),
|
||||||
'href' => $this->form ? $this->Link() : "",
|
'href' => $this->form ? $this->Link('tree') : "",
|
||||||
),
|
),
|
||||||
$this->createTag (
|
$this->createTag (
|
||||||
'input',
|
'input',
|
||||||
@ -152,7 +149,7 @@ class TreeDropdownField extends FormField {
|
|||||||
$this->createTag(
|
$this->createTag(
|
||||||
'input',
|
'input',
|
||||||
array(
|
array(
|
||||||
'class' => 'items',
|
'class' => 'title',
|
||||||
'value' => '(Choose or type search)'
|
'value' => '(Choose or type search)'
|
||||||
)
|
)
|
||||||
) :
|
) :
|
||||||
@ -197,7 +194,8 @@ class TreeDropdownField extends FormField {
|
|||||||
|
|
||||||
$this->search = Convert::Raw2SQL($request->getVar('search'));
|
$this->search = Convert::Raw2SQL($request->getVar('search'));
|
||||||
|
|
||||||
if($ID = (int) $request->latestparam('ID')) {
|
$ID = (is_numeric($request->latestparam('ID'))) ? (int)$request->latestparam('ID') : (int)$request->requestVar('ID');
|
||||||
|
if($ID) {
|
||||||
$obj = DataObject::get_by_id($this->sourceObject, $ID);
|
$obj = DataObject::get_by_id($this->sourceObject, $ID);
|
||||||
$isSubTree = true;
|
$isSubTree = true;
|
||||||
|
|
||||||
@ -232,15 +230,15 @@ class TreeDropdownField extends FormField {
|
|||||||
$obj->markToExpose($this->objectForKey($value));
|
$obj->markToExpose($this->objectForKey($value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$eval = '"<li id=\"selector-' . $this->Name() . '-{$child->' . $this->keyField . '}\" class=\"$child->class"' .
|
$eval = '"<li id=\"selector-' . $this->Name() . '-{$child->' . $this->keyField . '}\" data-id=\"$child->' . $this->keyField . '\" class=\"$child->class"' .
|
||||||
' . $child->markingClasses() . "\"><a rel=\"$child->ID\">" . $child->' . $this->labelField . ' . "</a>"';
|
' . $child->markingClasses() . "\"><a rel=\"$child->ID\">" . $child->' . $this->labelField . ' . "</a>"';
|
||||||
|
|
||||||
if($isSubTree) {
|
if($isSubTree) {
|
||||||
return substr(trim($obj->getChildrenAsUL('', $eval, null, true)), 4, -5);
|
return substr(trim($obj->getChildrenAsUL('', $eval, null, true)), 4, -5);
|
||||||
|
} else {
|
||||||
|
return $obj->getChildrenAsUL('class="tree"', $eval, null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $obj->getChildrenAsUL('class="tree"', $eval, null, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,40 +1,22 @@
|
|||||||
(function($) {
|
(function($) {
|
||||||
$.entwine('ss', function($){
|
$.entwine('ss', function($){
|
||||||
|
|
||||||
var strings = {
|
|
||||||
'openlink': 'Open',
|
|
||||||
'fieldTitle': '(choose)',
|
|
||||||
'searchFieldTitle': '(choose or search)'
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo Error display
|
* @todo Locale support
|
||||||
* @todo No results display for search
|
* @todo Multiselect
|
||||||
* @todo Automatic expansion of ajax children when multiselect is triggered
|
* @todo Search
|
||||||
* @todo Automatic panel positioning based on available space (top/bottom)
|
|
||||||
* @todo forceValue
|
|
||||||
* @todo Automatic width
|
|
||||||
* @todo Expand title height to fit all elements
|
|
||||||
*/
|
*/
|
||||||
$('.TreeDropdownField').entwine({
|
$('.TreeDropdownField').entwine({
|
||||||
onmatch: function() {
|
onmatch: function() {
|
||||||
this.append(
|
this.append('<div class="panel"><div class="tree-holder"></div></div>');
|
||||||
'<span class="title"></span>' +
|
|
||||||
'<a href="#" title="' + strings.openLink + '" class="toggle-panel-link"></a>' +
|
|
||||||
'<div class="panel"><div class="tree-holder"></div></div>'
|
|
||||||
);
|
|
||||||
if(this.data('title')) this.setTitle(this.data('title'));
|
|
||||||
this.getPanel().hide();
|
|
||||||
|
|
||||||
this._super();
|
|
||||||
},
|
},
|
||||||
getPanel: function() {
|
getPanel: function() {
|
||||||
return this.find('.panel');
|
return this.find('.panel');
|
||||||
},
|
},
|
||||||
openPanel: function() {
|
openPanel: function() {
|
||||||
var panel = this.getPanel(), tree = this.find('.tree-holder');
|
var panel = this.getPanel();
|
||||||
panel.show();
|
panel.show();
|
||||||
if(tree.is(':empty')) this.loadTree();
|
if(!panel.find('li').length) this.loadTree();
|
||||||
},
|
},
|
||||||
closePanel: function() {
|
closePanel: function() {
|
||||||
this.getPanel().hide();
|
this.getPanel().hide();
|
||||||
@ -43,55 +25,40 @@
|
|||||||
this[this.getPanel().is(':visible') ? 'closePanel' : 'openPanel']();
|
this[this.getPanel().is(':visible') ? 'closePanel' : 'openPanel']();
|
||||||
},
|
},
|
||||||
setTitle: function(title) {
|
setTitle: function(title) {
|
||||||
if(!title) title = strings.fieldTitle;
|
|
||||||
|
|
||||||
this.find('.title').text(title);
|
this.find('.title').text(title);
|
||||||
this.data('title', title); // separate view from storage (important for search cancellation)
|
|
||||||
},
|
},
|
||||||
getTitle: function() {
|
getTitle: function() {
|
||||||
return this.find('.title').text();
|
return this.find('.title').text();
|
||||||
},
|
},
|
||||||
setValue: function(val) {
|
setValue: function(val) {
|
||||||
this.find(':input:hidden').val(val);
|
this.find(':input').val(val);
|
||||||
},
|
},
|
||||||
getValue: function() {
|
getValue: function() {
|
||||||
return this.find(':input:hidden').val();
|
return this.find(':input').val();
|
||||||
},
|
},
|
||||||
loadTree: function(params, callback) {
|
loadTree: function() {
|
||||||
var self = this, panel = this.getPanel(), treeHolder = $(panel).find('.tree-holder');
|
var self = this, treeHolder = $(this.getPanel()).find('.tree-holder');
|
||||||
var params = (params) ? this.getRequestParams().concat(params) : this.getRequestParams();
|
this.addClass('loading');
|
||||||
panel.addClass('loading');
|
treeHolder.load(this.attr('href'), {}, function(html, status, xhr) {
|
||||||
treeHolder.load(this.data('url-tree'), params, function(html, status, xhr) {
|
|
||||||
var firstLoad = true;
|
|
||||||
if(status == 'success') {
|
if(status == 'success') {
|
||||||
$(this)
|
$(this)
|
||||||
.bind('loaded.jstree', function(e, data) {
|
.bind('loaded.jstree', function(e, data) {
|
||||||
var val = self.getValue();
|
var val = self.getValue();
|
||||||
if(val) data.inst.select_node(treeHolder.find('*[data-id=' + val + ']'));
|
if(val) data.inst.select_node(treeHolder.find('*[data-id=' + val + ']'));
|
||||||
firstLoad = false;
|
|
||||||
if(callback) callback.apply(self);
|
|
||||||
})
|
})
|
||||||
.jstree(self.getTreeConfig())
|
.jstree(self.getTreeConfig())
|
||||||
.bind('select_node.jstree', function(e, data) {
|
.bind('select_node.jstree', function(e, data) {
|
||||||
var node = data.rslt.obj, id = $(node).data('id');
|
var node = data.rslt.obj;
|
||||||
if(self.getValue() == id) {
|
self.setValue($(node).data('id'));
|
||||||
self.setValue(null);
|
self.setTitle(data.inst.get_text(node));
|
||||||
self.setTitle(null);
|
self.closePanel();
|
||||||
} else {
|
|
||||||
self.setValue(id);
|
|
||||||
self.setTitle(data.inst.get_text(node));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid auto-closing panel on first load
|
|
||||||
if(!firstLoad) self.closePanel();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
panel.removeClass('loading');
|
self.removeClass('loading');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getTreeConfig: function() {
|
getTreeConfig: function() {
|
||||||
var self = this;
|
|
||||||
return {
|
return {
|
||||||
'core': {
|
'core': {
|
||||||
'initially_open': ['record-0'],
|
'initially_open': ['record-0'],
|
||||||
@ -101,11 +68,9 @@
|
|||||||
// TODO Hack to avoid ajax load on init, see http://code.google.com/p/jstree/issues/detail?id=911
|
// TODO Hack to avoid ajax load on init, see http://code.google.com/p/jstree/issues/detail?id=911
|
||||||
'data': this.getPanel().find('.tree-holder').html(),
|
'data': this.getPanel().find('.tree-holder').html(),
|
||||||
'ajax': {
|
'ajax': {
|
||||||
'url': this.data('url-tree'),
|
'url': this.attr('href'),
|
||||||
'data': function(node) {
|
'data': function(node) {
|
||||||
var id = $(node).data("id") ? $(node).data("id") : 0, params = self.getRequestParams();
|
return { ID : $(node).data("id") ? $(node).data("id") : 0 , ajax: 1};
|
||||||
params = params.concat([{name: 'ID', value: id}, {name: 'ajax', value: 1}]);
|
|
||||||
return params;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -113,20 +78,11 @@
|
|||||||
"select_limit" : 1,
|
"select_limit" : 1,
|
||||||
'initially_select': [this.getPanel().find('.current').attr('id')]
|
'initially_select': [this.getPanel().find('.current').attr('id')]
|
||||||
},
|
},
|
||||||
|
'themes': {
|
||||||
|
'theme': 'apple'
|
||||||
|
},
|
||||||
'plugins': ['html_data', 'ui', 'themes']
|
'plugins': ['html_data', 'ui', 'themes']
|
||||||
// 'plugins': ['html_data', 'ui', 'themeroller']
|
|
||||||
};
|
};
|
||||||
},
|
|
||||||
/**
|
|
||||||
* If the field is contained in a form, submit all form parameters by default.
|
|
||||||
* This is useful to keep state like locale values which are typically
|
|
||||||
* encoded in hidden fields through the form.
|
|
||||||
*
|
|
||||||
* @return {array}
|
|
||||||
*/
|
|
||||||
getRequestParams: function() {
|
|
||||||
var form = this.parents('form');
|
|
||||||
return form.length ? form.serializeArray() : [];
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$('.TreeDropdownField *').entwine({
|
$('.TreeDropdownField *').entwine({
|
||||||
@ -134,103 +90,9 @@
|
|||||||
return this.parents('.TreeDropdownField:first');
|
return this.parents('.TreeDropdownField:first');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$('.TreeDropdownField .toggle-panel-link, .TreeDropdownField span.title').entwine({
|
$('.TreeDropdownField .editLink').entwine({
|
||||||
onclick: function(e) {
|
onclick: function(e) {
|
||||||
this.getField().togglePanel();
|
this.getField().togglePanel();
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.TreeDropdownField.searchable').entwine({
|
|
||||||
onmatch: function() {
|
|
||||||
this._super();
|
|
||||||
|
|
||||||
var title = this.data('title');
|
|
||||||
this.find('.title').replaceWith(
|
|
||||||
$('<input type="text" class="title search" />')
|
|
||||||
);
|
|
||||||
this.setTitle(title ? title : strings.searchFieldTitle);
|
|
||||||
},
|
|
||||||
setTitle: function(title) {
|
|
||||||
if(!title) title = strings.fieldTitle;
|
|
||||||
|
|
||||||
this.find('.title').val(title);
|
|
||||||
},
|
|
||||||
getTitle: function() {
|
|
||||||
return this.find('.title').val();
|
|
||||||
},
|
|
||||||
search: function(str, callback) {
|
|
||||||
this.openPanel();
|
|
||||||
this.loadTree({search: str}, callback);
|
|
||||||
},
|
|
||||||
cancelSearch: function() {
|
|
||||||
this.closePanel();
|
|
||||||
this.loadTree();
|
|
||||||
this.setTitle(this.data('title'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.TreeDropdownField.searchable input.search').entwine({
|
|
||||||
onkeydown: function(e) {
|
|
||||||
var field = this.getField();
|
|
||||||
if(e.keyCode == 13) {
|
|
||||||
// trigger search on ENTER key
|
|
||||||
field.search(this.val());
|
|
||||||
return false;
|
|
||||||
} else if(e.keyCode == 27) {
|
|
||||||
// cancel search on ESC key
|
|
||||||
field.cancelSearch();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.TreeDropdownField.multiple').entwine({
|
|
||||||
getTreeConfig: function() {
|
|
||||||
var cfg = this._super();
|
|
||||||
cfg.checkbox = {override_ui: true};
|
|
||||||
cfg.plugins.push('checkbox');
|
|
||||||
cfg.ui.select_limit = -1;
|
|
||||||
return cfg;
|
|
||||||
},
|
|
||||||
loadTree: function(params, callback) {
|
|
||||||
var self = this, panel = this.getPanel(), treeHolder = $(panel).find('.tree-holder');
|
|
||||||
var params = (params) ? this.getRequestParams().concat(params) : this.getRequestParams();
|
|
||||||
panel.addClass('loading');
|
|
||||||
treeHolder.load(this.data('url-tree'), params, function(html, status, xhr) {
|
|
||||||
var firstLoad = true;
|
|
||||||
if(status == 'success') {
|
|
||||||
$(this)
|
|
||||||
.bind('loaded.jstree', function(e, data) {
|
|
||||||
$.each(self.getValue(), function(i, val) {
|
|
||||||
data.inst.check_node(treeHolder.find('*[data-id=' + val + ']'));
|
|
||||||
});
|
|
||||||
firstLoad = false;
|
|
||||||
if(callback) callback.apply(self);
|
|
||||||
})
|
|
||||||
.jstree(self.getTreeConfig())
|
|
||||||
.bind('uncheck_node.jstree check_node.jstree', function(e, data) {
|
|
||||||
var nodes = data.inst.get_checked(null, true);
|
|
||||||
self.setValue($.map(nodes, function(el, i) {
|
|
||||||
return $(el).data('id');
|
|
||||||
}));
|
|
||||||
self.setTitle($.map(nodes, function(el, i) {
|
|
||||||
return data.inst.get_text(el);
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
panel.removeClass('loading');
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getValue: function() {
|
|
||||||
var val = this._super();
|
|
||||||
return val.split(/ *, */);
|
|
||||||
},
|
|
||||||
setValue: function(val) {
|
|
||||||
this._super($.isArray(val) ? val.join(',') : val);
|
|
||||||
},
|
|
||||||
setTitle: function(title) {
|
|
||||||
this._super($.isArray(title) ? title.join(', ') : title);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,438 +0,0 @@
|
|||||||
/**
|
|
||||||
* TreeDropdownField.js
|
|
||||||
*/
|
|
||||||
TreeDropdownField = Class.extend('Observable');
|
|
||||||
TreeDropdownField.prototype = {
|
|
||||||
initialize: function() {
|
|
||||||
// Hook up all the fieldy bits
|
|
||||||
this.editLink = this.getElementsByTagName('a')[0];
|
|
||||||
if (this.getElementsByTagName('span').length > 0) {
|
|
||||||
// no search, humanItems is a span
|
|
||||||
this.humanItems = this.getElementsByTagName('span')[0];
|
|
||||||
this.inputTag = this.getElementsByTagName('input')[0];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// search is present, humanItems is an input
|
|
||||||
this.inputTag = this.getElementsByTagName('input')[0];
|
|
||||||
this.humanItems = this.getElementsByTagName('input')[1];
|
|
||||||
this.humanItems.onkeyup = this.search_onkeyup;
|
|
||||||
}
|
|
||||||
this.editLink.treeDropdownField = this;
|
|
||||||
this.humanItems.treeDropdownField = this;
|
|
||||||
this.inputTag.treeDropdownField = this;
|
|
||||||
|
|
||||||
this.editLink.onclick = this.edit_click;
|
|
||||||
this.humanItems.onclick = this.human_click;
|
|
||||||
this.inputTag.setValue = this.setValue.bind(this);
|
|
||||||
},
|
|
||||||
|
|
||||||
destroy: function() {
|
|
||||||
if(this.editLink) {
|
|
||||||
this.editLink.onclick = null;
|
|
||||||
this.editLink.treeDropdownField = null;
|
|
||||||
this.editLink = null;
|
|
||||||
}
|
|
||||||
if(this.humanItems) {
|
|
||||||
this.humanItems.onclick = null;
|
|
||||||
this.humanItems.treeDropdownField = null;
|
|
||||||
this.humanItems = null;
|
|
||||||
}
|
|
||||||
if(this.inputTag) {
|
|
||||||
this.inputTag.setValue = null;
|
|
||||||
this.inputTag.treeDropdownField = null;
|
|
||||||
this.inputTag = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getName: function() {
|
|
||||||
return this.inputTag.name;
|
|
||||||
},
|
|
||||||
|
|
||||||
refresh: function() {
|
|
||||||
this.createTreeNode();
|
|
||||||
|
|
||||||
this.ajaxGetTree( (function(response) {
|
|
||||||
this.newTreeReady(response, false);
|
|
||||||
this.updateTreeLabel();
|
|
||||||
}).bind(this));
|
|
||||||
},
|
|
||||||
|
|
||||||
// Build a URL from the field's base URL and the given sub URL
|
|
||||||
buildURL: function(subURL) {
|
|
||||||
var baseURL = jQuery(this).attr('href');
|
|
||||||
if (!baseURL) {
|
|
||||||
// Occurs if treedropdown has no form e.g. treefields in widget areas.
|
|
||||||
baseURL = this.ownerForm().action + '/field/' + this.getName() + '/';
|
|
||||||
var baseTags = document.getElementsByTagName('base');
|
|
||||||
var base = (baseTags) ? baseTags[0].href : '';
|
|
||||||
if (base == baseURL.substring(0, base.length))
|
|
||||||
baseURL = baseURL.substring(base.length);
|
|
||||||
}
|
|
||||||
var subHasQuerystring = subURL.match(/\?/);
|
|
||||||
|
|
||||||
if(baseURL.match(/^(.*)\?(.*)$/)) {
|
|
||||||
if(subHasQuerystring) return RegExp.$1 + '/' + subURL + '&' + RegExp.$2
|
|
||||||
else return RegExp.$1 + '/' + subURL + '?' + RegExp.$2
|
|
||||||
} else {
|
|
||||||
return baseURL + '/' + subURL;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ownerForm: function() {
|
|
||||||
var f =this.parentNode;
|
|
||||||
while(f && f.tagName.toLowerCase() != 'form') f = f.parentNode;
|
|
||||||
return f;
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleTree: function() {
|
|
||||||
if(this.treeShown) this.hideTree();
|
|
||||||
else this.showTree();
|
|
||||||
},
|
|
||||||
|
|
||||||
createTreeNode: function(keepTreeHidden) {
|
|
||||||
if(!this.itemTree) {
|
|
||||||
this.itemTree = document.createElement('div');
|
|
||||||
|
|
||||||
if(keepTreeHidden) {
|
|
||||||
this.hideTree();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.itemTree.className = 'tree_holder';
|
|
||||||
this.itemTree.innerHTML = ss.i18n._t('LOADING', 'Loading...');
|
|
||||||
this.appendChild(this.itemTree);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteTreeNode: function() {
|
|
||||||
if (!this.itemTree) return;
|
|
||||||
var parent = this.itemTree.parentNode;
|
|
||||||
parent.removeChild(this.itemTree);
|
|
||||||
this.itemTree = null;
|
|
||||||
},
|
|
||||||
|
|
||||||
showTree: function () {
|
|
||||||
if (!this.treeShown) this.saveCurrentState();
|
|
||||||
this.treeShown = true;
|
|
||||||
|
|
||||||
if(this.itemTree) {
|
|
||||||
this.itemTree.style.display = 'block';
|
|
||||||
// Store this in a parameter so that stopObserving works
|
|
||||||
this.bound_testForBlur = this.testForBlur.bind(this);
|
|
||||||
Event.observe(document, 'click', this.bound_testForBlur);
|
|
||||||
this.stretchIframeIfNeeded();
|
|
||||||
} else {
|
|
||||||
this.createTreeNode();
|
|
||||||
|
|
||||||
this.ajaxGetTree( (function(response) {
|
|
||||||
this.newTreeReady(response, false);
|
|
||||||
this.updateTreeLabel();
|
|
||||||
}).bind(this));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
saveCurrentState: function() {
|
|
||||||
this.origHumanText = this.getHumanText();
|
|
||||||
},
|
|
||||||
|
|
||||||
restoreOriginalState: function() {
|
|
||||||
this.setHumanText(this.origHumanText);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If this control is inside an iframe, stretch the iframe out to fit the tree.
|
|
||||||
*/
|
|
||||||
stretchIframeIfNeeded: function() {
|
|
||||||
if(parent && parent.document) {
|
|
||||||
if(!this.iframeObj) {
|
|
||||||
var iframes = parent.document.getElementsByTagName('iframe')
|
|
||||||
var i,item;
|
|
||||||
for(i=0;item=iframes[i];i++) {
|
|
||||||
if(item.contentWindow == window) {
|
|
||||||
this.iframeObj = item;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This iframe stretching doesn't work with the greybox
|
|
||||||
if(this.iframeObj && this.iframeObj.id == 'GB_frame') return;
|
|
||||||
|
|
||||||
var desiredHeight = Position.cumulativeOffset(this.itemTree)[1] + this.itemTree.offsetHeight + 2;
|
|
||||||
if(this.iframeObj && desiredHeight > this.iframeObj.offsetHeight) {
|
|
||||||
this.iframeObj.oldHeight = this.iframeObj.offsetHeight;
|
|
||||||
this.iframeObj.style.height = desiredHeight + 'px';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
unstretchIframeIfNeeded: function() {
|
|
||||||
if(this.iframeObj && this.iframeObj.oldHeight)
|
|
||||||
this.iframeObj.style.height = this.iframeObj.oldHeight + 'px';
|
|
||||||
},
|
|
||||||
|
|
||||||
testForBlur: function (event) {
|
|
||||||
var clicked = Event.element(event);
|
|
||||||
if(clicked != this.itemTree && !hasAncestor(clicked, this.itemTree) && clicked != this.editLink && clicked != this.humanItems) {
|
|
||||||
this.hideTree();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
hideTree: function() {
|
|
||||||
this.treeShown = false;
|
|
||||||
if(this.itemTree) {
|
|
||||||
this.itemTree.style.display = 'none';
|
|
||||||
if(this.bound_testForBlur) Event.stopObserving(document, 'click', this.bound_testForBlur);
|
|
||||||
// this.editLink.style.display = this.humanItems.style.display = 'block';
|
|
||||||
this.unstretchIframeIfNeeded();
|
|
||||||
}
|
|
||||||
// this.style.position = '';
|
|
||||||
},
|
|
||||||
|
|
||||||
ajaxGetTree: function(after) {
|
|
||||||
var ajaxURL = this.buildURL('tree?forceValue=' + this.inputTag.value);
|
|
||||||
var secId = jQuery(':input[name=SecurityID]');
|
|
||||||
ajaxURL += secId.length ? '&SecurityID=' + secId.val() : '';
|
|
||||||
var localeField = jQuery(this.ownerForm()).find(':input[name=locale],:input[name=Locale]');
|
|
||||||
if(localeField.length) {ajaxURL += "&locale=" + localeField.val();}
|
|
||||||
if(this.inputTag.value) ajaxURL += '&forceValue=' + this.inputTag.value;
|
|
||||||
if(this.search() != null) ajaxURL += "&search=" + this.search();
|
|
||||||
jQuery.ajax({
|
|
||||||
'url': ajaxURL,
|
|
||||||
'method' : 'get',
|
|
||||||
'success' : after,
|
|
||||||
'error' : function(response) { errorMessage("Error getting data", response); }
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
search: function() {
|
|
||||||
if (this.humanItems.nodeName != 'INPUT' || !this.searched) return null;
|
|
||||||
return this.humanItems.value;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called once the tree has been delivered from ajax
|
|
||||||
*/
|
|
||||||
newTreeReady: function (response, keepTreeHidden) {
|
|
||||||
this.itemTree.innerHTML = response.responseText;
|
|
||||||
// HACK IE6: see http://www.hedgerwow.com/360/bugs/css-select-free.html
|
|
||||||
this.itemTree.appendChild(document.createElement('iframe'));
|
|
||||||
this.tree = Tree.create(this.itemTree.getElementsByTagName('ul')[0], {
|
|
||||||
ajaxExpansion: this.ajaxExpansion,
|
|
||||||
getIdx: function() {
|
|
||||||
return this.getElementsByTagName('a')[0].getAttribute('rel');
|
|
||||||
},
|
|
||||||
idxBase : 'selector-' + this.getName() + '-',
|
|
||||||
dropdownField : this,
|
|
||||||
onselect : this.tree_click
|
|
||||||
});
|
|
||||||
|
|
||||||
// Select the appropriate items
|
|
||||||
var selectedItems = this.inputTag.value.split(/ *, */);
|
|
||||||
var i, isSelected = {};
|
|
||||||
for(i=0;i<selectedItems.length;i++) isSelected[selectedItems[i]] = true;
|
|
||||||
|
|
||||||
if(!keepTreeHidden) {
|
|
||||||
this.showTree();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Expander bound to each tree node
|
|
||||||
*/
|
|
||||||
ajaxExpansion: function() {
|
|
||||||
this.addNodeClass('loading');
|
|
||||||
var ul = this.treeNodeHolder();
|
|
||||||
ul.innerHTML = ss.i18n._t('LOADING', 'Loading...');
|
|
||||||
|
|
||||||
var ajaxURL = this.options.dropdownField.buildURL('tree/' + this.getIdx());
|
|
||||||
ajaxURL += $('SecurityID') ? '&SecurityID=' + $('SecurityID').value : '';
|
|
||||||
if($('Form_EditForm_Locale')) ajaxURL += "&locale=" + $('Form_EditForm_Locale').value;
|
|
||||||
// ajaxExpansion is called in context of TreeNode, not Tree, so search() doesn't exist.
|
|
||||||
if (this.search && this.search() != null) ajaxURL += "&search=" + this.search();
|
|
||||||
|
|
||||||
jQuery.ajax({
|
|
||||||
'url': ajaxURL,
|
|
||||||
'success' : this.installSubtree.bind(this),
|
|
||||||
'error' : function(response) { errorMessage('error loading subtree', response); }
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
setValue: function(val) {
|
|
||||||
this.inputTag = this.getElementsByTagName('input')[0];
|
|
||||||
|
|
||||||
if(this.inputTag.value != val) {
|
|
||||||
this.inputTag.value = val;
|
|
||||||
jQuery(this).trigger('ss.TreeDropdownField.change', {val: val});
|
|
||||||
this.notify('Change', val);
|
|
||||||
|
|
||||||
// If the tree item is already downloaded, just update the label
|
|
||||||
if($('selector-' + this.getName() + '-' + this.inputTag.value)) {
|
|
||||||
this.updateTreeLabel();
|
|
||||||
|
|
||||||
// Otherwise, update the tree with ajax
|
|
||||||
} else {
|
|
||||||
this.ajaxGetTree( (function(response) {
|
|
||||||
this.createTreeNode(true);
|
|
||||||
this.newTreeReady(response, true);
|
|
||||||
this.updateTreeLabel();
|
|
||||||
}).bind(this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateTreeLabel: function() {
|
|
||||||
if ( this.searched || (this.humanItems.nodeName == 'INPUT' && !this.inputTag.value) ) return; // don't update the search
|
|
||||||
var treeNode;
|
|
||||||
if(treeNode = $('selector-' + this.getName() + '-' + this.inputTag.value)) {
|
|
||||||
this.setHumanText(treeNode.getTitle());
|
|
||||||
|
|
||||||
if(treeNode.tree.selected && treeNode.tree.selected.removeNodeClass) treeNode.tree.selected.removeNodeClass('current');
|
|
||||||
treeNode.addNodeClass('current');
|
|
||||||
this.tree.selected = treeNode;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
this.setHumanText(this.inputTag.value ? this.inputTag.value : '(Choose)');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getHumanText: function() {
|
|
||||||
return this.humanItems.nodeName == 'INPUT' ? this.humanItems.value : this.humanItems.innerHTML;
|
|
||||||
},
|
|
||||||
|
|
||||||
setHumanText: function (s) {
|
|
||||||
if (this.humanItems.nodeName == 'INPUT')
|
|
||||||
this.humanItems.value = s;
|
|
||||||
else
|
|
||||||
this.humanItems.innerHTML = s;
|
|
||||||
},
|
|
||||||
|
|
||||||
setValueFromTree: function(treeID, title) {
|
|
||||||
this.setHumanText(title);
|
|
||||||
this.inputTag.value = treeID.replace('selector-' + this.getName() + '-','');
|
|
||||||
jQuery(this).trigger('ss.TreeDropdownField.change', {val: this.inputTag.value});
|
|
||||||
this.notify('Change', this.inputTag.value);
|
|
||||||
|
|
||||||
this.hideTree();
|
|
||||||
},
|
|
||||||
|
|
||||||
edit_click : function() {
|
|
||||||
if (this.treeDropdownField.treeShown) this.treeDropdownField.restoreOriginalState();
|
|
||||||
this.treeDropdownField.toggleTree();
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
search_onkeyup: function(e) {
|
|
||||||
if(typeof window.event!="undefined") e=window.event; //code for IE
|
|
||||||
if (e.keyCode == 27) { // esc, cancel the selection and hide the tree.
|
|
||||||
this.treeDropdownField.restoreOriginalState();
|
|
||||||
this.treeDropdownField.hideTree();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var that = this;
|
|
||||||
clearTimeout(this.timeout);
|
|
||||||
this.timeout = setTimeout(function() {
|
|
||||||
that.treeDropdownField.searched = true;
|
|
||||||
that.treeDropdownField.deleteTreeNode();
|
|
||||||
that.treeDropdownField.showTree();
|
|
||||||
}, 750);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
human_click: function() {
|
|
||||||
if (this.treeDropdownField.humanItems.nodeName != 'INPUT') {
|
|
||||||
if (this.treeDropdownField.treeShown) this.treeDropdownField.restoreOriginalState();
|
|
||||||
this.treeDropdownField.toggleTree();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.treeDropdownField.treeShown) this.treeDropdownField.toggleTree();
|
|
||||||
if (!this.treeDropdownField.defaultCleared || !this.treeDropdownField.searched) {
|
|
||||||
this.treeDropdownField.defaultCleared = true;
|
|
||||||
this.treeDropdownField.setHumanText('');
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
tree_click : function() {
|
|
||||||
this.options.dropdownField.setValueFromTree(this.id, this.getTitle());
|
|
||||||
|
|
||||||
if(this.tree.selected && this.tree.selected.removeNodeClass) this.tree.selected.removeNodeClass('current');
|
|
||||||
this.addNodeClass('current');
|
|
||||||
this.tree.selected = this;
|
|
||||||
|
|
||||||
this.options.dropdownField.searched = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TreeMultiselectField = Class.extend('TreeDropdownField');
|
|
||||||
TreeMultiselectField.prototype = {
|
|
||||||
destroy: function() {
|
|
||||||
if(this.TreeDropdownField) this.TreeDropdownField.destroy();
|
|
||||||
this.TreeDropdownField = null;
|
|
||||||
},
|
|
||||||
|
|
||||||
newTreeReady: function (response) {
|
|
||||||
this.TreeDropdownField.newTreeReady(response);
|
|
||||||
MultiselectTree.create(this.tree);
|
|
||||||
this.tree.options.onselect = this.updateVal.bind(this);
|
|
||||||
|
|
||||||
// Select the appropriate items
|
|
||||||
var selectedItems = this.inputTag.value.split(/ *, */);
|
|
||||||
var i, isSelected = {};
|
|
||||||
for(i=0;i<selectedItems.length;i++) isSelected[selectedItems[i]] = true;
|
|
||||||
|
|
||||||
var allNodes = this.tree.getElementsByTagName('li');
|
|
||||||
|
|
||||||
for(i=0;i<allNodes.length;i++) {
|
|
||||||
allNodes[i].id.match(/([^-]+)-(\d+)$/);
|
|
||||||
var idx = RegExp.$2
|
|
||||||
if(isSelected[idx]) {
|
|
||||||
this.tree.selectNode(allNodes[i]);
|
|
||||||
allNodes[i].expose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
hideTree: function() {
|
|
||||||
this.TreeDropdownField.hideTree();
|
|
||||||
if(this.tree) this.updateVal();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the inputTag and humanItems from the currently selected nodes.
|
|
||||||
*/
|
|
||||||
updateVal: function() {
|
|
||||||
var internalVal = humanVal = "";
|
|
||||||
|
|
||||||
for(i in this.tree.selectedNodes) {
|
|
||||||
internalVal += (internalVal?',':'') + i;
|
|
||||||
humanVal += (humanVal?', ':'') + this.tree.selectedNodes[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.inputTag.value = internalVal;
|
|
||||||
this.setHumanText(humanVal);
|
|
||||||
},
|
|
||||||
|
|
||||||
updateTreeLabel: function() {
|
|
||||||
var treeNode;
|
|
||||||
|
|
||||||
if(this.inputTag.value) {
|
|
||||||
var innerHTML = '';
|
|
||||||
var selectedItems = this.inputTag.value.split(/ *, */);
|
|
||||||
for(i=0;i<selectedItems.length;i++) {
|
|
||||||
if(treeNode = $('selector-' + this.getName() + '-' + selectedItems[i])) {
|
|
||||||
innerHTML += (innerHTML?', ':'') + treeNode.getTitle();
|
|
||||||
} else {
|
|
||||||
innerHTML += selectedItems[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.setHumanText(innerHTML);
|
|
||||||
} else {
|
|
||||||
this.setHumanText('(Choose)');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
TreeMultiselectField.applyTo('div.TreeDropdownField.multiple');
|
|
||||||
TreeDropdownField.applyTo('div.TreeDropdownField.single');
|
|
Loading…
Reference in New Issue
Block a user