diff --git a/css/TreeDropdownField.css b/css/TreeDropdownField.css
index 23981f2f9..49ce0edb0 100644
--- a/css/TreeDropdownField.css
+++ b/css/TreeDropdownField.css
@@ -1,78 +1,43 @@
div.TreeDropdownField {
- width: 35em;
- padding: 0;
-}
-html>body div.TreeDropdownField {
- position:relative;
-}
-
-div.TreeDropdownField {
- width: 27.7em;
- background: #fff;
- font-size: 12px;
+ width: 200px;
height: 21px;
}
div.TreeDropdownField span.items {
+ float: left;
+ overflow:hidden;
+ width: 174px;
height: 14px;
border: 1px #7f9db9 solid;
cursor: pointer;
- width: 25.4em;
- float: left;
- padding: 2px 0 2.5px 4px;
+ padding: 2px 0 2px 4px;
background-color: #fff;
- margin:0;
- font-size: 12px;
- overflow:hidden;
}
-div.TreeDropdownField div.tree_holder {
- clear: left;
+div.TreeDropdownField .panel {
+ position:relative;
+ overflow: auto;
+ display: none;
+ z-index: 1000;
cursor: default;
border: 1px black solid;
- margin: 0;
+ width: 200px;
height: 200px;
- overflow: auto;
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;
}
-div.TreeDropdownField div.tree_holder ul.tree {
+div.TreeDropdownField .panel ul.tree {
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 {
border:none;
text-decoration: none;
diff --git a/forms/TreeDropdownField.php b/forms/TreeDropdownField.php
index 8423b59aa..605df3597 100755
--- a/forms/TreeDropdownField.php
+++ b/forms/TreeDropdownField.php
@@ -115,16 +115,13 @@ class TreeDropdownField extends FormField {
public function Field() {
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 . '/javascript/jquery_improvements.js');
- Requirements::javascript(SAPPHIRE_DIR . '/javascript/tree/tree.js');
- // needed for errorMessage()
- Requirements::javascript(SAPPHIRE_DIR . '/javascript/LeftAndMain.js');
- Requirements::javascript(SAPPHIRE_DIR . '/javascript/TreeSelectorField.js');
+ Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-entwine/dist/jquery.entwine-dist.js');
+ Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jstree/jquery.jstree.js');
+ Requirements::javascript(SAPPHIRE_DIR . '/javascript/TreeDropdownField.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');
if($this->Value() && $record = $this->objectForKey($this->Value())) {
@@ -138,7 +135,7 @@ class TreeDropdownField extends FormField {
array (
'id' => "TreeDropdownField_{$this->id()}",
'class' => 'TreeDropdownField single' . ($this->extraClass() ? " {$this->extraClass()}" : ''),
- 'href' => $this->form ? $this->Link() : "",
+ 'href' => $this->form ? $this->Link('tree') : "",
),
$this->createTag (
'input',
@@ -152,7 +149,7 @@ class TreeDropdownField extends FormField {
$this->createTag(
'input',
array(
- 'class' => 'items',
+ 'class' => 'title',
'value' => '(Choose or type search)'
)
) :
@@ -197,7 +194,8 @@ class TreeDropdownField extends FormField {
$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);
$isSubTree = true;
@@ -232,15 +230,15 @@ class TreeDropdownField extends FormField {
$obj->markToExpose($this->objectForKey($value));
}
}
-
- $eval = '"
Name() . '-{$child->' . $this->keyField . '}\" class=\"$child->class"' .
+
+ $eval = '"Name() . '-{$child->' . $this->keyField . '}\" data-id=\"$child->' . $this->keyField . '\" class=\"$child->class"' .
' . $child->markingClasses() . "\">ID\">" . $child->' . $this->labelField . ' . ""';
if($isSubTree) {
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);
}
/**
diff --git a/javascript/TreeDropdownField.js b/javascript/TreeDropdownField.js
index da0042b1a..8e52ff84d 100644
--- a/javascript/TreeDropdownField.js
+++ b/javascript/TreeDropdownField.js
@@ -1,40 +1,22 @@
(function($) {
$.entwine('ss', function($){
- var strings = {
- 'openlink': 'Open',
- 'fieldTitle': '(choose)',
- 'searchFieldTitle': '(choose or search)'
- };
-
/**
- * @todo Error display
- * @todo No results display for search
- * @todo Automatic expansion of ajax children when multiselect is triggered
- * @todo Automatic panel positioning based on available space (top/bottom)
- * @todo forceValue
- * @todo Automatic width
- * @todo Expand title height to fit all elements
+ * @todo Locale support
+ * @todo Multiselect
+ * @todo Search
*/
$('.TreeDropdownField').entwine({
onmatch: function() {
- this.append(
- '' +
- '' +
- ''
- );
- if(this.data('title')) this.setTitle(this.data('title'));
- this.getPanel().hide();
-
- this._super();
+ this.append('');
},
getPanel: function() {
return this.find('.panel');
},
openPanel: function() {
- var panel = this.getPanel(), tree = this.find('.tree-holder');
+ var panel = this.getPanel();
panel.show();
- if(tree.is(':empty')) this.loadTree();
+ if(!panel.find('li').length) this.loadTree();
},
closePanel: function() {
this.getPanel().hide();
@@ -43,55 +25,40 @@
this[this.getPanel().is(':visible') ? 'closePanel' : 'openPanel']();
},
setTitle: function(title) {
- if(!title) title = strings.fieldTitle;
-
this.find('.title').text(title);
- this.data('title', title); // separate view from storage (important for search cancellation)
},
getTitle: function() {
return this.find('.title').text();
},
setValue: function(val) {
- this.find(':input:hidden').val(val);
+ this.find(':input').val(val);
},
getValue: function() {
- return this.find(':input:hidden').val();
+ return this.find(':input').val();
},
- 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;
+ loadTree: function() {
+ var self = this, treeHolder = $(this.getPanel()).find('.tree-holder');
+ this.addClass('loading');
+ treeHolder.load(this.attr('href'), {}, function(html, status, xhr) {
if(status == 'success') {
$(this)
.bind('loaded.jstree', function(e, data) {
var val = self.getValue();
if(val) data.inst.select_node(treeHolder.find('*[data-id=' + val + ']'));
- firstLoad = false;
- if(callback) callback.apply(self);
})
.jstree(self.getTreeConfig())
.bind('select_node.jstree', function(e, data) {
- var node = data.rslt.obj, id = $(node).data('id');
- if(self.getValue() == id) {
- self.setValue(null);
- self.setTitle(null);
- } else {
- self.setValue(id);
- self.setTitle(data.inst.get_text(node));
- }
-
- // Avoid auto-closing panel on first load
- if(!firstLoad) self.closePanel();
+ var node = data.rslt.obj;
+ self.setValue($(node).data('id'));
+ self.setTitle(data.inst.get_text(node));
+ self.closePanel();
});
}
- panel.removeClass('loading');
+ self.removeClass('loading');
});
},
getTreeConfig: function() {
- var self = this;
return {
'core': {
'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
'data': this.getPanel().find('.tree-holder').html(),
'ajax': {
- 'url': this.data('url-tree'),
+ 'url': this.attr('href'),
'data': function(node) {
- var id = $(node).data("id") ? $(node).data("id") : 0, params = self.getRequestParams();
- params = params.concat([{name: 'ID', value: id}, {name: 'ajax', value: 1}]);
- return params;
+ return { ID : $(node).data("id") ? $(node).data("id") : 0 , ajax: 1};
}
}
},
@@ -113,20 +78,11 @@
"select_limit" : 1,
'initially_select': [this.getPanel().find('.current').attr('id')]
},
+ 'themes': {
+ 'theme': 'apple'
+ },
'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({
@@ -134,103 +90,9 @@
return this.parents('.TreeDropdownField:first');
}
});
- $('.TreeDropdownField .toggle-panel-link, .TreeDropdownField span.title').entwine({
+ $('.TreeDropdownField .editLink').entwine({
onclick: function(e) {
this.getField().togglePanel();
- return false;
- }
- });
-
- $('.TreeDropdownField.searchable').entwine({
- onmatch: function() {
- this._super();
-
- var title = this.data('title');
- this.find('.title').replaceWith(
- $('')
- );
- 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);
}
});
});
diff --git a/javascript/TreeSelectorField.js b/javascript/TreeSelectorField.js
deleted file mode 100755
index 6a4403ead..000000000
--- a/javascript/TreeSelectorField.js
+++ /dev/null
@@ -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