You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by jk...@apache.org on 2006/09/29 05:43:06 UTC
svn commit: r451106 [24/40] - in /tapestry/tapestry4/trunk: ./
tapestry-framework/src/java/org/apache/tapestry/asset/
tapestry-framework/src/js/dojo/ tapestry-framework/src/js/dojo/src/
tapestry-framework/src/js/dojo/src/animation/ tapestry-framework/s...
Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/ComboBox.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/ComboBox.js?view=auto&rev=451106
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/ComboBox.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/ComboBox.js Thu Sep 28 20:42:39 2006
@@ -0,0 +1,820 @@
+/*
+ Copyright (c) 2004-2006, The Dojo Foundation
+ All Rights Reserved.
+
+ Licensed under the Academic Free License version 2.1 or above OR the
+ modified BSD license. For more information on Dojo licensing, see:
+
+ http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.widget.ComboBox");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.event.*");
+dojo.require("dojo.io.*");
+dojo.require("dojo.lfx.*");
+dojo.require("dojo.html.*");
+dojo.require("dojo.html.display");
+dojo.require("dojo.html.layout");
+dojo.require("dojo.html.iframe");
+dojo.require("dojo.string");
+dojo.require("dojo.widget.html.stabile");
+dojo.require("dojo.widget.PopupContainer");
+
+dojo.widget.incrementalComboBoxDataProvider = function(url, limit, timeout){
+ this.searchUrl = url;
+ this.inFlight = false;
+ this.activeRequest = null;
+ this.allowCache = false;
+
+ this.cache = {};
+
+ this.init = function(cbox){
+ this.searchUrl = cbox.dataUrl;
+ };
+
+ this.addToCache = function(keyword, data){
+ if(this.allowCache){
+ this.cache[keyword] = data;
+ }
+ };
+
+ this.startSearch = function(searchStr, type, ignoreLimit){
+ if(this.inFlight){
+ // FIXME: implement backoff!
+ }
+ var tss = encodeURIComponent(searchStr);
+ var realUrl = dojo.string.substituteParams(this.searchUrl, {"searchString": tss});
+ var _this = this;
+ var request = dojo.io.bind({
+ url: realUrl,
+ method: "get",
+ mimetype: "text/json",
+ load: function(type, data, evt){
+ _this.inFlight = false;
+ if(!dojo.lang.isArray(data)){
+ var arrData = [];
+ for(var key in data){
+ arrData.push([data[key], key]);
+ }
+ data = arrData;
+ }
+ _this.addToCache(searchStr, data);
+ _this.provideSearchResults(data);
+ }
+ });
+ this.inFlight = true;
+ };
+};
+
+dojo.widget.ComboBoxDataProvider = function(dataPairs, limit, timeout){
+ // NOTE: this data provider is designed as a naive reference
+ // implementation, and as such it is written more for readability than
+ // speed. A deployable data provider would implement lookups, search
+ // caching (and invalidation), and a significantly less naive data
+ // structure for storage of items.
+
+ this.data = [];
+ this.searchTimeout = timeout | 500;
+ this.searchLimit = limit | 30;
+ this.searchType = "STARTSTRING"; // may also be "STARTWORD" or "SUBSTRING"
+ this.caseSensitive = false;
+ // for caching optimizations
+ this._lastSearch = "";
+ this._lastSearchResults = null;
+
+ this.init = function(cbox, node){
+ if(!dojo.string.isBlank(cbox.dataUrl)){
+ this.getData(cbox.dataUrl);
+ }else{
+ // check to see if we can populate the list from <option> elements
+ if((node)&&(node.nodeName.toLowerCase() == "select")){
+ // NOTE: we're not handling <optgroup> here yet
+ var opts = node.getElementsByTagName("option");
+ var ol = opts.length;
+ var data = [];
+ for(var x=0; x<ol; x++){
+ var keyValArr = [String(opts[x].innerHTML), String(opts[x].value)];
+ data.push(keyValArr);
+ if(opts[x].selected){
+ cbox.setAllValues(keyValArr[0], keyValArr[1]);
+ }
+ }
+ this.setData(data);
+ }
+ }
+ };
+
+ this.getData = function(url){
+ dojo.io.bind({
+ url: url,
+ load: dojo.lang.hitch(this, function(type, data, evt){
+ if(!dojo.lang.isArray(data)){
+ var arrData = [];
+ for(var key in data){
+ arrData.push([data[key], key]);
+ }
+ data = arrData;
+ }
+ this.setData(data);
+ }),
+ mimetype: "text/json"
+ });
+ };
+
+ this.startSearch = function(searchStr, type, ignoreLimit){
+ // FIXME: need to add timeout handling here!!
+ this._preformSearch(searchStr, type, ignoreLimit);
+ };
+
+ this._preformSearch = function(searchStr, type, ignoreLimit){
+ //
+ // NOTE: this search is LINEAR, which means that it exhibits perhaps
+ // the worst possible speed characteristics of any search type. It's
+ // written this way to outline the responsibilities and interfaces for
+ // a search.
+ //
+ var st = type||this.searchType;
+ // FIXME: this is just an example search, which means that we implement
+ // only a linear search without any of the attendant (useful!) optimizations
+ var ret = [];
+ if(!this.caseSensitive){
+ searchStr = searchStr.toLowerCase();
+ }
+ for(var x=0; x<this.data.length; x++){
+ if((!ignoreLimit)&&(ret.length >= this.searchLimit)){
+ break;
+ }
+ // FIXME: we should avoid copies if possible!
+ var dataLabel = new String((!this.caseSensitive) ? this.data[x][0].toLowerCase() : this.data[x][0]);
+ if(dataLabel.length < searchStr.length){
+ // this won't ever be a good search, will it? What if we start
+ // to support regex search?
+ continue;
+ }
+
+ if(st == "STARTSTRING"){
+ if(searchStr == dataLabel.substr(0, searchStr.length)){
+ ret.push(this.data[x]);
+ }
+ }else if(st == "SUBSTRING"){
+ // this one is a gimmie
+ if(dataLabel.indexOf(searchStr) >= 0){
+ ret.push(this.data[x]);
+ }
+ }else if(st == "STARTWORD"){
+ // do a substring search and then attempt to determine if the
+ // preceeding char was the beginning of the string or a
+ // whitespace char.
+ var idx = dataLabel.indexOf(searchStr);
+ if(idx == 0){
+ // implicit match
+ ret.push(this.data[x]);
+ }
+ if(idx <= 0){
+ // if we didn't match or implicily matched, march onward
+ continue;
+ }
+ // otherwise, we have to go figure out if the match was at the
+ // start of a word...
+ // this code is taken almost directy from nWidgets
+ var matches = false;
+ while(idx!=-1){
+ // make sure the match either starts whole string, or
+ // follows a space, or follows some punctuation
+ if(" ,/(".indexOf(dataLabel.charAt(idx-1)) != -1){
+ // FIXME: what about tab chars?
+ matches = true; break;
+ }
+ idx = dataLabel.indexOf(searchStr, idx+1);
+ }
+ if(!matches){
+ continue;
+ }else{
+ ret.push(this.data[x]);
+ }
+ }
+ }
+ this.provideSearchResults(ret);
+ };
+
+ this.provideSearchResults = function(resultsDataPairs){
+ };
+
+ this.addData = function(pairs){
+ // FIXME: incredibly naive and slow!
+ this.data = this.data.concat(pairs);
+ };
+
+ this.setData = function(pdata){
+ // populate this.data and initialize lookup structures
+ this.data = pdata;
+ };
+
+ if(dataPairs){
+ this.setData(dataPairs);
+ }
+};
+
+dojo.widget.defineWidget(
+ "dojo.widget.ComboBox",
+ dojo.widget.HtmlWidget,
+ {
+ // Applies to any renderer
+ isContainer: false,
+
+ forceValidOption: false,
+ searchType: "stringstart",
+ dataProvider: null,
+
+ startSearch: function(searchString){},
+ selectNextResult: function(){},
+ selectPrevResult: function(){},
+ setSelectedResult: function(){},
+
+ // HTML specific stuff
+ autoComplete: true,
+ name: "", // clone in the name from the DOM node
+ textInputNode: null,
+ comboBoxValue: null,
+ comboBoxSelectionValue: null,
+ optionsListWrapper: null,
+ optionsListNode: null,
+ downArrowNode: null,
+ cbTableNode: null,
+ searchTimer: null,
+ searchDelay: 100,
+ dataUrl: "",
+ fadeTime: 200,
+ // maxListLength limits list to X visible rows, scroll on rest
+ maxListLength: 8,
+ // mode can also be "remote" for JSON-returning live search or "html" for
+ // dumber live search
+ mode: "local",
+ selectedResult: null,
+ _highlighted_option: null,
+ _prev_key_backspace: false,
+ _prev_key_esc: false,
+ _gotFocus: false,
+ _mouseover_list: false,
+ dataProviderClass: "dojo.widget.ComboBoxDataProvider",
+ buttonSrc: dojo.uri.dojoUri("src/widget/templates/images/combo_box_arrow.png"),
+
+ //the old implementation has builtin fade toggle, so we mimic it here
+ dropdownToggle: "fade",
+
+ templatePath: dojo.uri.dojoUri("src/widget/templates/ComboBox.html"),
+ templateCssPath: dojo.uri.dojoUri("src/widget/templates/ComboBox.css"),
+
+
+ setValue: function(value) {
+ this.comboBoxValue.value = value;
+ if (this.textInputNode.value != value) { // prevent mucking up of selection
+ this.textInputNode.value = value;
+ // only change state and value if a new value is set
+ dojo.widget.html.stabile.setState(this.widgetId, this.getState(), true);
+ this.onValueChanged(value);
+ }
+ },
+
+ // for user to override
+ onValueChanged: function(){ },
+
+ getValue: function() {
+ return this.comboBoxValue.value;
+ },
+
+ getState: function() {
+ return {value: this.getValue()};
+ },
+
+ setState: function(state) {
+ this.setValue(state.value);
+ },
+
+ getCaretPos: function(element){
+ // khtml 3.5.2 has selection* methods as does webkit nightlies from 2005-06-22
+ if(dojo.lang.isNumber(element.selectionStart)){
+ // FIXME: this is totally borked on Moz < 1.3. Any recourse?
+ return element.selectionStart;
+ }else if(dojo.render.html.ie){
+ // in the case of a mouse click in a popup being handled,
+ // then the document.selection is not the textarea, but the popup
+ // var r = document.selection.createRange();
+ // hack to get IE 6 to play nice. What a POS browser.
+ var tr = document.selection.createRange().duplicate();
+ var ntr = element.createTextRange();
+ tr.move("character",0);
+ ntr.move("character",0);
+ try {
+ // If control doesnt have focus, you get an exception.
+ // Seems to happen on reverse-tab, but can also happen on tab (seems to be a race condition - only happens sometimes).
+ // There appears to be no workaround for this - googled for quite a while.
+ ntr.setEndPoint("EndToEnd", tr);
+ return String(ntr.text).replace(/\r/g,"").length;
+ } catch (e) {
+ return 0; // If focus has shifted, 0 is fine for caret pos.
+ }
+
+ }
+ },
+
+ setCaretPos: function(element, location){
+ location = parseInt(location);
+ this.setSelectedRange(element, location, location);
+ },
+
+ setSelectedRange: function(element, start, end){
+ if(!end){ end = element.value.length; } // NOTE: Strange - should be able to put caret at start of text?
+ // Mozilla
+ // parts borrowed from http://www.faqts.com/knowledge_base/view.phtml/aid/13562/fid/130
+ if(element.setSelectionRange){
+ element.focus();
+ element.setSelectionRange(start, end);
+ }else if(element.createTextRange){ // IE
+ var range = element.createTextRange();
+ with(range){
+ collapse(true);
+ moveEnd('character', end);
+ moveStart('character', start);
+ select();
+ }
+ }else{ //otherwise try the event-creation hack (our own invention)
+ // do we need these?
+ element.value = element.value;
+ element.blur();
+ element.focus();
+ // figure out how far back to go
+ var dist = parseInt(element.value.length)-end;
+ var tchar = String.fromCharCode(37);
+ var tcc = tchar.charCodeAt(0);
+ for(var x = 0; x < dist; x++){
+ var te = document.createEvent("KeyEvents");
+ te.initKeyEvent("keypress", true, true, null, false, false, false, false, tcc, tcc);
+ element.dispatchEvent(te);
+ }
+ }
+ },
+
+ // does the keyboard related stuff
+ _handleKeyEvents: function(evt){
+ if(evt.ctrlKey || evt.altKey || !evt.key){ return; }
+
+ // reset these
+ this._prev_key_backspace = false;
+ this._prev_key_esc = false;
+
+ var k = dojo.event.browser.keys;
+ var doSearch = true;
+
+ switch(evt.key){
+ case k.KEY_DOWN_ARROW:
+ if(!this.popupWidget.isShowingNow){
+ this.startSearchFromInput();
+ }
+ this.highlightNextOption();
+ dojo.event.browser.stopEvent(evt);
+ return;
+ case k.KEY_UP_ARROW:
+ this.highlightPrevOption();
+ dojo.event.browser.stopEvent(evt);
+ return;
+ case k.KEY_TAB:
+ // using linux alike tab for autocomplete
+ if(!this.autoComplete && this.popupWidget.isShowingNow && this._highlighted_option){
+ dojo.event.browser.stopEvent(evt);
+ this.selectOption({ 'target': this._highlighted_option, 'noHide': false});
+
+ // put caret last
+ this.setSelectedRange(this.textInputNode, this.textInputNode.value.length, null);
+ }else{
+ this.selectOption();
+ return;
+ }
+ break;
+ case k.KEY_ENTER:
+ // prevent submitting form if we press enter with list open
+ if(this.popupWidget.isShowingNow){
+ dojo.event.browser.stopEvent(evt);
+ }
+ // fallthrough
+ case " ":
+ if(this.popupWidget.isShowingNow && this._highlighted_option){
+ dojo.event.browser.stopEvent(evt);
+ this.selectOption();
+ this.hideResultList();
+ return;
+ }
+ break;
+ case k.KEY_ESCAPE:
+ this.hideResultList();
+ this._prev_key_esc = true;
+ return;
+ case k.KEY_BACKSPACE:
+ this._prev_key_backspace = true;
+ if(!this.textInputNode.value.length){
+ this.setAllValues("", "");
+ this.hideResultList();
+ doSearch = false;
+ }
+ break;
+ case k.KEY_RIGHT_ARROW: // fall through
+ case k.KEY_LEFT_ARROW: // fall through
+ doSearch = false;
+ break;
+ default:// non char keys (F1-F12 etc..) shouldn't open list
+ if(evt.charCode==0){
+ doSearch = false;
+ }
+ }
+
+ if(this.searchTimer){
+ clearTimeout(this.searchTimer);
+ }
+ if(doSearch){
+ // if we have gotten this far we dont want to keep our highlight
+ this.blurOptionNode();
+
+ // need to wait a tad before start search so that the event bubbles through DOM and we have value visible
+ this.searchTimer = setTimeout(dojo.lang.hitch(this, this.startSearchFromInput), this.searchDelay);
+ }
+ },
+
+ // When inputting characters using an input method, such as Asian
+ // languages, it will generate this event instead of onKeyDown event
+ compositionEnd: function(evt){
+ evt.key = evt.keyCode;
+ this._handleKeyEvents(evt);
+ },
+
+ onKeyUp: function(evt){
+ this.setValue(this.textInputNode.value);
+ },
+
+ setSelectedValue: function(value){
+ // FIXME, not sure what to do here!
+ this.comboBoxSelectionValue.value = value;
+ },
+
+ setAllValues: function(value1, value2){
+ this.setValue(value1);
+ this.setSelectedValue(value2);
+ },
+
+ // does the actual highlight
+ focusOptionNode: function(node){
+ if(this._highlighted_option != node){
+ this.blurOptionNode();
+ this._highlighted_option = node;
+ dojo.html.addClass(this._highlighted_option, "dojoComboBoxItemHighlight");
+ }
+ },
+
+ // removes highlight on highlighted
+ blurOptionNode: function(){
+ if(this._highlighted_option){
+ dojo.html.removeClass(this._highlighted_option, "dojoComboBoxItemHighlight");
+ this._highlighted_option = null;
+ }
+ },
+
+ highlightNextOption: function(){
+ if((!this._highlighted_option) || !this._highlighted_option.parentNode){
+ this.focusOptionNode(this.optionsListNode.firstChild);
+ }else if(this._highlighted_option.nextSibling){
+ this.focusOptionNode(this._highlighted_option.nextSibling);
+ }
+ dojo.html.scrollIntoView(this._highlighted_option);
+ },
+
+ highlightPrevOption: function(){
+ if(this._highlighted_option && this._highlighted_option.previousSibling){
+ this.focusOptionNode(this._highlighted_option.previousSibling);
+ }else{
+ this._highlighted_option = null;
+ this.hideResultList();
+ return;
+ }
+ dojo.html.scrollIntoView(this._highlighted_option);
+ },
+
+ itemMouseOver: function(evt){
+ if (evt.target === this.optionsListNode) { return; }
+ this.focusOptionNode(evt.target);
+ dojo.html.addClass(this._highlighted_option, "dojoComboBoxItemHighlight");
+ },
+
+ itemMouseOut: function(evt){
+ if (evt.target === this.optionsListNode) { return; }
+ this.blurOptionNode();
+ },
+
+ // reset button size; this function is called when the input area has changed size
+ onResize: function(){
+ var inputSize = dojo.html.getBorderBox(this.textInputNode);
+ var buttonSize = { width: inputSize.height, height: inputSize.height};
+ dojo.html.setMarginBox(this.downArrowNode, buttonSize);
+ },
+
+ postMixInProperties: function(args, frag){
+ this.inherited("postMixInProperties", [args, frag]);
+
+ // set image size before instantiating template;
+ // changing it afterwards doesn't work on FF
+ var inputNode = this.getFragNodeRef(frag);
+ var inputSize = dojo.html.getBorderBox(inputNode);
+ this.initialButtonSize = inputSize.height + "px";
+ },
+
+ fillInTemplate: function(args, frag){
+ // For inlining a table we need browser specific CSS
+ dojo.html.applyBrowserClass(this.domNode);
+
+ var source = this.getFragNodeRef(frag);
+ if (! this.name && source.name){ this.name = source.name; }
+ this.comboBoxValue.name = this.name;
+ this.comboBoxSelectionValue.name = this.name+"_selected";
+
+ dojo.html.copyStyle(this.textInputNode, source);
+
+ var dpClass;
+ if(this.mode == "remote"){
+ dpClass = dojo.widget.incrementalComboBoxDataProvider;
+ }else if(typeof this.dataProviderClass == "string"){
+ dpClass = dojo.evalObjPath(this.dataProviderClass)
+ }else{
+ dpClass = this.dataProviderClass;
+ }
+ this.dataProvider = new dpClass();
+ this.dataProvider.init(this, this.getFragNodeRef(frag));
+
+ this.popupWidget = new dojo.widget.createWidget("PopupContainer",
+ {toggle: this.dropdownToggle, toggleDuration: this.toggleDuration});
+ dojo.event.connect(this, 'destroy', this.popupWidget, 'destroy');
+ this.optionsListNode = this.popupWidget.domNode;
+ this.domNode.appendChild(this.optionsListNode);
+ dojo.html.addClass(this.optionsListNode, 'dojoComboBoxOptions');
+ dojo.event.connect(this.optionsListNode, 'onclick', this, 'selectOption');
+ dojo.event.connect(this.optionsListNode, 'onmouseover', this, '_onMouseOver');
+ dojo.event.connect(this.optionsListNode, 'onmouseout', this, '_onMouseOut');
+
+ dojo.event.connect(this.optionsListNode, "onmouseover", this, "itemMouseOver");
+ dojo.event.connect(this.optionsListNode, "onmouseout", this, "itemMouseOut");
+ },
+
+ focus: function(){
+ // summary
+ // set focus to input node from code
+ this.tryFocus();
+ },
+
+ openResultList: function(results){
+ this.clearResultList();
+ if(!results.length){
+ this.hideResultList();
+ }
+
+ if( (this.autoComplete)&&
+ (results.length)&&
+ (!this._prev_key_backspace)&&
+ (this.textInputNode.value.length > 0)){
+ var cpos = this.getCaretPos(this.textInputNode);
+ // only try to extend if we added the last character at the end of the input
+ if((cpos+1) > this.textInputNode.value.length){
+ // only add to input node as we would overwrite Capitalisation of chars
+ this.textInputNode.value += results[0][0].substr(cpos);
+ // build a new range that has the distance from the earlier
+ // caret position to the end of the first string selected
+ this.setSelectedRange(this.textInputNode, cpos, this.textInputNode.value.length);
+ }
+ }
+
+ var even = true;
+ while(results.length){
+ var tr = results.shift();
+ if(tr){
+ var td = document.createElement("div");
+ td.appendChild(document.createTextNode(tr[0]));
+ td.setAttribute("resultName", tr[0]);
+ td.setAttribute("resultValue", tr[1]);
+ td.className = "dojoComboBoxItem "+((even) ? "dojoComboBoxItemEven" : "dojoComboBoxItemOdd");
+ even = (!even);
+ this.optionsListNode.appendChild(td);
+ }
+ }
+
+ // show our list (only if we have content, else nothing)
+ this.showResultList();
+ },
+
+ onFocusInput: function(){
+ this._hasFocus = true;
+ },
+
+ onBlurInput: function(){
+ this._hasFocus = false;
+ this._handleBlurTimer(true, 500);
+ },
+
+ // collect all blur timers issues here
+ _handleBlurTimer: function(/*Boolean*/clear, /*Number*/ millisec){
+ if(this.blurTimer && (clear || millisec)){
+ clearTimeout(this.blurTimer);
+ }
+ if(millisec){ // we ignore that zero is false and never sets as that never happens in this widget
+ this.blurTimer = dojo.lang.setTimeout(this, "checkBlurred", millisec);
+ }
+ },
+
+ // these 2 are needed in IE and Safari as inputTextNode loses focus when scrolling optionslist
+ _onMouseOver: function(evt){
+ if(!this._mouseover_list){
+ this._handleBlurTimer(true, 0);
+ this._mouseover_list = true;
+ }
+ },
+
+ _onMouseOut:function(evt){
+ var relTarget = evt.relatedTarget;
+ if(!relTarget || relTarget.parentNode!=this.optionsListNode){
+ this._mouseover_list = false;
+ this._handleBlurTimer(true, 100);
+ this.tryFocus();
+ }
+ },
+
+ _isInputEqualToResult: function(result){
+ var input = this.textInputNode.value;
+ if(!this.dataProvider.caseSensitive){
+ input = input.toLowerCase();
+ result = result.toLowerCase();
+ }
+ return (input == result);
+ },
+
+ _isValidOption: function(){
+ var tgt = dojo.html.firstElement(this.optionsListNode);
+ var isValidOption = false;
+ var tgt = dojo.html.firstElement(this.optionsListNode);
+ var isValidOption = false;
+ while(!isValidOption && tgt){
+ if(this._isInputEqualToResult(tgt.getAttribute("resultName"))){
+ isValidOption = true;
+ }else{
+ tgt = dojo.html.nextElement(tgt);
+ }
+ }
+ return isValidOption;
+ },
+
+ checkBlurred: function(){
+ if(!this._hasFocus && !this._mouseover_list){
+ this.hideResultList();
+ // clear the list if the user empties field and moves away.
+ if(!this.textInputNode.value.length){
+ this.setAllValues("", "");
+ return;
+ }
+
+ var isValidOption = this._isValidOption();
+ // enforce selection from option list
+ if(this.forceValidOption && !isValidOption){
+ this.setAllValues("", "");
+ return;
+ }
+ if(!isValidOption){// clear
+ this.setSelectedValue("");
+ }
+ }
+ },
+
+ sizeBackgroundIframe: function(){
+ var mb = dojo.html.getMarginBox(this.optionsListNode);
+ if( mb.width==0 || mb.height==0 ){
+ // need more time to calculate size
+ dojo.lang.setTimeout(this, "sizeBackgroundIframe", 100);
+ return;
+ }
+ },
+
+ selectOption: function(evt){
+ var tgt = null;
+ if(!evt){
+ evt = { target: this._highlighted_option };
+ }
+
+ if(!dojo.html.isDescendantOf(evt.target, this.optionsListNode)){
+ // handle autocompletion where the the user has hit ENTER or TAB
+
+ // if the input is empty do nothing
+ if(!this.textInputNode.value.length){
+ return;
+ }
+ tgt = dojo.html.firstElement(this.optionsListNode);
+
+ // user has input value not in option list
+ if(!tgt || !this._isInputEqualToResult(tgt.getAttribute("resultName"))){
+ return;
+ }
+ // otherwise the user has accepted the autocompleted value
+ }else{
+ tgt = evt.target;
+ }
+
+ while((tgt.nodeType!=1)||(!tgt.getAttribute("resultName"))){
+ tgt = tgt.parentNode;
+ if(tgt === dojo.body()){
+ return false;
+ }
+ }
+
+ this.textInputNode.value = tgt.getAttribute("resultName");
+ this.selectedResult = [tgt.getAttribute("resultName"), tgt.getAttribute("resultValue")];
+ this.setAllValues(tgt.getAttribute("resultName"), tgt.getAttribute("resultValue"));
+ if(!evt.noHide){
+ this.hideResultList();
+ this.setSelectedRange(this.textInputNode, 0, null);
+ }
+ this.tryFocus();
+ },
+
+ clearResultList: function(){
+ if(this.optionsListNode.innerHTML){
+ this.optionsListNode.innerHTML = ""; // browser natively knows how to collect this memory
+ }
+ },
+
+ hideResultList: function(){
+ this.popupWidget.close();
+ },
+
+ showResultList: function(){
+ // Our dear friend IE doesnt take max-height so we need to calculate that on our own every time
+ var childs = this.optionsListNode.childNodes;
+ if(childs.length){
+ var visibleCount = this.maxListLength;
+ if(childs.length < visibleCount){
+ visibleCount = childs.length;
+ }
+
+ with(this.optionsListNode.style)
+ {
+ display = "";
+ if(visibleCount == childs.length){
+ //no scrollbar is required, so unset height to let browser calcuate it,
+ //as in css, overflow is already set to auto
+ height = "";
+ }else{
+ //show it first to get the correct dojo.style.getOuterHeight(childs[0])
+ //FIXME: shall we cache the height of the item?
+ height = visibleCount * dojo.html.getMarginBox(childs[0]).height +"px";
+ }
+ width = (dojo.html.getMarginBox(this.domNode).width-2)+"px";
+
+ }
+ this.popupWidget.open(this.cbTableNode, this, this.downArrowNode);
+ }else{
+ this.hideResultList();
+ }
+ },
+
+ handleArrowClick: function(){
+ this._handleBlurTimer(true, 0);
+ this.tryFocus();
+ if(this.popupWidget.isShowingNow){
+ this.hideResultList();
+ }else{
+ // forces full population of results, if they click
+ // on the arrow it means they want to see more options
+ this.startSearch("");
+ }
+ },
+
+ tryFocus: function(){
+ try {
+ this.textInputNode.focus();
+ } catch (e) {
+ // element isn't focusable if disabled, or not visible etc - not easy to test for.
+ };
+ },
+
+ startSearchFromInput: function(){
+ this.startSearch(this.textInputNode.value);
+ },
+
+ postCreate: function(){
+ dojo.event.connect(this, "startSearch", this.dataProvider, "startSearch");
+ dojo.event.connect(this.dataProvider, "provideSearchResults", this, "openResultList");
+ dojo.event.connect(this.textInputNode, "onblur", this, "onBlurInput");
+ dojo.event.connect(this.textInputNode, "onfocus", this, "onFocusInput");
+
+ var s = dojo.widget.html.stabile.getState(this.widgetId);
+ if (s) {
+ this.setState(s);
+ }
+ }
+ }
+);
+
+
Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/ComboBox.js
------------------------------------------------------------------------------
svn:eol-style = native
Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/ContentPane.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/ContentPane.js?view=auto&rev=451106
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/ContentPane.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/ContentPane.js Thu Sep 28 20:42:39 2006
@@ -0,0 +1,600 @@
+/*
+ Copyright (c) 2004-2006, The Dojo Foundation
+ All Rights Reserved.
+
+ Licensed under the Academic Free License version 2.1 or above OR the
+ modified BSD license. For more information on Dojo licensing, see:
+
+ http://dojotoolkit.org/community/licensing.shtml
+*/
+
+// This widget doesn't do anything; is basically the same as <div>.
+// It's useful as a child of LayoutContainer, SplitContainer, or TabContainer.
+// But note that those classes can contain any widget as a child.
+
+dojo.provide("dojo.widget.ContentPane");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.io.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.string");
+dojo.require("dojo.string.extras");
+dojo.require("dojo.html.style");
+
+dojo.widget.defineWidget(
+ "dojo.widget.ContentPane",
+ dojo.widget.HtmlWidget,
+ function(){
+ // per widgetImpl variables
+ this._styleNodes = [];
+ this._onLoadStack = [];
+ this._onUnloadStack = [];
+ this._callOnUnLoad = false;
+ this.scriptScope; // undefined for now
+ this._ioBindObj;
+
+ // loading option
+ this.bindArgs = {}; // example bindArgs="preventCache:false;" overrides cacheContent
+ }, {
+ isContainer: true,
+
+ // loading options
+ adjustPaths: true, // fix relative paths in content to fit in this page
+ href: "", // only usable on construction, use setUrl or setContent after that
+ extractContent: true, // extract visible content from inside of <body> .... </body>
+ parseContent: true, // construct all widgets that is in content
+ cacheContent: true,
+ preload: false, // force load of data even if pane is hidden
+ refreshOnShow: false, // use with cacheContent: false
+ handler: "", // generate pane content from a java function
+ executeScripts: false, // if true scripts in content will be evaled after content is innerHTML'ed
+ scriptSeparation: true, // if false script eval in global scope
+ loadingMessage: "Loading...",
+
+ postCreate: function(args, frag, parentComp){
+ if (this.handler!==""){
+ this.setHandler(this.handler);
+ }
+ if(this.isShowing() || this.preload){
+ this.loadContents();
+ }
+ },
+
+ show: function(){
+ // if refreshOnShow is true, reload the contents every time; otherwise, load only the first time
+ if(this.refreshOnShow){
+ this.refresh();
+ }else{
+ this.loadContents();
+ }
+ dojo.widget.ContentPane.superclass.show.call(this);
+ },
+
+ refresh: function(){
+ this.isLoaded=false;
+ this.loadContents();
+ },
+
+ loadContents: function() {
+ if ( this.isLoaded ){
+ return;
+ }
+ if ( dojo.lang.isFunction(this.handler)) {
+ this._runHandler();
+ } else if ( this.href != "" ) {
+ this._downloadExternalContent(this.href, this.cacheContent && !this.refreshOnShow);
+ }
+ },
+
+ setUrl: function(/*String or dojo.uri.Uri*/ url) {
+ // summary:
+ // Reset the (external defined) content of this pane and replace with new url
+ this.href = url;
+ this.isLoaded = false;
+ if ( this.preload || this.isShowing() ){
+ this.loadContents();
+ }
+ },
+
+ abort: function(){
+ // summary
+ // abort download of content
+ var bind = this._ioBindObj;
+ if(!bind || !bind.abort){ return; }
+ bind.abort();
+ delete this._ioBindObj;
+ },
+
+ _downloadExternalContent: function(url, useCache) {
+ this.abort();
+ this._handleDefaults(this.loadingMessage, "onDownloadStart");
+ var self = this;
+ this._ioBindObj = dojo.io.bind(
+ this._cacheSetting({
+ url: url,
+ mimetype: "text/html",
+ handler: function(type, data, xhr){
+ delete self._ioBindObj; // makes sure abort doesnt clear cache
+ if(type=="load"){
+ self.onDownloadEnd.call(self, url, data);
+ }else{
+ // XHR isnt a normal JS object, IE doesnt have prototype on XHR so we cant extend it or shallowCopy it
+ var e = {
+ responseText: xhr.responseText,
+ status: xhr.status,
+ statusText: xhr.statusText,
+ responseHeaders: xhr.getAllResponseHeaders(),
+ text: "Error loading '" + url + "' (" + xhr.status + " "+ xhr.statusText + ")"
+ };
+ self._handleDefaults.call(self, e, "onDownloadError");
+ self.onLoad();
+ }
+ }
+ }, useCache)
+ );
+ },
+
+ _cacheSetting: function(bindObj, useCache){
+ for(var x in this.bindArgs){
+ if(dojo.lang.isUndefined(bindObj[x])){
+ bindObj[x] = this.bindArgs[x];
+ }
+ }
+
+ if(dojo.lang.isUndefined(bindObj.useCache)){ bindObj.useCache = useCache; }
+ if(dojo.lang.isUndefined(bindObj.preventCache)){ bindObj.preventCache = !useCache; }
+ if(dojo.lang.isUndefined(bindObj.mimetype)){ bindObj.mimetype = "text/html"; }
+ return bindObj;
+ },
+
+ // called when setContent is finished
+ onLoad: function(e){
+ this._runStack("_onLoadStack");
+ this.isLoaded=true;
+ },
+
+ // called before old content is cleared
+ onUnLoad: function(e){
+ this._runStack("_onUnloadStack");
+ delete this.scriptScope;
+ },
+
+ _runStack: function(stName){
+ var st = this[stName]; var err = "";
+ var scope = this.scriptScope || window;
+ for(var i = 0;i < st.length; i++){
+ try{
+ st[i].call(scope);
+ }catch(e){
+ err += "\n"+st[i]+" failed: "+e.description;
+ }
+ }
+ this[stName] = [];
+
+ if(err.length){
+ var name = (stName== "_onLoadStack") ? "addOnLoad" : "addOnUnLoad";
+ this._handleDefaults(name+" failure\n "+err, "onExecError", "debug");
+ }
+ },
+
+ addOnLoad: function(/*Function or Object ?*/ obj, /*Function*/ func){
+ // summary
+ // same as to dojo.addOnLoad but does not take "function_name" as a string
+ this._pushOnStack(this._onLoadStack, obj, func);
+ },
+
+ addOnUnload: function(/*Function or Object ?*/ obj, /*Function*/ func){
+ // summary
+ // same as to dojo.addUnOnUnload but does not take "function_name" as a string
+ this._pushOnStack(this._onUnloadStack, obj, func);
+ },
+
+ addOnUnLoad: function(){
+ dojo.deprecated(this.widgetType + ".addOnUnLoad, use addOnUnload instead. (lowercased Load)", 0.5);
+ this.addOnUnload.apply(this, arguments);
+ },
+
+ _pushOnStack: function(stack, obj, func){
+ if(typeof func == 'undefined') {
+ stack.push(obj);
+ }else{
+ stack.push(function(){ obj[func](); });
+ }
+ },
+
+ destroy: function(){
+ // make sure we call onUnLoad
+ this.onUnLoad();
+ dojo.widget.ContentPane.superclass.destroy.call(this);
+ },
+
+ // called when content script eval error or Java error occurs, preventDefault-able
+ onExecError: function(e){ /*stub*/ },
+
+ // called on DOM faults, require fault etc in content, preventDefault-able
+ onContentError: function(e){ /*stub*/ },
+
+ // called when download error occurs, preventDefault-able
+ onDownloadError: function(e){ /*stub*/ },
+
+ // called before download starts, preventDefault-able
+ onDownloadStart: function(e){ /*stub*/ },
+
+ // called when download is finished
+ onDownloadEnd: function(/*String*/ url, /*content*/ data){
+ data = this.splitAndFixPaths(data, url);
+ this.setContent(data);
+ },
+
+ // useful if user wants to prevent default behaviour ie: _setContent("Error...")
+ _handleDefaults: function(e, handler, messType){
+ if(!handler){ handler = "onContentError"; }
+
+ if(dojo.lang.isString(e)){ e = {text: e}; }
+
+ if(!e.text){ e.text = e.toString(); }
+
+ e.toString = function(){ return this.text; };
+
+ if(typeof e.returnValue != "boolean"){
+ e.returnValue = true;
+ }
+ if(typeof e.preventDefault != "function"){
+ e.preventDefault = function(){ this.returnValue = false; };
+ }
+ // call our handler
+ this[handler](e);
+ if(e.returnValue){
+ switch(messType){
+ case true: // fallthrough, old compat
+ case "alert":
+ alert(e.toString()); break;
+ case "debug":
+ dojo.debug(e.toString()); break;
+ default:
+ // makes sure scripts can clean up after themselves, before we setContent
+ if(this._callOnUnLoad){ this.onUnLoad(); }
+ // makes sure we dont try to call onUnLoad again on this event,
+ // ie onUnLoad before 'Loading...' but not before clearing 'Loading...'
+ this._callOnUnLoad = false;
+
+ // we might end up in a endless recursion here if domNode cant append content
+ if(arguments.callee._loopStop){
+ dojo.debug(e.toString());
+ }else{
+ arguments.callee._loopStop = true;
+ this._setContent(e.toString());
+ }
+ }
+ }
+ arguments.callee._loopStop = false;
+ },
+
+ // pathfixes, require calls, css stuff and neccesary content clean
+ splitAndFixPaths: function(/*String*/s, /*dojo.uri.Uri?*/url){
+ // summary:
+ // fixes all relative paths in (hopefully) all cases for example images, remote scripts, links etc.
+ // splits up content in different pieces, scripts, title, style, link and whats left becomes .xml
+
+ // init vars
+ var titles = [], scripts = [],tmp = [];
+ var match = [], requires = [], attr = [], styles = [];
+ var str = '', path = '', fix = '', tagFix = '', tag = '', origPath = '';
+
+ if(!url) { url = "./"; } // point to this page if not set
+
+ if(s){ // make sure we dont run regexes on empty content
+
+ /************** <title> ***********/
+ // khtml is picky about dom faults, you can't attach a <style> or <title> node as child of body
+ // must go into head, so we need to cut out those tags
+ var regex = /<title[^>]*>([\s\S]*?)<\/title>/i;
+ while(match = regex.exec(s)){
+ titles.push(match[1]);
+ s = s.substring(0, match.index) + s.substr(match.index + match[0].length);
+ };
+
+ /************** adjust paths *****************/
+ if(this.adjustPaths){
+ // attributepaths one tag can have multiple paths example:
+ // <input src="..." style="url(..)"/> or <a style="url(..)" href="..">
+ // strip out the tag and run fix on that.
+ // this guarantees that we won't run replace on another tag's attribute + it was easier do
+ var regexFindTag = /<[a-z][a-z0-9]*[^>]*\s(?:(?:src|href|style)=[^>])+[^>]*>/i;
+ var regexFindAttr = /\s(src|href|style)=(['"]?)([\w()\[\]\/.,\\'"-:;#=&?\s@]+?)\2/i;
+ // these are the supported protocols, all other is considered relative
+ var regexProtocols = /^(?:[#]|(?:(?:https?|ftps?|file|javascript|mailto|news):))/;
+
+ while(tag = regexFindTag.exec(s)){
+ str += s.substring(0, tag.index);
+ s = s.substring((tag.index + tag[0].length), s.length);
+ tag = tag[0];
+
+ // loop through attributes
+ tagFix = '';
+ while(attr = regexFindAttr.exec(tag)){
+ path = ""; origPath = attr[3];
+ switch(attr[1].toLowerCase()){
+ case "src":// falltrough
+ case "href":
+ if(regexProtocols.exec(origPath)){
+ path = origPath;
+ } else {
+ path = (new dojo.uri.Uri(url, origPath).toString());
+ }
+ break;
+ case "style":// style
+ path = dojo.html.fixPathsInCssText(origPath, url);
+ break;
+ default:
+ path = origPath;
+ }
+ fix = " " + attr[1] + "=" + attr[2] + path + attr[2];
+ // slices up tag before next attribute check
+ tagFix += tag.substring(0, attr.index) + fix;
+ tag = tag.substring((attr.index + attr[0].length), tag.length);
+ }
+ str += tagFix + tag; //dojo.debug(tagFix + tag);
+ }
+ s = str+s;
+ }
+
+ /**************** cut out all <style> and <link rel="stylesheet" href=".."> **************/
+ regex = /(?:<(style)[^>]*>([\s\S]*?)<\/style>|<link ([^>]*rel=['"]?stylesheet['"]?[^>]*)>)/i;
+ while(match = regex.exec(s)){
+ if(match[1] && match[1].toLowerCase() == "style"){
+ styles.push(dojo.html.fixPathsInCssText(match[2],url));
+ }else if(attr = match[3].match(/href=(['"]?)([^'">]*)\1/i)){
+ styles.push({path: attr[2]});
+ }
+ s = s.substring(0, match.index) + s.substr(match.index + match[0].length);
+ };
+
+ /***************** cut out all <script> tags, push them into scripts array ***************/
+ var regex = /<script([^>]*)>([\s\S]*?)<\/script>/i;
+ var regexSrc = /src=(['"]?)([^"']*)\1/i;
+ var regexDojoJs = /.*(\bdojo\b\.js(?:\.uncompressed\.js)?)$/;
+ var regexInvalid = /(?:var )?\bdjConfig\b(?:[\s]*=[\s]*\{[^}]+\}|\.[\w]*[\s]*=[\s]*[^;\n]*)?;?|dojo\.hostenv\.writeIncludes\(\s*\);?/g;
+ var regexRequires = /dojo\.(?:(?:require(?:After)?(?:If)?)|(?:widget\.(?:manager\.)?registerWidgetPackage)|(?:(?:hostenv\.)?setModulePrefix|registerModulePath)|defineNamespace)\((['"]).*?\1\)\s*;?/;
+
+ while(match = regex.exec(s)){
+ if(this.executeScripts && match[1]){
+ if(attr = regexSrc.exec(match[1])){
+ // remove a dojo.js or dojo.js.uncompressed.js from remoteScripts
+ // we declare all files named dojo.js as bad, regardless of path
+ if(regexDojoJs.exec(attr[2])){
+ dojo.debug("Security note! inhibit:"+attr[2]+" from being loaded again.");
+ }else{
+ scripts.push({path: attr[2]});
+ }
+ }
+ }
+ if(match[2]){
+ // remove all invalid variables etc like djConfig and dojo.hostenv.writeIncludes()
+ var sc = match[2].replace(regexInvalid, "");
+ if(!sc){ continue; }
+
+ // cut out all dojo.require (...) calls, if we have execute
+ // scripts false widgets dont get there require calls
+ // takes out possible widgetpackage registration as well
+ while(tmp = regexRequires.exec(sc)){
+ requires.push(tmp[0]);
+ sc = sc.substring(0, tmp.index) + sc.substr(tmp.index + tmp[0].length);
+ }
+ if(this.executeScripts){
+ scripts.push(sc);
+ }
+ }
+ s = s.substr(0, match.index) + s.substr(match.index + match[0].length);
+ }
+
+ /********* extract content *********/
+ if(this.extractContent){
+ match = s.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
+ if(match) { s = match[1]; }
+ }
+
+ /*** replace scriptScope prefix in html Event handler
+ * working order: find tags with scriptScope in a tag attribute
+ * then replace all standalone scriptScope occurencies with reference to to this widget
+ * valid onClick="scriptScope.func()" or onClick="scriptScope['func']();scriptScope.i++"
+ * not valid onClick="var.scriptScope.ref" nor onClick="var['scriptScope'].ref" */
+ if(this.executeScripts && this.scriptSeparation){
+ var regex = /(<[a-zA-Z][a-zA-Z0-9]*\s[^>]*?\S=)((['"])[^>]*scriptScope[^>]*>)/;
+ var regexAttr = /([\s'";:\(])scriptScope(.*)/; // we rely on that attribute begins ' or "
+ str = "";
+ while(tag = regex.exec(s)){
+ tmp = ((tag[3]=="'") ? '"': "'");fix= "";
+ str += s.substring(0, tag.index) + tag[1];
+ while(attr = regexAttr.exec(tag[2])){
+ tag[2] = tag[2].substring(0, attr.index) + attr[1] + "dojo.widget.byId("+ tmp + this.widgetId + tmp + ").scriptScope" + attr[2];
+ }
+ str += tag[2];
+ s = s.substr(tag.index + tag[0].length);
+ }
+ s = str + s;
+ }
+ }
+
+ return {"xml": s, // Object
+ "styles": styles,
+ "titles": titles,
+ "requires": requires,
+ "scripts": scripts,
+ "url": url};
+ },
+
+
+ _setContent: function(cont){
+ this.destroyChildren();
+
+ // remove old stylenodes from HEAD
+ for(var i = 0; i < this._styleNodes.length; i++){
+ if(this._styleNodes[i] && this._styleNodes[i].parentNode){
+ this._styleNodes[i].parentNode.removeChild(this._styleNodes[i]);
+ }
+ }
+ this._styleNodes = [];
+
+ var node = this.containerNode || this.domNode;
+ while(node.firstChild){
+ try{
+ dojo.event.browser.clean(node.firstChild);
+ }catch(e){}
+ node.removeChild(node.firstChild);
+ }
+ try{
+ if(typeof cont != "string"){
+ node.innerHTML = "";
+ node.appendChild(cont);
+ }else{
+ node.innerHTML = cont;
+ }
+ }catch(e){
+ e.text = "Couldn't load content:"+e.description;
+ this._handleDefaults(e, "onContentError");
+ }
+ },
+
+ setContent: function(/*String or DOMNode*/ data){
+ // summary:
+ // Destroys old content and sets new content, and possibly initialize any widgets within 'data'
+ this.abort();
+ if(this._callOnUnLoad){ this.onUnLoad(); }// this tells a remote script clean up after itself
+ this._callOnUnLoad = true;
+
+ if(!data || dojo.html.isNode(data)){
+ // if we do a clean using setContent(""); or setContent(#node) bypass all parsing, extractContent etc
+ this._setContent(data);
+ this.onResized();
+ this.onLoad();
+ }else{
+ // need to run splitAndFixPaths? ie. manually setting content
+ // adjustPaths is taken care of inside splitAndFixPaths
+ if(typeof data.xml != "string"){
+ this.href = ""; // so we can refresh safely
+ data = this.splitAndFixPaths(data);
+ }
+
+ this._setContent(data.xml);
+
+ // insert styles from content (in same order they came in)
+ for(var i = 0; i < data.styles.length; i++){
+ if(data.styles[i].path){
+ this._styleNodes.push(dojo.html.insertCssFile(data.styles[i].path));
+ }else{
+ this._styleNodes.push(dojo.html.insertCssText(data.styles[i]));
+ }
+ }
+
+ if(this.parseContent){
+ for(var i = 0; i < data.requires.length; i++){
+ try{
+ eval(data.requires[i]);dojo.debug(data.requires[i]);
+ } catch(e){
+ e.text = "ContentPane: error in package loading calls, " + (e.description||e);
+ this._handleDefaults(e, "onContentError", "debug");
+ }
+ }
+ }
+ // need to allow async load, Xdomain uses it
+ // is inline function because we cant send args to dojo.addOnLoad
+ var _self = this;
+ function asyncParse(){
+ if(_self.executeScripts){
+ _self._executeScripts(data.scripts);
+ }
+
+ if(_self.parseContent){
+ var node = _self.containerNode || _self.domNode;
+ var parser = new dojo.xml.Parse();
+ var frag = parser.parseElement(node, null, true);
+ // createSubComponents not createComponents because frag has already been created
+ dojo.widget.getParser().createSubComponents(frag, _self);
+ }
+
+ _self.onResized();
+ _self.onLoad();
+ }
+ // try as long as possible to make setContent sync call
+ if(dojo.hostenv.isXDomain && data.requires.length){
+ dojo.addOnLoad(asyncParse);
+ }else{
+ asyncParse();
+ }
+ }
+ },
+
+ // Generate pane content from given java function
+ setHandler: function(handler) {
+ var fcn = dojo.lang.isFunction(handler) ? handler : window[handler];
+ if(!dojo.lang.isFunction(fcn)) {
+ // FIXME: needs testing! somebody with java knowledge needs to try this
+ this._handleDefaults("Unable to set handler, '" + handler + "' not a function.", "onExecError", true);
+ return;
+ }
+ this.handler = function() {
+ return fcn.apply(this, arguments);
+ }
+ },
+
+ _runHandler: function() {
+ var ret = true;
+ if(dojo.lang.isFunction(this.handler)) {
+ this.handler(this, this.domNode);
+ ret = false;
+ }
+ this.onLoad();
+ return ret;
+ },
+
+ _executeScripts: function(scripts) {
+ // loop through the scripts in the order they came in
+ var self = this;
+ var tmp = "", code = "";
+ for(var i = 0; i < scripts.length; i++){
+ if(scripts[i].path){ // remotescript
+ dojo.io.bind(this._cacheSetting({
+ "url": scripts[i].path,
+ "load": function(type, scriptStr){
+ dojo.lang.hitch(self, tmp = ";"+scriptStr);
+ },
+ "error": function(type, error){
+ error.text = type + " downloading remote script";
+ self._handleDefaults.call(self, error, "onExecError", "debug");
+ },
+ "mimetype": "text/plain",
+ "sync": true
+ }, this.cacheContent));
+ code += tmp;
+ }else{
+ code += scripts[i];
+ }
+ }
+
+
+ try{
+ if(this.scriptSeparation){
+ // initialize a new anonymous container for our script, dont make it part of this widgets scope chain
+ // instead send in a variable that points to this widget, useful to connect events to onLoad, onUnLoad etc..
+ delete this.scriptScope;
+ this.scriptScope = new (new Function('_container_', code+'; return this;'))(self);
+ }else{
+ // exec in global, lose the _container_ feature
+ var djg = dojo.global();
+ if(djg.execScript){
+ djg.execScript(code);
+ }else{
+ var djd = dojo.doc();
+ var sc = djd.createElement("script");
+ sc.appendChild(djd.createTextNode(code));
+ (this.containerNode||this.domNode).appendChild(sc);
+ }
+ }
+ }catch(e){
+ e.text = "Error running scripts from content:\n"+e.description;
+ this._handleDefaults(e, "onExecError", "debug");
+ }
+ }
+ }
+);
Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/ContentPane.js
------------------------------------------------------------------------------
svn:eol-style = native
Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/DatePicker.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/DatePicker.js?view=auto&rev=451106
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/DatePicker.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/DatePicker.js Thu Sep 28 20:42:39 2006
@@ -0,0 +1,470 @@
+/*
+ Copyright (c) 2004-2006, The Dojo Foundation
+ All Rights Reserved.
+
+ Licensed under the Academic Free License version 2.1 or above OR the
+ modified BSD license. For more information on Dojo licensing, see:
+
+ http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.widget.DatePicker");
+dojo.require("dojo.date.common");
+dojo.require("dojo.date.format");
+dojo.require("dojo.date.serialize");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.event.*");
+dojo.require("dojo.dom");
+dojo.require("dojo.html.style");
+
+dojo.widget.defineWidget(
+ "dojo.widget.DatePicker",
+ dojo.widget.HtmlWidget,
+ {
+ /*
+ summary:
+ Base class for a stand-alone DatePicker widget
+ that makes it easy to select a date, or switch by month and/or year.
+ description:
+ A stand-alone DatePicker widget that makes it
+ easy to select a date, or increment by week, month, and/or year.
+ It is designed to be used on its own, or inside of other widgets to
+ create drop down DatePickers or other similar combination widgets.
+ To get a sense of what month to highlight, we initialize on
+ the first Saturday of each month, since that will be either the first
+ of two or the second of three months being partially displayed, and
+ then work forwards and backwards from that point.
+ Dates are passed as Date objects or in the `RFC 3339` format
+ http://www.faqs.org/rfcs/rfc3339.html (2005-06-30T08:05:00-07:00),
+ so that they are serializable and locale-independent.
+
+ usage:
+ var datePicker = dojo.widget.createWidget("DatePicker", {},
+ dojo.byId("datePickerNode"));
+
+ <div dojoType="DatePicker"></div>
+ */
+
+ //start attributes
+
+ //total weeks to display default
+ displayWeeks: 6,
+ //if true, weekly size of calendar changes to acomodate the month if false, 42 day format is used
+ adjustWeeks: false,
+ //first available date in the calendar set
+ startDate: "1492-10-12",
+ //last available date in the calendar set
+ endDate: "2941-10-12",
+ //adjusts the first day of the week 0==Sunday..6==Saturday
+ weekStartsOn: "",
+ //current date selected by DatePicker in rfc 3339 date format "yyyy-MM-dd" -- once initialized, this.date will be a date Object
+ date: "",
+ storedDate: "", //deprecated use date instead
+ //disable all incremental controls, must pick a date in the current display
+ staticDisplay: false,
+
+ //how to render the names of the days in the header. see dojo.date.getDayNames
+ dayWidth: 'narrow',
+ classNames: {
+ // summary:
+ // stores a list of class names that may be overriden
+ previous: "previousMonth",
+ disabledPrevious: "previousMonthDisabled",
+ current: "currentMonth",
+ disabledCurrent: "currentMonthDisabled",
+ next: "nextMonth",
+ disabledNext: "nextMonthDisabled",
+ currentDate: "currentDate",
+ selectedDate: "selectedItem"
+ },
+ templatePath: dojo.uri.dojoUri("src/widget/templates/DatePicker.html"),
+ templateCssPath: dojo.uri.dojoUri("src/widget/templates/DatePicker.css"),
+
+ postMixInProperties: function(){
+ // summary: see dojo.widget.DomWidget
+
+ dojo.widget.DatePicker.superclass.postMixInProperties.apply(this, arguments);
+ if(this.storedDate){
+ dojo.deprecated("dojo.widget.DatePicker", "use 'date' instead of 'storedDate'", "0.5");
+ this.date=this.storedDate;
+ }
+ this.startDate = dojo.date.fromRfc3339(this.startDate);
+ this.endDate = dojo.date.fromRfc3339(this.endDate);
+ this.startDate.setHours(0,0,0,0); //adjust startDate to be exactly midnight
+ this.endDate.setHours(24,0,0,-1); //adjusting endDate to be a fraction of a second before midnight
+ if(!this.weekStartsOn){
+ this.weekStartsOn=dojo.date.getFirstDayOfWeek(this.lang);
+ }
+ this.today = new Date();
+ this.today.setHours(0,0,0,0);
+ if(this.date && (typeof this.date=="string") && (this.date.split("-").length > 2)) {
+ this.date = dojo.date.fromRfc3339(this.date);
+ this.date.setHours(0,0,0,0);
+ }
+ },
+
+ fillInTemplate: function(args, frag) {
+ // summary: see dojo.widget.DomWidget
+
+ dojo.widget.DatePicker.superclass.fillInTemplate.apply(this, arguments);
+
+ // Copy style info from input node to output node
+ var source = this.getFragNodeRef(frag);
+ dojo.html.copyStyle(this.domNode, source);
+
+ this.weekTemplate = dojo.dom.removeNode(this.calendarWeekTemplate);
+ this._preInitUI((this.date)?this.date:this.today,false,true); //init UI with date selected ONLY if user supplies one
+
+ // Insert localized day names in the template
+ var dayLabels = dojo.lang.unnest(dojo.date.getNames('days', this.dayWidth, 'standAlone', this.lang)); //if we dont use unnest, we risk modifying the dayLabels array inside of dojo.date and screwing up other calendars on the page
+ if(this.weekStartsOn > 0){
+ //adjust dayLabels for different first day of week. ie: Monday or Thursday instead of Sunday
+ for(var i=0;i<this.weekStartsOn;i++){
+ dayLabels.push(dayLabels.shift());
+ }
+ }
+ var dayLabelNodes = this.dayLabelsRow.getElementsByTagName("td");
+ for(i=0; i<7; i++) {
+ dayLabelNodes.item(i).innerHTML = dayLabels[i];
+ }
+ },
+
+ getValue: function() {
+ // summary: return current date in RFC 3339 format
+ return dojo.date.toRfc3339(new Date(this.date),'dateOnly'); /*String*/
+ },
+
+ getDate: function() {
+ // summary: return current date as a Date object
+ return this.date; /*Date*/
+ },
+
+ setValue: function(/*Date|String*/rfcDate) {
+ //summary: set the current date from RFC 3339 formatted string or a date object, synonymous with setDate
+ this.setDate(rfcDate);
+ },
+
+ setDate: function(/*Date|String*/dateObj) {
+ //summary: set the current date and update the UI
+ if(typeof dateObj=="string"){
+ this.date = dojo.date.fromRfc3339(dateObj);
+ }else{
+ this.date = new Date(dateObj);
+ }
+ this.date.setHours(0,0,0,0);
+ if(this.selectedNode!=null){
+ dojo.html.removeClass(this.selectedNode,this.classNames.selectedDate);
+ }
+ if(this.clickedNode!=null){
+ dojo.html.addClass(this.clickedNode,this.classNames.selectedDate);
+ this.selectedNode = this.clickedNode;
+ }else{
+ //only call this if setDate was called by means other than clicking a date
+ this._preInitUI(this.date,false,true);
+ }
+ this.clickedNode=null;
+ this.onSetDate();
+ },
+
+ _preInitUI: function(dateObj,initFirst,initUI) {
+ //initFirst is to tell _initFirstDay if you want first day of the displayed calendar, or first day of the week for dateObj
+ //initUI tells preInitUI to go ahead and run initUI if set to true
+ this.firstDay = this._initFirstDay(dateObj,initFirst,false);
+ this.selectedIsUsed = false;
+ this.currentIsUsed = false;
+ var nextDate = new Date(this.firstDay);
+ var tmpMonth = nextDate.getMonth();
+ this.curMonth = new Date(nextDate);
+ this.curMonth.setDate(nextDate.getDate()+6); //first saturday gives us the current Month
+ this.curMonth.setDate(1);
+ if(this.displayWeeks=="" || this.adjustWeeks){
+ this.adjustWeeks = true;
+ this.displayWeeks = Math.ceil((dojo.date.getDaysInMonth(this.curMonth) + this._getAdjustedDay(this.curMonth))/7);
+ }
+ var days = this.displayWeeks*7; //init total days to display
+ if(dojo.date.diff(this.startDate,this.endDate, dojo.date.dateParts.DAY) < days){
+ this.staticDisplay = true;
+ if(dojo.date.diff(nextDate,this.endDate, dojo.date.dateParts.DAY) > days){
+ this._preInitUI(this.startDate,true,false);
+ nextDate = new Date(this.firstDay);
+ }
+ this.curMonth = new Date(nextDate);
+ this.curMonth.setDate(nextDate.getDate()+6);
+ this.curMonth.setDate(1);
+ var curClass = (nextDate.getMonth() == this.curMonth.getMonth())?'current':'previous';
+ }
+ if(initUI){
+ this._initUI(days);
+ }
+ },
+ _initUI: function(days) {
+ dojo.dom.removeChildren(this.calendarDatesContainerNode);
+ for(var i=0;i<this.displayWeeks;i++){
+ this.calendarDatesContainerNode.appendChild(this.weekTemplate.cloneNode(true));
+ }
+
+ var nextDate = new Date(this.firstDay);
+ this._setMonthLabel(this.curMonth.getMonth());
+ this._setYearLabels(this.curMonth.getFullYear());
+ var calendarNodes = this.calendarDatesContainerNode.getElementsByTagName("td");
+ var calendarRows = this.calendarDatesContainerNode.getElementsByTagName("tr");
+ var currentCalendarNode;
+ for(i=0;i<days;i++){
+ //this is our new UI loop... one loop to rule them all, and in the datepicker bind them
+ currentCalendarNode = calendarNodes.item(i);
+ currentCalendarNode.innerHTML = nextDate.getDate();
+ var curClass = (nextDate.getMonth() == this.curMonth.getMonth())? 'current':'previous';
+ var mappedClass = curClass;
+ if(this._isDisabledDate(nextDate)){
+ var classMap={previous:"disabledPrevious",current:"disabledCurrent",next:"disabledNext"};
+ mappedClass=classMap[curClass];
+ }
+ dojo.html.setClass(currentCalendarNode, this._getDateClassName(nextDate, mappedClass));
+ if(dojo.html.hasClass(currentCalendarNode,this.classNames.selectedDate)){
+ this.selectedNode = currentCalendarNode;
+ }
+ nextDate = dojo.date.add(nextDate, dojo.date.dateParts.DAY, 1);
+ }
+ this.lastDay = dojo.date.add(nextDate,dojo.date.dateParts.DAY,-1);
+ this._initControls();
+ },
+ _initControls: function(){
+ var d = this.firstDay;
+ var d2 = this.lastDay;
+ var decWeek, incWeek, decMonth, incMonth, decYear, incYear;
+ decWeek = incWeek = decMonth = incMonth = decYear = incYear = !this.staticDisplay;
+ with(dojo.date.dateParts){
+ var add = dojo.date.add;
+ if(decWeek && add(d,DAY,(-1*(this._getAdjustedDay(d)+1)))<this.startDate){
+ decWeek = decMonth = decYear = false;
+ }
+ if(incWeek && d2>this.endDate){
+ incWeek = incMonth = incYear = false;
+ }
+ if(decMonth && add(d,DAY,-1)<this.startDate){
+ decMonth = decYear = false;
+ }
+ if(incMonth && add(d2,DAY,1)>this.endDate){
+ incMonth = incYear = false;
+ }
+ if(decYear && add(d2,YEAR,-1)<this.startDate){
+ decYear = false;
+ }
+ if(incYear && add(d,YEAR,1)>this.endDate){
+ incYear = false;
+ }
+ }
+
+ function enableControl(node, enabled){
+ dojo.html.setVisibility(node, enabled ? '' : 'hidden');
+ }
+ enableControl(this.decreaseWeekNode,decWeek);
+ enableControl(this.increaseWeekNode,incWeek);
+ enableControl(this.decreaseMonthNode,decMonth);
+ enableControl(this.increaseMonthNode,incMonth);
+ enableControl(this.previousYearLabelNode,decYear);
+ enableControl(this.nextYearLabelNode,incYear);
+ },
+
+ _incrementWeek: function(evt) {
+ var d = new Date(this.firstDay);
+ switch(evt.target) {
+ case this.increaseWeekNode.getElementsByTagName("img").item(0):
+ case this.increaseWeekNode:
+ var tmpDate = dojo.date.add(d, dojo.date.dateParts.WEEK, 1);
+ if(tmpDate < this.endDate){
+ d = dojo.date.add(d, dojo.date.dateParts.WEEK, 1);
+ }
+ break;
+ case this.decreaseWeekNode.getElementsByTagName("img").item(0):
+ case this.decreaseWeekNode:
+ if(d >= this.startDate){
+ d = dojo.date.add(d, dojo.date.dateParts.WEEK, -1);
+ }
+ break;
+ }
+ this._preInitUI(d,true,true);
+ },
+
+ _incrementMonth: function(evt) {
+ var d = new Date(this.curMonth);
+ var tmpDate = new Date(this.firstDay);
+ switch(evt.currentTarget) {
+ case this.increaseMonthNode.getElementsByTagName("img").item(0):
+ case this.increaseMonthNode:
+ tmpDate = dojo.date.add(tmpDate, dojo.date.dateParts.DAY, this.displayWeeks*7);
+ if(tmpDate < this.endDate){
+ d = dojo.date.add(d, dojo.date.dateParts.MONTH, 1);
+ }else{
+ var revertToEndDate = true;
+ }
+ break;
+ case this.decreaseMonthNode.getElementsByTagName("img").item(0):
+ case this.decreaseMonthNode:
+ if(tmpDate > this.startDate){
+ d = dojo.date.add(d, dojo.date.dateParts.MONTH, -1);
+ }else{
+ var revertToStartDate = true;
+ }
+ break;
+ }
+ if(revertToStartDate){
+ d = new Date(this.startDate);
+ }else if(revertToEndDate){
+ d = new Date(this.endDate);
+ }
+ this._preInitUI(d,false,true);
+ },
+
+ _incrementYear: function(evt) {
+ var year = this.curMonth.getFullYear();
+ var tmpDate = new Date(this.firstDay);
+ switch(evt.target) {
+ case this.nextYearLabelNode:
+ tmpDate = dojo.date.add(tmpDate, dojo.date.dateParts.YEAR, 1);
+ if(tmpDate<this.endDate){
+ year++;
+ }else{
+ var revertToEndDate = true;
+ }
+ break;
+ case this.previousYearLabelNode:
+ tmpDate = dojo.date.add(tmpDate, dojo.date.dateParts.YEAR, -1);
+ if(tmpDate>this.startDate){
+ year--;
+ }else{
+ var revertToStartDate = true;
+ }
+ break;
+ }
+ var d;
+ if(revertToStartDate){
+ d = new Date(this.startDate);
+ }else if(revertToEndDate){
+ d = new Date(this.endDate);
+ }else{
+ d = new Date(year, this.curMonth.getMonth(), 1);
+ }
+ this._preInitUI(d,false,true);
+ },
+
+ onIncrementWeek: function(/*Event*/evt) {
+ // summary: handler for increment week event
+ evt.stopPropagation();
+ if(!this.staticDisplay){
+ this._incrementWeek(evt);
+ }
+ },
+
+ onIncrementMonth: function(/*Event*/evt) {
+ // summary: handler for increment month event
+ evt.stopPropagation();
+ if(!this.staticDisplay){
+ this._incrementMonth(evt);
+ }
+ },
+
+ onIncrementYear: function(/*Event*/evt) {
+ // summary: handler for increment year event
+ evt.stopPropagation();
+ if(!this.staticDisplay){
+ this._incrementYear(evt);
+ }
+ },
+
+ _setMonthLabel: function(monthIndex) {
+ this.monthLabelNode.innerHTML = dojo.date.getNames('months', 'wide', 'standAlone', this.lang)[monthIndex];
+ },
+
+ _setYearLabels: function(year) {
+ var y = year - 1;
+ var that = this;
+ function f(n){
+ that[n+"YearLabelNode"].innerHTML =
+ dojo.date.format(new Date(y++, 0), {formatLength:'yearOnly', locale:that.lang});
+ }
+ f("previous");
+ f("current");
+ f("next");
+ },
+
+ _getDateClassName: function(date, monthState) {
+ var currentClassName = this.classNames[monthState];
+ //we use Number comparisons because 2 dateObjects never seem to equal each other otherwise
+ if ((!this.selectedIsUsed && this.date) && (Number(date) == Number(this.date))) {
+ currentClassName = this.classNames.selectedDate + " " + currentClassName;
+ this.selectedIsUsed = true;
+ }
+ if((!this.currentIsUsed) && (Number(date) == Number(this.today))) {
+ currentClassName = currentClassName + " " + this.classNames.currentDate;
+ this.currentIsUsed = true;
+ }
+ return currentClassName;
+ },
+
+ onClick: function(/*Event*/evt) {
+ //summary: the click event handler
+ dojo.event.browser.stopEvent(evt);
+ },
+
+ _handleUiClick: function(/*Event*/evt) {
+ var eventTarget = evt.target;
+ if(eventTarget.nodeType != dojo.dom.ELEMENT_NODE){eventTarget = eventTarget.parentNode;}
+ dojo.event.browser.stopEvent(evt);
+ this.selectedIsUsed = this.todayIsUsed = false;
+ var month = this.curMonth.getMonth();
+ var year = this.curMonth.getFullYear();
+ if(dojo.html.hasClass(eventTarget, this.classNames["disabledPrevious"])||dojo.html.hasClass(eventTarget, this.classNames["disabledCurrent"])||dojo.html.hasClass(eventTarget, this.classNames["disabledNext"])){
+ return; //this date is disabled... ignore it
+ }else if (dojo.html.hasClass(eventTarget, this.classNames["next"])) {
+ month = ++month % 12;
+ if(month===0){++year;}
+ } else if (dojo.html.hasClass(eventTarget, this.classNames["previous"])) {
+ month = --month % 12;
+ if(month==11){--year;}
+ }
+ this.clickedNode = eventTarget;
+ this.setDate(new Date(year, month, eventTarget.innerHTML));
+ },
+
+ onSetDate: function() {
+ //summary: the set date event handler
+ },
+
+ _isDisabledDate: function(dateObj){
+ if(dateObj<this.startDate||dateObj>this.endDate){
+ return true;
+ }
+
+ return this.isDisabledDate(dateObj, this.lang);
+ },
+
+ isDisabledDate: function(/*Date*/dateObj, /*String?*/locale){
+ // summary:
+ // May be overridden to disable certain dates in the calendar e.g. isDisabledDate=dojo.date.isWeekend
+
+ return false; // Boolean
+ },
+
+ _initFirstDay: function(/*Date*/dateObj, /*Boolean*/adj){
+ //adj: false for first day of month, true for first day of week adjusted by startOfWeek
+ var d = new Date(dateObj);
+ if(!adj){d.setDate(1);}
+ d.setDate(d.getDate()-this._getAdjustedDay(d,this.weekStartsOn));
+ d.setHours(0,0,0,0);
+ return d; // Date
+ },
+
+ _getAdjustedDay: function(/*Date*/dateObj){
+ //summary: used to adjust date.getDay() values to the new values based on the current first day of the week value
+ var days = [0,1,2,3,4,5,6];
+ if(this.weekStartsOn>0){
+ for(var i=0;i<this.weekStartsOn;i++){
+ days.unshift(days.pop());
+ }
+ }
+ return days[dateObj.getDay()]; // Number: 0..6 where 0=Sunday
+ }
+ }
+);
Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/DatePicker.js
------------------------------------------------------------------------------
svn:eol-style = native
Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/DebugConsole.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/DebugConsole.js?view=auto&rev=451106
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/DebugConsole.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/DebugConsole.js Thu Sep 28 20:42:39 2006
@@ -0,0 +1,26 @@
+/*
+ Copyright (c) 2004-2006, The Dojo Foundation
+ All Rights Reserved.
+
+ Licensed under the Academic Free License version 2.1 or above OR the
+ modified BSD license. For more information on Dojo licensing, see:
+
+ http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.widget.DebugConsole");
+dojo.require("dojo.widget.Widget");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.FloatingPane");
+
+dojo.widget.defineWidget(
+ "dojo.widget.DebugConsole",
+ dojo.widget.FloatingPane,
+{
+ fillInTemplate: function() {
+ dojo.widget.DebugConsole.superclass.fillInTemplate.apply(this, arguments);
+ this.containerNode.id = "debugConsoleClientPane";
+ djConfig.isDebug = true;
+ djConfig.debugContainerId = this.containerNode.id;
+ }
+});
Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/DebugConsole.js
------------------------------------------------------------------------------
svn:eol-style = native
Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/Dialog.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/Dialog.js?view=auto&rev=451106
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/Dialog.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/Dialog.js Thu Sep 28 20:42:39 2006
@@ -0,0 +1,298 @@
+/*
+ Copyright (c) 2004-2006, The Dojo Foundation
+ All Rights Reserved.
+
+ Licensed under the Academic Free License version 2.1 or above OR the
+ modified BSD license. For more information on Dojo licensing, see:
+
+ http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.widget.Dialog");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.ContentPane");
+dojo.require("dojo.event.*");
+dojo.require("dojo.gfx.color");
+dojo.require("dojo.html.layout");
+dojo.require("dojo.html.display");
+dojo.require("dojo.html.iframe");
+
+dojo.declare(
+ "dojo.widget.ModalDialogBase",
+ null,
+ {
+ isContainer: true,
+ _scrollConnected: false,
+
+ // provide a focusable element or element id if you need to
+ // work around FF's tendency to send focus into outer space on hide
+ focusElement: "",
+
+ shared: {bg: null, bgIframe: null},
+ bgColor: "black",
+ bgOpacity: 0.4,
+ followScroll: true,
+ _fromTrap: false,
+
+ trapTabs: function(e){
+ if(e.target == this.tabStartOuter) {
+ if(this._fromTrap) {
+ this.tabStart.focus();
+ this._fromTrap = false;
+ } else {
+ this._fromTrap = true;
+ this.tabEnd.focus();
+ }
+ } else if (e.target == this.tabStart) {
+ if(this._fromTrap) {
+ this._fromTrap = false;
+ } else {
+ this._fromTrap = true;
+ this.tabEnd.focus();
+ }
+ } else if(e.target == this.tabEndOuter) {
+ if(this._fromTrap) {
+ this.tabEnd.focus();
+ this._fromTrap = false;
+ } else {
+ this._fromTrap = true;
+ this.tabStart.focus();
+ }
+ } else if(e.target == this.tabEnd) {
+ if(this._fromTrap) {
+ this._fromTrap = false;
+ } else {
+ this._fromTrap = true;
+ this.tabStart.focus();
+ }
+ }
+ },
+
+ clearTrap: function(e) {
+ var _this = this;
+ setTimeout(function() {
+ _this._fromTrap = false;
+ }, 100);
+ },
+
+ //if the target mixin class already defined postCreate,
+ //dojo.widget.ModalDialogBase.prototype.postCreate.call(this)
+ //should be called in its postCreate()
+ postCreate: function() {
+ with(this.domNode.style) {
+ position = "absolute";
+ zIndex = 999;
+ display = "none";
+ overflow = "visible";
+ }
+ var b = dojo.body();
+ b.appendChild(this.domNode);
+
+ if(!this.shared.bg){
+ this.shared.bg = document.createElement("div");
+ this.shared.bg.className = "dialogUnderlay";
+ with(this.shared.bg.style) {
+ position = "absolute";
+ left = top = "0px";
+ zIndex = 998;
+ display = "none";
+ }
+ this.setBackgroundColor(this.bgColor);
+ b.appendChild(this.shared.bg);
+
+ this.shared.bgIframe = new dojo.html.BackgroundIframe(this.shared.bg);
+ }
+ },
+
+ setBackgroundColor: function(color) {
+ if(arguments.length >= 3) {
+ color = new dojo.gfx.color.Color(arguments[0], arguments[1], arguments[2]);
+ } else {
+ color = new dojo.gfx.color.Color(color);
+ }
+ this.shared.bg.style.backgroundColor = color.toString();
+ return this.bgColor = color;
+ },
+
+ setBackgroundOpacity: function(op) {
+ if(arguments.length == 0) { op = this.bgOpacity; }
+ dojo.html.setOpacity(this.shared.bg, op);
+ try {
+ this.bgOpacity = dojo.html.getOpacity(this.shared.bg);
+ } catch (e) {
+ this.bgOpacity = op;
+ }
+ return this.bgOpacity;
+ },
+
+ sizeBackground: function() {
+ if(this.bgOpacity > 0) {
+ var viewport = dojo.html.getViewport();
+ var h = viewport.height;
+ var w = viewport.width;
+ this.shared.bg.style.width = w + "px";
+ this.shared.bg.style.height = h + "px";
+ // process twice since the scroll bar may have been removed
+ // by the previous resizing
+ var viewport = dojo.html.getViewport();
+ if (viewport.width != w) { this.shared.bg.style.width = viewport.width + "px"; }
+ if (viewport.height != h) { this.shared.bg.style.height = viewport.height + "px"; }
+ }
+ },
+
+ showBackground: function() {
+ if(this.bgOpacity > 0) {
+ this.shared.bg.style.display = "block";
+ }
+ },
+
+ placeModalDialog: function() {
+ var scroll_offset = dojo.html.getScroll().offset;
+ var viewport_size = dojo.html.getViewport();
+
+ // find the size of the dialog
+ var mb = dojo.html.getMarginBox(this.containerNode);
+
+ var x = scroll_offset.x + (viewport_size.width - mb.width)/2;
+ var y = scroll_offset.y + (viewport_size.height - mb.height)/2;
+
+ with(this.domNode.style) {
+ left = x + "px";
+ top = y + "px";
+ }
+ },
+
+ //call this function in show() of subclass
+ showModalDialog: function() {
+ if (this.followScroll && !this._scrollConnected){
+ this._scrollConnected = true;
+ dojo.event.connect(window, "onscroll", this, "onScroll");
+ }
+ this.setBackgroundOpacity();
+ this.sizeBackground();
+ this.showBackground();
+ },
+
+ //call this function in hide() of subclass
+ hideModalDialog: function(){
+ // workaround for FF focus going into outer space
+ if (this.focusElement) {
+ dojo.byId(this.focusElement).focus();
+ dojo.byId(this.focusElement).blur();
+ }
+
+ this.shared.bg.style.display = "none";
+ this.shared.bg.style.width = this.shared.bg.style.height = "1px";
+
+ if (this._scrollConnected){
+ this._scrollConnected = false;
+ dojo.event.disconnect(window, "onscroll", this, "onScroll");
+ }
+ },
+
+ onScroll: function(){
+ var scroll_offset = dojo.html.getScroll().offset;
+ this.shared.bg.style.top = scroll_offset.y + "px";
+ this.shared.bg.style.left = scroll_offset.x + "px";
+ this.placeModalDialog();
+ },
+
+ // Called when the browser window's size is changed
+ checkSize: function() {
+ if(this.isShowing()){
+ this.sizeBackground();
+ this.placeModalDialog();
+ this.onResized();
+ }
+ }
+ });
+
+dojo.widget.defineWidget(
+ "dojo.widget.Dialog",
+ [dojo.widget.ContentPane, dojo.widget.ModalDialogBase],
+ {
+ templatePath: dojo.uri.dojoUri("src/widget/templates/Dialog.html"),
+
+ anim: null,
+ blockDuration: 0,
+ lifetime: 0,
+
+ show: function() {
+ if(this.lifetime){
+ this.timeRemaining = this.lifetime;
+ if(!this.blockDuration){
+ dojo.event.connect(this.shared.bg, "onclick", this, "hide");
+ }else{
+ dojo.event.disconnect(this.shared.bg, "onclick", this, "hide");
+ }
+ if(this.timerNode){
+ this.timerNode.innerHTML = Math.ceil(this.timeRemaining/1000);
+ }
+ if(this.blockDuration && this.closeNode){
+ if(this.lifetime > this.blockDuration){
+ this.closeNode.style.visibility = "hidden";
+ }else{
+ this.closeNode.style.display = "none";
+ }
+ }
+ this.timer = setInterval(dojo.lang.hitch(this, "onTick"), 100);
+ }
+
+ this.showModalDialog();
+ dojo.widget.Dialog.superclass.show.call(this);
+ this.checkSize();
+ },
+
+ onLoad: function(){
+ // when href is specified we need to reposition
+ // the dialog after the data is loaded
+ this.placeModalDialog();
+ dojo.widget.Dialog.superclass.onLoad.call(this);
+ },
+
+ fillInTemplate: function(){
+ // dojo.event.connect(this.domNode, "onclick", this, "killEvent");
+ },
+
+ hide: function(){
+ this.hideModalDialog();
+ dojo.widget.Dialog.superclass.hide.call(this);
+
+ if(this.timer){
+ clearInterval(this.timer);
+ }
+ },
+
+ setTimerNode: function(node){
+ this.timerNode = node;
+ },
+
+ setCloseControl: function(node) {
+ this.closeNode = node;
+ dojo.event.connect(node, "onclick", this, "hide");
+ },
+
+ setShowControl: function(node) {
+ dojo.event.connect(node, "onclick", this, "show");
+ },
+
+ onTick: function(){
+ if(this.timer){
+ this.timeRemaining -= 100;
+ if(this.lifetime - this.timeRemaining >= this.blockDuration){
+ dojo.event.connect(this.shared.bg, "onclick", this, "hide");
+ if(this.closeNode){
+ this.closeNode.style.visibility = "visible";
+ }
+ }
+ if(!this.timeRemaining){
+ clearInterval(this.timer);
+ this.hide();
+ }else if(this.timerNode){
+ this.timerNode.innerHTML = Math.ceil(this.timeRemaining/1000);
+ }
+ }
+ }
+ }
+);
Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/Dialog.js
------------------------------------------------------------------------------
svn:eol-style = native
Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/DocPane.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/DocPane.js?view=auto&rev=451106
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/DocPane.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/DocPane.js Thu Sep 28 20:42:39 2006
@@ -0,0 +1,382 @@
+/*
+ Copyright (c) 2004-2006, The Dojo Foundation
+ All Rights Reserved.
+
+ Licensed under the Academic Free License version 2.1 or above OR the
+ modified BSD license. For more information on Dojo licensing, see:
+
+ http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.widget.DocPane");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.io.*");
+dojo.require("dojo.event.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.Editor2");
+dojo.require("dojo.widget.Dialog");
+
+dojo.require("dojo.html.common");
+dojo.require("dojo.html.display");
+
+dojo.widget.DocPane = function(){
+ dojo.event.topic.subscribe("/docs/function/results", this, "onDocResults");
+ dojo.event.topic.subscribe("/docs/package/results", this, "onPkgResults");
+ dojo.event.topic.subscribe("/docs/function/detail", this, "onDocSelectFunction");
+}
+
+dojo.widget.defineWidget(
+ "dojo.widget.DocPane",
+ dojo.widget.HtmlWidget,
+ {
+ // Template parameters
+ dialog: null,
+ dialogBg: null,
+ dialogFg: null,
+ logIn: null,
+ edit: null,
+ save: null,
+ cancel: null,
+ detail: null,
+ result: null,
+ packag: null,
+ fn: null,
+ fnLink: null,
+ count: null,
+ row: null,
+ summary: null,
+ description: null,
+ variables: null,
+ vRow: null,
+ vLink: null,
+ vDesc: null,
+ methods: null,
+ mRow: null,
+ mLink: null,
+ mDesc: null,
+ requires: null,
+ rRow: null,
+ rRow2: null,
+ rH3: null,
+ rLink: null,
+ parameters: null,
+ pRow: null,
+ pLink: null,
+ pDesc: null,
+ pOpt: null,
+ pType: null,
+ sType: null,
+ sName: null,
+ sParams: null,
+ sPType: null,
+ sPTypeSave: null,
+ sPName: null,
+ sPNameSave: null,
+ pkgDescription: null,
+
+ // Fields and methods
+ _appends: [],
+ templatePath: dojo.uri.dojoUri("src/widget/templates/DocPane.html"),
+ templateCssPath: dojo.uri.dojoUri("src/widget/templates/DocPane.css"),
+ isContainer: true,
+ fillInTemplate: function(){
+ this.requires = dojo.html.removeNode(this.requires);
+ this.rRow.style.display = "none";
+ this.rRow2.style.display = "none";
+
+ this.methods = dojo.html.removeNode(this.methods);
+ this.mRow.style.display = "none";
+
+ this.dialog = dojo.widget.createWidget("dialog", {}, this.dialog);
+ this.dialog.setCloseControl(this.cancel);
+ dojo.html.setOpacity(this.dialogBg, 0.8);
+ dojo.html.setOpacity(this.dialogFg, 1);
+
+ dojo.event.connect(this.edit, "onclick", dojo.lang.hitch(this, function(){
+ if(!this._isLoggedIn){
+ this.dialog.show();
+ }
+ }));
+ dojo.event.connect(this.logIn, "onclick", this, "_logIn");
+ dojo.event.connect(this.save, "onclick", this, "_save");
+ dojo.event.connect(dojo.docs, "logInSuccess", this, "_loggedIn");
+
+ /*
+ this.pkgDescription = dojo.widget.createWidget("editor2", {
+ toolbarAlwaysVisible: true
+ }, this.pkgDescription);
+ */
+
+ this.homeSave = this.containerNode.cloneNode(true);
+ this.detailSave = dojo.html.removeNode(this.detail);
+ this.resultSave = dojo.html.removeNode(this.result);
+ this.packageSave = dojo.html.removeNode(this.packag);
+ this.results = dojo.html.removeNode(this.results);
+ this.rowParent = this.row.parentNode;
+ this.rowSave = dojo.html.removeNode(this.row);
+ this.vParent = this.vRow.parentNode;
+ this.vSave = dojo.html.removeNode(this.vRow);
+ this.pParent = this.pRow.parentNode;
+ this.pSave = dojo.html.removeNode(this.pRow);
+ this.sPTypeSave = dojo.html.removeNode(this.sPType);
+ this.sPNameSave = dojo.html.removeNode(this.sPName);
+ this.navSave = dojo.html.removeNode(this.nav);
+ },
+
+ _logIn: function(){
+ dojo.docs.setUserName(this.userName.value);
+ dojo.docs.setPassword(this.password.value);
+ },
+
+ _loggedIn: function(){
+ this._isLoggedIn = true;
+ this.dialog.hide();
+ this.pkgEditor = dojo.widget.createWidget("editor2", {
+ toolbarAlwaysVisible: true
+ }, this.pkgDescription);
+ },
+
+ _save: function(){
+ if(this.pkgEditor){
+ dojo.docs.savePackage(this._pkgPath, {
+ description: this.pkgEditor.getEditorContent()
+ });
+ }
+ },
+
+ onDocSelectFunction: function(message){
+ dojo.debug("onDocSelectFunction()");
+ for(var key in message){
+ dojo.debug(key + ": " + dojo.json.serialize(message[key]));
+ }
+ var meta = message.meta;
+ if(meta){
+ var variables = meta.variables;
+ var this_variables = meta.this_variables;
+ var child_variables = meta.child_variables;
+ var parameters = meta.parameters;
+ }
+ var doc = message.doc;
+ dojo.debug(dojo.json.serialize(doc));
+
+ var appends = this._appends;
+ dojo.html.removeChildren(this.domNode);
+ this.fn.innerHTML = message.name;
+
+ this.variables.style.display = "block";
+ var all = [];
+ if(variables){
+ all = variables;
+ }
+ if(this_variables){
+ all = all.concat(this_variables);
+ }
+ if(child_variables){
+ all = all.concat(child_variables);
+ }
+ if(!all.length){
+ this.variables.style.display = "none";
+ }else{
+ for(var i = 0, one; one = all[i]; i++){
+ this.vLink.innerHTML = one;
+ this.vDesc.parentNode.style.display = "none";
+ appends.push(this.vParent.appendChild(this.vSave.cloneNode(true)));
+ }
+ }
+
+ this.sParams.innerHTML = "";
+ var first = true;
+ for(var param in parameters){
+ var paramType = parameters[param].type;
+ var paramSummary = parameters[param].summary;
+ var paramName = param;
+ this.parameters.style.display = "block";
+ this.pLink.innerHTML = paramName;
+ this.pOpt.style.display = "none";
+ if(parameters[param].opt){
+ this.pOpt.style.display = "inline";
+ }
+ this.pType.parentNode.style.display = "none";
+ if(parameters[param][0]){
+ this.pType.parentNode.style.display = "inline";
+ this.pType.innerHTML = paramType;
+ }
+ this.pDesc.parentNode.style.display = "none";
+ if(paramSummary){
+ this.pDesc.parentNode.style.display = "inline";
+ this.pDesc.innerHTML = paramSummary;
+ }
+ appends.push(this.pParent.appendChild(this.pSave.cloneNode(true)));
+
+ if(!first) {
+ this.sParams.appendChild(document.createTextNode(", "));
+ }
+ first = false;
+ if(paramType){
+ dojo.debug(this.sPTypeSave);
+ this.sPTypeSave.innerHTML = paramType;
+ this.sParams.appendChild(this.sPTypeSave.cloneNode(true));
+ this.sParams.appendChild(document.createTextNode(" "));
+ }
+ dojo.debug(this.sPNameSave);
+ this.sPNameSave.innerHTML = paramName;
+ this.sParams.appendChild(this.sPNameSave.cloneNode(true))
+ }
+
+ if(message.returns){
+ this.sType.innerHTML = message.returns;
+ }else{
+ this.sType.innerHTML = "void";
+ }
+
+ this.sName.innerHTML = message.name;
+
+ this.domNode.appendChild(this.navSave);
+ this.domNode.appendChild(this.detailSave.cloneNode(true));
+
+ for(var i = 0, append; append = appends[i]; i++){
+ dojo.html.removeNode(append);
+ }
+ },
+
+ onPkgResult: function(/*Object*/ results){
+ if(this.pkgEditor){
+ this.pkgEditor.close(true);
+ dojo.debug(this.pkgDescription);
+ }
+ var methods = results.methods;
+ var requires = results.requires;
+ var description = results.description;
+ this._pkgPath = results.path;
+ var requireLinks = [];
+ var appends = this._appends;
+ while(appends.length){
+ dojo.html.removeNode(appends.shift());
+ }
+
+ dojo.html.removeChildren(this.domNode);
+
+ this.pkg.innerHTML = results.pkg;
+
+ var hasRequires = false;
+ for(var env in requires){
+ hasRequires = true;
+
+ this.rH3.style.display = "none";
+ if(env != "common"){
+ this.rH3.style.display = "";
+ this.rH3.innerHTML = env;
+ }
+
+ for(var i = 0, require; require = requires[env][i]; i++){
+ requireLinks.push({
+ name: require
+ });
+ this.rLink.innerHTML = require;
+ this.rLink.href = "#" + require;
+ var rRow2 = this.rRow2.parentNode.insertBefore(this.rRow2.cloneNode(true), this.rRow2);
+ rRow2.style.display = "";
+ appends.push(rRow2);
+ }
+ var rRow = this.rRow.parentNode.insertBefore(this.rRow.cloneNode(true), this.rRow);
+ rRow.style.display = "";
+ appends.push(rRow);
+ }
+
+ if(hasRequires){
+ appends.push(this.packageSave.appendChild(this.requires.cloneNode(true)));
+ }
+
+ if(results.size){
+ for(var i = 0, method; method = methods[i]; i++){
+ this.mLink.innerHTML = method.name;
+ this.mLink.href = "#" + method.name;
+ this.mDesc.parentNode.style.display = "none";
+ if(method.summary){
+ this.mDesc.parentNode.style.display = "inline";
+ this.mDesc.innerHTML = method.summary;
+ }
+ var mRow = this.mRow.parentNode.insertBefore(this.mRow.cloneNode(true), this.mRow);
+ mRow.style.display = "";
+ appends.push(mRow);
+ }
+ appends.push(this.packageSave.appendChild(this.methods.cloneNode(true)));
+ }
+
+ this.domNode.appendChild(this.packageSave);
+
+ /*
+ dojo.debug(description);
+ function fillContent(){
+ this.pkgDescription.replaceEditorContent(description);
+ this.pkgDescription._updateHeight();
+ }
+ if(this.pkgDescription.isLoaded){
+ fillContent();
+ }else{
+ dojo.event.connect(this.pkgDescription, "onLoad", dojo.lang.hitch(this, fillContent));
+ }
+ */
+ this.pkgDescription.innerHTML = description;
+
+ function makeSelect(fOrP, x){
+ return function(e) {
+ dojo.event.topic.publish("/docs/" + fOrP + "/select", x);
+ }
+ }
+
+ var as = this.domNode.getElementsByTagName("a");
+ for(var i = 0, a; a = as[i]; i++){
+ if(a.className == "docMLink"){
+ dojo.event.connect(a, "onclick", makeSelect("function", methods[i]));
+ }else if(a.className == "docRLink"){
+ dojo.event.connect(a, "onclick", makeSelect("package", requireLinks[i]));
+ }
+ }
+ },
+
+ onDocResults: function(fns){
+ dojo.debug("onDocResults(): called");
+
+ if(fns.length == 1){
+ dojo.event.topic.publish("/docs/function/select", fns[0]);
+ return;
+ }
+
+ dojo.html.removeChildren(this.domNode);
+
+ this.count.innerHTML = fns.length;
+ var appends = [];
+ for(var i = 0, fn; fn = fns[i]; i++){
+ this.fnLink.innerHTML = fn.name;
+ this.fnLink.href = "#" + fn.name;
+ if(fn.id){
+ this.fnLink.href = this.fnLink.href + "," + fn.id;
+ }
+ this.summary.parentNode.style.display = "none";
+ if(fn.summary){
+ this.summary.parentNode.style.display = "inline";
+ this.summary.innerHTML = fn.summary;
+ }
+ appends.push(this.rowParent.appendChild(this.rowSave.cloneNode(true)));
+ }
+
+ function makeSelect(x){
+ return function(e) {
+ dojo.event.topic.publish("/docs/function/select", x);
+ }
+ }
+
+ this.domNode.appendChild(this.resultSave.cloneNode(true));
+ var as = this.domNode.getElementsByTagName("a");
+ for(var i = 0, a; a = as[i]; i++){
+ dojo.event.connect(a, "onclick", makeSelect(fns[i]));
+ }
+
+ for(var i = 0, append; append = appends[i]; i++){
+ this.rowParent.removeChild(append);
+ }
+ }
+ }
+);
Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/DocPane.js
------------------------------------------------------------------------------
svn:eol-style = native