From c1937cccd0985e86e247287faa9ac60870feecd7 Mon Sep 17 00:00:00 2001 From: xue <> Date: Sun, 27 Aug 2006 23:26:55 +0000 Subject: Merge from 3.0 branch till 1350. --- framework/Web/Javascripts/TJavaScript.php | 12 +- framework/Web/Javascripts/datepicker/datepicker.js | 230 ++++++++++----------- framework/Web/Javascripts/effects/controls.js | 10 +- framework/Web/Javascripts/effects/dragdrop.js | 34 ++- framework/Web/Javascripts/effects/effects.js | 59 +++--- framework/Web/Javascripts/effects/slider.js | 21 +- framework/Web/Javascripts/prado/ajax.js | 92 +++++---- framework/Web/UI/TClientScriptManager.php | 7 +- framework/Web/UI/TControl.php | 2 +- framework/Web/UI/TTemplateControl.php | 2 + framework/Web/UI/TThemeManager.php | 6 +- framework/Web/UI/WebControls/TBaseValidator.php | 30 ++- framework/Web/UI/WebControls/TBoundColumn.php | 10 + framework/Web/UI/WebControls/TButtonColumn.php | 9 + framework/Web/UI/WebControls/TCheckBoxColumn.php | 10 + framework/Web/UI/WebControls/TDataGrid.php | 5 + framework/Web/UI/WebControls/TDatePicker.php | 94 +++++++-- .../Web/UI/WebControls/TEditCommandColumn.php | 22 +- framework/Web/UI/WebControls/THyperLinkColumn.php | 9 + framework/Web/UI/WebControls/TListControl.php | 74 ++++--- .../Web/UI/WebControls/TValidationSummary.php | 26 +-- 21 files changed, 476 insertions(+), 288 deletions(-) (limited to 'framework/Web') diff --git a/framework/Web/Javascripts/TJavaScript.php b/framework/Web/Javascripts/TJavaScript.php index 0f6414ec..e48de8f4 100644 --- a/framework/Web/Javascripts/TJavaScript.php +++ b/framework/Web/Javascripts/TJavaScript.php @@ -89,7 +89,7 @@ class TJavaScript else return strtr($js,array("\t"=>'\t',"\n"=>'\n',"\r"=>'\r','"'=>'\"','\''=>'\\\'','\\'=>'\\\\')); } - + /** * @return string considers the string as raw javascript function code */ @@ -98,16 +98,16 @@ class TJavaScript if(self::isFunction($js)) return $js; else - return 'javascript:'.$js; + return 'javascript:'.$js; } - + /** * @return boolean true if string is raw javascript function code, i.e., if * the string begins with javascript: */ public static function isFunction($js) { - return preg_match('/^\s*javascript:/', $js); + return preg_match('/^\s*javascript:/i', $js); } /** @@ -124,9 +124,9 @@ class TJavaScript * * For higher complexity data structures use {@link jsonEncode} and {@link jsonDecode} * to serialize and unserialize. - * + * * Note: strings begining with javascript: will be considered as - * raw javascript code and no encoding of that string will be enforced. + * raw javascript code and no encoding of that string will be enforced. * * @param mixed PHP variable to be encoded * @param boolean whether the output is a map or a list. diff --git a/framework/Web/Javascripts/datepicker/datepicker.js b/framework/Web/Javascripts/datepicker/datepicker.js index 4525c1ba..5fff5f0f 100644 --- a/framework/Web/Javascripts/datepicker/datepicker.js +++ b/framework/Web/Javascripts/datepicker/datepicker.js @@ -10,41 +10,41 @@ Object.extend(Prado.WebUI.TDatePicker, var year=now.getFullYear(); var month=now.getMonth(); var day=1; - + var month_list = this.getMonthListControl(control); var day_list = this.getDayListControl(control); var year_list = this.getYearListControl(control); - + var day = day_list ? $F(day_list) : 1; var month = month_list ? $F(month_list) : now.getMonth(); var year = year_list ? $F(year_list) : now.getFullYear(); - + return new Date(year,month,day, 0, 0, 0); }, - + getYearListControl : function(control) { return $(control.id+"_year"); }, - + getMonthListControl : function(control) { return $(control.id+"_month"); }, - + getDayListControl : function(control) { return $(control.id+"_day"); } }); -Prado.WebUI.TDatePicker.prototype = +Prado.WebUI.TDatePicker.prototype = { MonthNames : [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ], - AbbreviatedMonthNames : ["Jan", "Feb", "Mar", "Apr", "May", + AbbreviatedMonthNames : ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], ShortWeekDayNames : ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], @@ -52,20 +52,20 @@ Prado.WebUI.TDatePicker.prototype = Format : "yyyy-MM-dd", FirstDayOfWeek : 1, // 0 for sunday - + ClassName : "TDatePicker", FromYear : 2000, UpToYear: 2015, - + initialize : function(options) { this.options = options || []; - this.control = $(options.ID); + this.control = $(options.ID); this.dateSlot = new Array(42); this.weekSlot = new Array(6); this.minimalDaysInFirstWeek = 4; this.selectedDate = this.newDate(); - + //which element to trigger to show the calendar if(this.options.Trigger) { @@ -77,45 +77,45 @@ Prado.WebUI.TDatePicker.prototype = this.trigger = this.control; var triggerEvent = this.options.TriggerEvent || "focus"; } - + Object.extend(this,options); - + Event.observe(this.trigger, triggerEvent, this.show.bindEvent(this)); - + }, create : function() { if(typeof(this._calDiv) != "undefined") return; - + var div; var table; var tbody; var tr; var td; - + // Create the top-level div element this._calDiv = document.createElement("div"); this._calDiv.className = this.ClassName; - this._calDiv.style.display = "none"; + this._calDiv.style.display = "none"; this._calDiv.style.position = "absolute" - + // header div div = document.createElement("div"); div.className = "calendarHeader"; this._calDiv.appendChild(div); - + table = document.createElement("table"); table.style.cellSpacing = 0; div.appendChild(table); - + tbody = document.createElement("tbody"); table.appendChild(tbody); - + tr = document.createElement("tr"); tbody.appendChild(tr); - + // Previous Month Button td = document.createElement("td"); var previousMonth = document.createElement("input"); @@ -124,11 +124,11 @@ Prado.WebUI.TDatePicker.prototype = previousMonth.value = "<<"; td.appendChild(previousMonth); tr.appendChild(td); - - - + + + // - // Create the month drop down + // Create the month drop down // td = document.createElement("td"); tr.appendChild(td); @@ -144,9 +144,9 @@ Prado.WebUI.TDatePicker.prototype = this._monthSelect.appendChild(opt); } td.appendChild(this._monthSelect); - - // + + // // Create the year drop down // td = document.createElement("td"); @@ -163,8 +163,8 @@ Prado.WebUI.TDatePicker.prototype = this._yearSelect.appendChild(opt); } td.appendChild(this._yearSelect); - - + + td = document.createElement("td"); var nextMonth = document.createElement("input"); nextMonth.className = "nextMonthButton button"; @@ -172,26 +172,26 @@ Prado.WebUI.TDatePicker.prototype = nextMonth.value = ">>"; td.appendChild(nextMonth); tr.appendChild(td); - + // Calendar body div = document.createElement("div"); div.className = "calendarBody"; this._calDiv.appendChild(div); var calendarBody = div; - - // Create the inside of calendar body - + + // Create the inside of calendar body + var text; table = document.createElement("table"); table.align="center"; table.className = "grid"; - + div.appendChild(table); var thead = document.createElement("thead"); table.appendChild(thead); tr = document.createElement("tr"); thead.appendChild(tr); - + for(i=0; i < 7; ++i) { td = document.createElement("th"); text = document.createTextNode(this.ShortWeekDayNames[(i+this.FirstDayOfWeek)%7]); @@ -199,11 +199,11 @@ Prado.WebUI.TDatePicker.prototype = td.className = "weekDayHead"; tr.appendChild(td); } - + // Date grid tbody = document.createElement("tbody"); table.appendChild(tbody); - + for(week=0; week<6; ++week) { tr = document.createElement("tr"); tbody.appendChild(tr); @@ -220,13 +220,13 @@ Prado.WebUI.TDatePicker.prototype = tmp.value = -1; tmp.data = text; this.dateSlot[(week*7)+day] = tmp; - + Event.observe(td, "mouseover", this.hover.bindEvent(this)); Event.observe(td, "mouseout", this.hover.bindEvent(this)); - + } } - + // Calendar Footer div = document.createElement("div"); div.className = "calendarFooter"; @@ -251,18 +251,18 @@ Prado.WebUI.TDatePicker.prototype = } this.control.parentNode.appendChild(this._calDiv); - + this.update(); this.updateHeader(); - + this.ieHack(true); - // IE55+ extension + // IE55+ extension previousMonth.hideFocus = true; nextMonth.hideFocus = true; todayButton.hideFocus = true; // end IE55+ extension - + // hook up events Event.observe(previousMonth, "click", this.prevMonth.bindEvent(this)); Event.observe(nextMonth, "click", this.nextMonth.bindEvent(this)); @@ -272,18 +272,18 @@ Prado.WebUI.TDatePicker.prototype = Event.observe(this._yearSelect, "change", this.yearSelect.bindEvent(this)); // ie6 extension - Event.observe(this._calDiv, "mousewheel", this.mouseWheelChange.bindEvent(this)); - + Event.observe(this._calDiv, "mousewheel", this.mouseWheelChange.bindEvent(this)); + Event.observe(calendarBody, "click", this.selectDate.bindEvent(this)); - + Prado.Element.focus(this.control); - + }, - - ieHack : function(cleanup) + + ieHack : function(cleanup) { // IE hack - if(this.iePopUp) + if(this.iePopUp) { this.iePopUp.style.display = "block"; this.iePopUp.style.top = (this._calDiv.offsetTop -1 ) + "px"; @@ -299,7 +299,7 @@ Prado.WebUI.TDatePicker.prototype = if(!this.showing) return; if (!ev) ev = document.parentWindow.event; var kc = ev.keyCode != null ? ev.keyCode : ev.charCode; - + if(kc == Event.KEY_RETURN || kc == Event.KEY_SPACEBAR || kc == Event.KEY_TAB) { this.setSelectedDate(this.selectedDate); @@ -310,8 +310,8 @@ Prado.WebUI.TDatePicker.prototype = { Event.stop(ev); this.hide(); } - - var getDaysPerMonth = function (nMonth, nYear) + + var getDaysPerMonth = function (nMonth, nYear) { nMonth = (nMonth + 12) % 12; var days= [31,28,31,30,31,30,31,31,30,31,30,31]; @@ -355,43 +355,43 @@ Prado.WebUI.TDatePicker.prototype = else d -= 604800000; // -7 days } - else if (kc == Event.KEY_DOWN) + else if (kc == Event.KEY_DOWN) { if(ev.ctrlKey || ev.shiftKey) // +1 year { current.setDate( Math.min(current.getDate(), getDaysPerMonth(current.getMonth(),current.getFullYear() + 1)) ); // no need to catch dec -> jan for the year d = current.setFullYear( current.getFullYear() + 1 ); } - else + else d += 7 * 24 * 61 * 60 * 1000; // +7 days } this.setSelectedDate(d); - Event.stop(ev); + Event.stop(ev); }, - + selectDate : function(ev) { var el = Event.element(ev); while (el.nodeType != 1) el = el.parentNode; - + while (el != null && el.tagName && el.tagName.toLowerCase() != "td") el = el.parentNode; - + // if no td found, return if (el == null || el.tagName == null || el.tagName.toLowerCase() != "td") return; - + var d = this.newDate(this.selectedDate); var n = Number(el.firstChild.data); if (isNaN(n) || n <= 0 || n == null) return; - + d.setDate(n); this.setSelectedDate(d); this.hide(); }, - + selectToday : function() { if(this.selectedDate.toISODate() == this.newDate().toISODate()) @@ -399,36 +399,36 @@ Prado.WebUI.TDatePicker.prototype = this.setSelectedDate(this.newDate()); }, - + clearSelection : function() { this.setSelectedDate(this.newDate()); this.hide(); }, - + monthSelect : function(ev) { this.setMonth(Form.Element.getValue(Event.element(ev))); }, - + yearSelect : function(ev) { this.setYear(Form.Element.getValue(Event.element(ev))); }, - + // ie6 extension - mouseWheelChange : function (e) + mouseWheelChange : function (e) { if (e == null) e = document.parentWindow.event; var n = - e.wheelDelta / 120; var d = this.newDate(this.selectedDate); var m = d.getMonth() + n; this.setMonth(m); - + return false; }, - onChange : function() + onChange : function() { if(this.options.InputMode == "TextBox") { @@ -459,7 +459,7 @@ Prado.WebUI.TDatePicker.prototype = Event.fireEvent(day || month || year, "change"); } }, - + formatDate : function() { return this.selectedDate ? this.selectedDate.SimpleFormat(this.Format,this) : ''; @@ -474,52 +474,52 @@ Prado.WebUI.TDatePicker.prototype = return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0,0,0); }, - setSelectedDate : function(date) + setSelectedDate : function(date) { if (date == null) return; this.selectedDate = this.newDate(date); - + this.updateHeader(); this.update(); if (typeof(this.onChange) == "function") - this.onChange(); + this.onChange(this, date); }, - getElement : function() + getElement : function() { return this._calDiv; }, - getSelectedDate : function () + getSelectedDate : function () { return this.selectedDate == null ? null : this.newDate(this.selectedDate); }, - - setYear : function(year) + + setYear : function(year) { var d = this.newDate(this.selectedDate); d.setFullYear(year); this.setSelectedDate(d); }, - setMonth : function (month) + setMonth : function (month) { var d = this.newDate(this.selectedDate); d.setMonth(month); this.setSelectedDate(d); }, - nextMonth : function () + nextMonth : function () { this.setMonth(this.selectedDate.getMonth()+1); }, - prevMonth : function () + prevMonth : function () { this.setMonth(this.selectedDate.getMonth()-1); }, - + getDatePickerOffsetHeight : function() { if(this.options.InputMode == "TextBox") @@ -529,27 +529,27 @@ Prado.WebUI.TDatePicker.prototype = if(control) return control.offsetHeight; var control = Prado.WebUI.TDatePicker.getMonthListControl(this.control); - if(control) return control.offsetHeight; + if(control) return control.offsetHeight; var control = Prado.WebUI.TDatePicker.getYearListControl(this.control); - if(control) return control.offsetHeight; + if(control) return control.offsetHeight; return 0; }, - - show : function() + + show : function() { this.create(); - + if(!this.showing) { var pos = Position.positionedOffset(this.control); - + pos[1] += this.getDatePickerOffsetHeight(); - + this._calDiv.style.display = "block"; this._calDiv.style.top = (pos[1]-1) + "px"; this._calDiv.style.left = pos[0] + "px"; - + this.ieHack(false); this.documentClickEvent = this.hideOnClick.bindEvent(this); this.documentKeyDownEvent = this.keyPressed.bindEvent(this); @@ -560,7 +560,7 @@ Prado.WebUI.TDatePicker.prototype = this.selectedDate = date; this.setSelectedDate(date); } - Event.observe(document,"keydown", this.documentKeyDownEvent); + Event.observe(document,"keydown", this.documentKeyDownEvent); this.showing = true; } }, @@ -572,7 +572,7 @@ Prado.WebUI.TDatePicker.prototype = else return Prado.WebUI.TDatePicker.getDropDownDate(this.control); }, - + //hide the calendar when clicked outside any calendar hideOnClick : function(ev) { @@ -585,12 +585,12 @@ Prado.WebUI.TDatePicker.prototype = within = within || el == this.trigger; within = within || el == this.control; if(within) break; - el = el.parentNode; + el = el.parentNode; } while(el); if(!within) this.hide(); }, - + hide : function() { @@ -599,28 +599,28 @@ Prado.WebUI.TDatePicker.prototype = this._calDiv.style.display = "none"; if(this.iePopUp) this.iePopUp.style.display = "none"; - this.showing = false; - Event.stopObserving(document.body, "click", this.documentClickEvent); - Event.stopObserving(document,"keydown", this.documentKeyDownEvent); - } + this.showing = false; + Event.stopObserving(document.body, "click", this.documentClickEvent); + Event.stopObserving(document,"keydown", this.documentKeyDownEvent); + } }, - - update : function() + + update : function() { // Calculate the number of days in the month for the selected date var date = this.selectedDate; var today = (this.newDate()).toISODate(); - + var selected = date.toISODate(); var d1 = new Date(date.getFullYear(), date.getMonth(), 1); var d2 = new Date(date.getFullYear(), date.getMonth()+1, 1); var monthLength = Math.round((d2 - d1) / (24 * 60 * 60 * 1000)); - + // Find out the weekDay index for the first of this month var firstIndex = (d1.getDay() - this.FirstDayOfWeek) % 7 ; if (firstIndex < 0) firstIndex += 7; - + var index = 0; while (index < firstIndex) { this.dateSlot[index].value = -1; @@ -628,7 +628,7 @@ Prado.WebUI.TDatePicker.prototype = this.dateSlot[index].data.parentNode.className = "empty"; index++; } - + for (i = 1; i <= monthLength; i++, index++) { var slot = this.dateSlot[index]; var slotNode = slot.data.parentNode; @@ -645,18 +645,18 @@ Prado.WebUI.TDatePicker.prototype = } d1 = new Date(d1.getFullYear(), d1.getMonth(), d1.getDate()+1); } - + var lastDateIndex = index; - + while(index < 42) { this.dateSlot[index].value = -1; this.dateSlot[index].data.data = String.fromCharCode(160); this.dateSlot[index].data.parentNode.className = "empty"; ++index; } - + }, - + hover : function(ev) { if(Event.element(ev).tagName) @@ -667,7 +667,7 @@ Prado.WebUI.TDatePicker.prototype = Event.element(ev).removeClassName("hover"); } }, - + updateHeader : function () { var options = this._monthSelect.options; @@ -678,7 +678,7 @@ Prado.WebUI.TDatePicker.prototype = options[i].selected = true; } } - + options = this._yearSelect.options; var year = this.selectedDate.getFullYear(); for(var i=0; i < options.length; ++i) { @@ -687,8 +687,8 @@ Prado.WebUI.TDatePicker.prototype = options[i].selected = true; } } - + } - - + + }; \ No newline at end of file diff --git a/framework/Web/Javascripts/effects/controls.js b/framework/Web/Javascripts/effects/controls.js index de0261ed..ea5ce6ea 100644 --- a/framework/Web/Javascripts/effects/controls.js +++ b/framework/Web/Javascripts/effects/controls.js @@ -33,6 +33,9 @@ // useful when one of the tokens is \n (a newline), as it // allows smart autocompletion after linebreaks. +if(typeof Effect == 'undefined') + throw("controls.js requires including script.aculo.us' effects.js library"); + var Autocompleter = {} Autocompleter.Base = function() {}; Autocompleter.Base.prototype = { @@ -94,7 +97,7 @@ Autocompleter.Base.prototype = { }, fixIEOverlapping: function() { - Position.clone(this.update, this.iefix); + Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)}); this.iefix.style.zIndex = 1; this.update.style.zIndex = 2; Element.show(this.iefix); @@ -202,11 +205,13 @@ Autocompleter.Base.prototype = { markPrevious: function() { if(this.index > 0) this.index-- else this.index = this.entryCount-1; + this.getEntry(this.index).scrollIntoView(true); }, markNext: function() { if(this.index < this.entryCount-1) this.index++ else this.index = 0; + this.getEntry(this.index).scrollIntoView(false); }, getEntry: function(index) { @@ -531,7 +536,7 @@ Ajax.InPlaceEditor.prototype = { Element.hide(this.element); this.createForm(); this.element.parentNode.insertBefore(this.form, this.element); - Field.scrollFreeActivate(this.editField); + if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField); // stop the event to avoid a page refresh in Safari if (evt) { Event.stop(evt); @@ -636,6 +641,7 @@ Ajax.InPlaceEditor.prototype = { Element.removeClassName(this.form, this.options.loadingClassName); this.editField.disabled = false; this.editField.value = transport.responseText.stripTags(); + Field.scrollFreeActivate(this.editField); }, onclickCancel: function() { this.onComplete(); diff --git a/framework/Web/Javascripts/effects/dragdrop.js b/framework/Web/Javascripts/effects/dragdrop.js index be2a30f5..1528eced 100644 --- a/framework/Web/Javascripts/effects/dragdrop.js +++ b/framework/Web/Javascripts/effects/dragdrop.js @@ -5,6 +5,9 @@ /*--------------------------------------------------------------------------*/ +if(typeof Effect == 'undefined') + throw("dragdrop.js requires including script.aculo.us' effects.js library"); + var Droppables = { drops: [], @@ -204,21 +207,31 @@ var Draggables = { /*--------------------------------------------------------------------------*/ var Draggable = Class.create(); +Draggable._revertCache = {}; +Draggable._dragging = {}; + Draggable.prototype = { initialize: function(element) { var options = Object.extend({ handle: false, starteffect: function(element) { - element._opacity = Element.getOpacity(element); + element._opacity = Element.getOpacity(element); + Draggable._dragging[element] = true; new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); }, reverteffect: function(element, top_offset, left_offset) { var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02; - element._revert = new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur}); + Draggable._revertCache[element] = + new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur, + queue: {scope:'_draggable', position:'end'} + }); }, endeffect: function(element) { - var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0 - new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity}); + var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0; + new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, + queue: {scope:'_draggable', position:'end'}, + afterFinish: function(){ Draggable._dragging[element] = false } + }); }, zindex: 1000, revert: false, @@ -264,6 +277,8 @@ Draggable.prototype = { }, initDrag: function(event) { + if(typeof Draggable._dragging[this.element] != undefined && + Draggable._dragging[this.element]) return; if(Event.isLeftClick(event)) { // abort on form elements, fixes a Firefox issue var src = Event.element(event); @@ -274,9 +289,9 @@ Draggable.prototype = { src.tagName=='BUTTON' || src.tagName=='TEXTAREA')) return; - if(this.element._revert) { - this.element._revert.cancel(); - this.element._revert = null; + if(Draggable._revertCache[this.element]) { + Draggable._revertCache[this.element].cancel(); + Draggable._revertCache[this.element] = null; } var pointer = [Event.pointerX(event), Event.pointerY(event)]; @@ -442,6 +457,7 @@ Draggable.prototype = { }, startScrolling: function(speed) { + if(!(speed[0] || speed[1])) return; this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed]; this.lastScrolled = new Date(); this.scrollInterval = setInterval(this.scroll.bind(this), 10); @@ -708,7 +724,7 @@ var Sortable = { if(!Element.isParent(dropon, element)) { var index; - var children = Sortable.findElements(dropon, {tag: droponOptions.tag}); + var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only}); var child = null; if(children) { @@ -869,7 +885,7 @@ var Sortable = { if (options.tree) { return Sortable.tree(element, arguments[1]).children.map( function (item) { - return [name + Sortable._constructIndex(item) + "=" + + return [name + Sortable._constructIndex(item) + "[id]=" + encodeURIComponent(item.id)].concat(item.children.map(arguments.callee)); }).flatten().join('&'); } else { diff --git a/framework/Web/Javascripts/effects/effects.js b/framework/Web/Javascripts/effects/effects.js index 0864323e..1f3d50bb 100644 --- a/framework/Web/Javascripts/effects/effects.js +++ b/framework/Web/Javascripts/effects/effects.js @@ -105,6 +105,9 @@ Array.prototype.call = function() { var Effect = { tagifyText: function(element) { + if(typeof Builder == 'undefined') + throw("Effect.tagifyText requires including script.aculo.us' builder.js library"); + var tagifyStyle = 'position:relative'; if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1'; element = $(element); @@ -161,9 +164,8 @@ var Effect2 = Effect; // deprecated Effect.Transitions = {} -Effect.Transitions.linear = function(pos) { - return pos; -} +Effect.Transitions.linear = Prototype.K; + Effect.Transitions.sinoidal = function(pos) { return (-Math.cos(pos*Math.PI)/2) + 0.5; } @@ -353,7 +355,7 @@ Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), { initialize: function(element) { this.element = $(element); // make this work on IE on elements without 'layout' - if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout)) + if(/MSIE/.test(navigator.userAgent) && (!this.element.currentStyle.hasLayout)) this.element.setStyle({zoom: 1}); var options = Object.extend({ from: this.element.getOpacity() || 0.0, @@ -393,8 +395,8 @@ Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), { }, update: function(position) { this.element.setStyle({ - left: this.options.x * position + this.originalLeft + 'px', - top: this.options.y * position + this.originalTop + 'px' + left: Math.round(this.options.x * position + this.originalLeft) + 'px', + top: Math.round(this.options.y * position + this.originalTop) + 'px' }); } }); @@ -433,7 +435,7 @@ Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), { this.originalLeft = this.element.offsetLeft; var fontSize = this.element.getStyle('font-size') || '100%'; - ['em','px','%'].each( function(fontSizeType) { + ['em','px','%','pt'].each( function(fontSizeType) { if(fontSize.indexOf(fontSizeType)>0) { this.fontSize = parseFloat(fontSize); this.fontSizeType = fontSizeType; @@ -462,8 +464,8 @@ Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), { }, setDimensions: function(height, width) { var d = {}; - if(this.options.scaleX) d.width = width + 'px'; - if(this.options.scaleY) d.height = height + 'px'; + if(this.options.scaleX) d.width = Math.round(width) + 'px'; + if(this.options.scaleY) d.height = Math.round(height) + 'px'; if(this.options.scaleFromCenter) { var topd = (height - this.dims[0])/2; var leftd = (width - this.dims[1])/2; @@ -589,7 +591,7 @@ Effect.Puff = function(element) { Effect.BlindUp = function(element) { element = $(element); element.makeClipping(); - return new Effect.Scale(element, 0, + return new Effect.Scale(element, 0, Object.extend({ scaleContent: false, scaleX: false, restoreAfterFinish: true, @@ -604,28 +606,27 @@ Effect.BlindUp = function(element) { Effect.BlindDown = function(element) { element = $(element); var elementDimensions = element.getDimensions(); - return new Effect.Scale(element, 100, - Object.extend({ scaleContent: false, - scaleX: false, - scaleFrom: 0, - scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, - restoreAfterFinish: true, - afterSetup: function(effect) { - effect.element.makeClipping(); - effect.element.setStyle({height: '0px'}); - effect.element.show(); - }, - afterFinishInternal: function(effect) { - effect.element.undoClipping(); - } - }, arguments[1] || {}) - ); + return new Effect.Scale(element, 100, Object.extend({ + scaleContent: false, + scaleX: false, + scaleFrom: 0, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { + effect.element.makeClipping(); + effect.element.setStyle({height: '0px'}); + effect.element.show(); + }, + afterFinishInternal: function(effect) { + effect.element.undoClipping(); + } + }, arguments[1] || {})); } Effect.SwitchOff = function(element) { element = $(element); var oldOpacity = element.getInlineOpacity(); - return new Effect.Appear(element, { + return new Effect.Appear(element, Object.extend({ duration: 0.4, from: 0, transition: Effect.Transitions.flicker, @@ -645,7 +646,7 @@ Effect.SwitchOff = function(element) { } }) } - }); + }, arguments[1] || {})); } Effect.DropOut = function(element) { @@ -729,7 +730,7 @@ Effect.SlideDown = function(element) { }, arguments[1] || {}) ); } - + Effect.SlideUp = function(element) { element = $(element); element.cleanWhitespace(); diff --git a/framework/Web/Javascripts/effects/slider.js b/framework/Web/Javascripts/effects/slider.js index c0f1fc01..696992ca 100644 --- a/framework/Web/Javascripts/effects/slider.js +++ b/framework/Web/Javascripts/effects/slider.js @@ -64,7 +64,12 @@ Control.Slider.prototype = { this.alignY = parseInt(this.options.alignY || '0'); this.trackLength = this.maximumOffset() - this.minimumOffset(); - this.handleLength = this.isVertical() ? this.handles[0].offsetHeight : this.handles[0].offsetWidth; + + this.handleLength = this.isVertical() ? + (this.handles[0].offsetHeight != 0 ? + this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) : + (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth : + this.handles[0].style.width.replace(/px$/,"")); this.active = false; this.dragging = false; @@ -137,8 +142,8 @@ Control.Slider.prototype = { }, setValue: function(sliderValue, handleIdx){ if(!this.active) { - this.activeHandle = this.handles[handleIdx]; - this.activeHandleIdx = handleIdx; + this.activeHandleIdx = handleIdx || 0; + this.activeHandle = this.handles[this.activeHandleIdx]; this.updateStyles(); } handleIdx = handleIdx || this.activeHandleIdx || 0; @@ -180,8 +185,11 @@ Control.Slider.prototype = { return(this.isVertical() ? this.alignY : this.alignX); }, maximumOffset: function(){ - return(this.isVertical() ? - this.track.offsetHeight - this.alignY : this.track.offsetWidth - this.alignX); + return(this.isVertical() ? + (this.track.offsetHeight != 0 ? this.track.offsetHeight : + this.track.style.height.replace(/px$/,"")) - this.alignY : + (this.track.offsetWidth != 0 ? this.track.offsetWidth : + this.track.style.width.replace(/px$/,"")) - this.alignY); }, isVertical: function(){ return (this.axis == 'vertical'); @@ -217,7 +225,8 @@ Control.Slider.prototype = { var handle = Event.element(event); var pointer = [Event.pointerX(event), Event.pointerY(event)]; - if(handle==this.track) { + var track = handle; + if(track==this.track) { var offsets = Position.cumulativeOffset(this.track); this.event = event; this.setValue(this.translateToValue( diff --git a/framework/Web/Javascripts/prado/ajax.js b/framework/Web/Javascripts/prado/ajax.js index 06c3d741..4f1c1ec9 100644 --- a/framework/Web/Javascripts/prado/ajax.js +++ b/framework/Web/Javascripts/prado/ajax.js @@ -1,4 +1,4 @@ -/** +/** * Prado AJAX service. The default service provider is JPSpan. */ Prado.AJAX = { Service : 'Prototype' }; @@ -11,10 +11,10 @@ Prado.AJAX.EvalScript = function(output) var match = new RegExp(Ajax.Updater.ScriptFragment, 'img'); var scripts = output.match(match); - if (scripts) + if (scripts) { match = new RegExp(Ajax.Updater.ScriptFragment, 'im'); - setTimeout((function() + setTimeout((function() { for (var i = 0; i < scripts.length; i++) eval(scripts[i].match(match)[1]); @@ -27,21 +27,21 @@ Prado.AJAX.EvalScript = function(output) * AJAX service request using Prototype's AJAX request class. */ Prado.AJAX.Request = Class.create(); -Prado.AJAX.Request.prototype = Object.extend(Ajax.Request.prototype, +Prado.AJAX.Request.prototype = Object.extend(Ajax.Request.prototype, { /** * Evaluate the respond JSON data, override parent implementing. * If default eval fails, try parsing the JSON data (slower). */ - evalJSON: function() + evalJSON: function() { - try + try { var json = this.transport.getResponseHeader('X-JSON'), object; object = eval(json); return object; - } - catch (e) + } + catch (e) { if(isString(json)) { @@ -49,15 +49,15 @@ Prado.AJAX.Request.prototype = Object.extend(Ajax.Request.prototype, } } }, - + respondToReadyState: function(readyState) { var event = Ajax.Request.Events[readyState]; var transport = this.transport, json = this.evalJSON(); - + if(event == 'Complete' && transport.status) Ajax.Responders.dispatch('on' + transport.status, this, transport, json); - + (this.options['on' + event] || Prototype.emptyFunction)(transport, json); Ajax.Responders.dispatch('on' + event, this, transport, json); @@ -66,15 +66,15 @@ Prado.AJAX.Request.prototype = Object.extend(Ajax.Request.prototype, || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')] || Prototype.emptyFunction)(transport, json); - + /* Avoid memory leak in MSIE: clean up the oncomplete event handler */ if (event == 'Complete') this.transport.onreadystatechange = Prototype.emptyFunction; } - + }); -Prado.AJAX.Error = function(e, code) +Prado.AJAX.Error = function(e, code) { e.name = 'Prado.AJAX.Error'; e.code = code; @@ -85,7 +85,7 @@ Prado.AJAX.Error = function(e, code) * Post data builder, serialize the data using JSON. */ Prado.AJAX.RequestBuilder = Class.create(); -Prado.AJAX.RequestBuilder.prototype = +Prado.AJAX.RequestBuilder.prototype = { initialize : function() { @@ -96,13 +96,13 @@ Prado.AJAX.RequestBuilder.prototype = { return Prado.AJAX.JSON.stringify(data); }, - build : function(data) + build : function(data) { var sep = ''; - for ( var argName in data) + for ( var argName in data) { if(isFunction(data[argName])) continue; - try + try { this.body += sep + argName + '='; this.body += encodeURIComponent(this.encode(data[argName])); @@ -110,9 +110,9 @@ Prado.AJAX.RequestBuilder.prototype = throw Prado.AJAX.Error(e, 1006); } sep = '&'; - } + } }, - + getAll : function() { this.build(this.data); @@ -141,7 +141,7 @@ Prado.AJAX.RemoteObject.Request.prototype = Object.extend(Prado.AJAX.Request.pro }, /** - * Call the remote object, + * Call the remote object, * @param string the remote server url * @param array additional arguments */ @@ -165,7 +165,7 @@ Prado.AJAX.RemoteObject.Request.prototype = Object.extend(Prado.AJAX.Request.pro /** * Base proxy class for Prado RemoteObjects via AJAX. - * e.g. + * e.g. * * var TestObject1 = Class.create(); * TestObject1.prototype = Object.extend(new Prado.AJAX.RemoteObject(), @@ -182,18 +182,18 @@ Prado.AJAX.RemoteObject.Request.prototype = Object.extend(Prado.AJAX.Request.pro * } * }); * - * And client usage, + * And client usage, * * var test1 = new TestObject1(); //create new remote object * test1.method1(); //call the method, no onComplete hook * * var onComplete = { method1 : function(result){ alert(result) } }; * //create new remote object with onComplete callback - * var test2 = new TestObject1(onComplete); + * var test2 = new TestObject1(onComplete); * test2.method1(); //call it, on success, onComplete's method1 is called. * */ -Prado.AJAX.RemoteObject.prototype = +Prado.AJAX.RemoteObject.prototype = { baseInitialize : function(handlers, options) { @@ -207,11 +207,11 @@ Prado.AJAX.RemoteObject.prototype = this.__callback = method; return this.__service.invokeRemoteObject(url+"/"+method, args); }, - + __onSuccess : function(transport, json) { if(this.__handlers[this.__callback]) - this.__handlers[this.__callback](json, transport.responseText); + this.__handlers[this.__callback](json, transport.responseText); } }; @@ -224,12 +224,12 @@ Prado.AJAX.Exception = * Server returns 505 exception. Just log it. */ "on505" : function(request, transport, e) - { + { var msg = 'HTTP '+transport.status+" with response"; Logger.error(msg, transport.responseText); Logger.exception(e); }, - + onComplete : function(request, transport, e) { if(transport.status != 505) @@ -265,8 +265,8 @@ Prado.AJAX.Exception = //Add HTTP exception respones when logger is enabled. Event.OnLoad(function() -{ - if(typeof Logger != "undefined") +{ + if(typeof Logger != "undefined") { Logger.exception = Prado.AJAX.Exception.logException; Ajax.Responders.register(Prado.AJAX.Exception); @@ -274,7 +274,7 @@ Event.OnLoad(function() }); /** - * Prado Callback service that provides component intergration, + * Prado Callback service that provides component intergration, * viewstate (read only), and automatic form data serialization. * Usage: new Prado.AJAX.Callback('MyPage.MyComponentID.raiseCallbackEvent', options) * These classes should be called by the components developers. @@ -283,7 +283,7 @@ Event.OnLoad(function() Prado.AJAX.Callback = Class.create(); Prado.AJAX.Callback.prototype = Object.extend(new Prado.AJAX.RemoteObject(), { - + /** * Create and request a new Prado callback service. * @param string|element the callback ID, must be of the form, ClassName.ComponentID.MethodName @@ -294,14 +294,14 @@ Prado.AJAX.Callback.prototype = Object.extend(new Prado.AJAX.RemoteObject(), { if(!isString(ID) && typeof(ID.id) != "undefined") ID = ID.id; - if(!isString(ID)) + if(!isString(ID)) throw new Error('A Control ID must be specified'); this.baseInitialize(this, options); this.options = options || []; this.__service.post.data['__ID'] = ID; this.requestCallback(); }, - + /** * Get form data for components that implements IPostBackHandler. */ @@ -313,7 +313,7 @@ Prado.AJAX.Callback.prototype = Object.extend(new Prado.AJAX.RemoteObject(), { var id = IDs[i]; if(id.indexOf("[]") > -1) - this.__service.post.data['__data'][id] = + this.__service.post.data['__data'][id] = this.collectArrayPostData(id); else if(isObject($(id))) this.__service.post.data['__data'][id] = $F(id); @@ -325,12 +325,12 @@ Prado.AJAX.Callback.prototype = Object.extend(new Prado.AJAX.RemoteObject(), var elements = document.getElementsByName(name); var data = []; $A(elements).each(function(el) - { - if($F(el)) data.push($F(el)); + { + if($F(el)) data.push($F(el)); }); return data; }, - + /** * Prepares and calls the AJAX request. * Collects the data from components that implements IPostBackHandler @@ -377,7 +377,7 @@ Prado.AJAX.Callback.Action = /** - * Returns false if validation required and validates to false, + * Returns false if validation required and validates to false, * returns true otherwise. * @return boolean true if validation passes. */ @@ -406,20 +406,22 @@ Prado.AJAX.Callback.IDs = []; /** * Simple AJAX callback interface, suitable for inline javascript. * e.g., Click me - * @param string callback ID - * @param array parameters to pass to the callback service + * @param {String} callback ID + * @param {Array} parameters to pass to the callback service + * @param {Function} on callback success handler method + * @param {Object} additional callback options */ Prado.Callback = function(ID, params, onSuccess, options) { - var callback = + var callback = { 'params' : [params] || [], - 'onSuccess' : onSuccess || Prototype.emptyFunction, + 'onSuccess' : onSuccess || Prototype.emptyFunction, 'CausesValidation' : true }; Object.extend(callback, options || {}); - + new Prado.AJAX.Callback(ID, callback); return false; } \ No newline at end of file diff --git a/framework/Web/UI/TClientScriptManager.php b/framework/Web/UI/TClientScriptManager.php index d1f3f457..504cfb40 100644 --- a/framework/Web/UI/TClientScriptManager.php +++ b/framework/Web/UI/TClientScriptManager.php @@ -623,13 +623,12 @@ abstract class TClientSideOptions extends TComponent /** * Ensure that the javascript statements are wrapped in a javascript - * function block. Default has no wrapping. Override this method to - * customize the wrapping javascript function block. + * function block as function(sender, parameter){ //code }. */ protected function ensureFunction($javascript) { - return $javascript; + return "function(sender, parameter){ {$javascript} }"; } } -?> +?> \ No newline at end of file diff --git a/framework/Web/UI/TControl.php b/framework/Web/UI/TControl.php index 4e9361aa..a347b0ba 100644 --- a/framework/Web/UI/TControl.php +++ b/framework/Web/UI/TControl.php @@ -941,7 +941,7 @@ class TControl extends TApplicationComponent implements IRenderable, IBindable { foreach($this->_rf[self::RF_CONTROLS] as $control) { - if($control instanceof $type) + if(is_object($control) && get_class($control)===$type) $controls[]=$control; if(($control instanceof TControl) && $control->getHasControls()) $controls=array_merge($controls,$control->findControlsByType($type)); diff --git a/framework/Web/UI/TTemplateControl.php b/framework/Web/UI/TTemplateControl.php index 3bbe52c5..7d1f2d7e 100644 --- a/framework/Web/UI/TTemplateControl.php +++ b/framework/Web/UI/TTemplateControl.php @@ -235,6 +235,8 @@ class TTemplateControl extends TCompositeControl foreach($this->_contents as $id=>$content) $master->injectContent($id,$content); } + else if(!empty($this->_contents)) + throw new TConfigurationException('templatecontrol_mastercontrol_required',get_class($this)); parent::initRecursive($namingContainer); } } diff --git a/framework/Web/UI/TThemeManager.php b/framework/Web/UI/TThemeManager.php index 245f8049..1d80d0c5 100644 --- a/framework/Web/UI/TThemeManager.php +++ b/framework/Web/UI/TThemeManager.php @@ -292,10 +292,10 @@ class TTheme extends TApplicationComponent implements ITheme $template=new TTemplate(file_get_contents($themePath.'/'.$file),$themePath,$themePath.'/'.$file); foreach($template->getItems() as $skin) { - if($skin[0]!==-1) - throw new TConfigurationException('theme_control_nested',$skin[1],dirname($themePath)); - else if(!isset($skin[2])) // a text string, ignored + if(!isset($skin[2])) // a text string, ignored continue; + else if($skin[0]!==-1) + throw new TConfigurationException('theme_control_nested',$skin[1],dirname($themePath)); $type=$skin[1]; $id=isset($skin[2]['skinid'])?$skin[2]['skinid']:0; unset($skin[2]['skinid']); diff --git a/framework/Web/UI/WebControls/TBaseValidator.php b/framework/Web/UI/WebControls/TBaseValidator.php index ff026b49..a6d044ff 100644 --- a/framework/Web/UI/WebControls/TBaseValidator.php +++ b/framework/Web/UI/WebControls/TBaseValidator.php @@ -85,7 +85,7 @@ abstract class TBaseValidator extends TLabel implements IValidator */ private $_registered=false; /** - * @var TClientSideValidatorOptions validator client-script options. + * @var TValidatorClientSide validator client-script options. */ private $_clientSide; /** @@ -167,7 +167,7 @@ abstract class TBaseValidator extends TLabel implements IValidator $options['ControlCssClass'] = $this->getControlCssClass(); $options['ControlType'] = $this->getClientControlClass($control); - + //get date format from date picker target control if($control instanceof TDatePicker) $options['DateFormat'] = $control->getDateFormat(); @@ -194,7 +194,7 @@ abstract class TBaseValidator extends TLabel implements IValidator } /** - * Gets the TClientSideValidatorOptions that allows modification of the client- + * Gets the TValidatorClientSide that allows modification of the client- * side validator events. * * The client-side validator supports the following events. @@ -207,21 +207,21 @@ abstract class TBaseValidator extends TLabel implements IValidator * * You can attach custom javascript code to each of these events * - * @return TClientSideValidatorOptions javascript validator event options. + * @return TValidatorClientSide javascript validator event options. */ public function getClientSide() { if(is_null($this->_clientSide)) - $this->_clientSide = $this->createClientSideOptions(); + $this->_clientSide = $this->createClientSide(); return $this->_clientSide; } /** - * @return TClientSideValidatorOptions javascript validator event options. + * @return TValidatorClientSide javascript validator event options. */ - protected function createClientSideOptions() + protected function createClientSide() { - return new TClientSideValidatorOptions; + return new TValidatorClientSide; } /** @@ -568,11 +568,11 @@ abstract class TBaseValidator extends TLabel implements IValidator } /** - * TClientSideValidatorOptions class. + * TValidatorClientSide class. * * Client-side validator events can be modified through the {@link * TBaseValidator::getClientSide ClientSide} property of a validator. The - * subproperties of ClientSide are those of the TClientSideValidatorOptions + * subproperties of ClientSide are those of the TValidatorClientSide * properties. The client-side validator supports the following events. * * The OnValidate event is raise before the validator validation @@ -590,7 +590,7 @@ abstract class TBaseValidator extends TLabel implements IValidator * @package System.Web.UI.WebControls * @since 3.0 */ -class TClientSideValidatorOptions extends TClientSideOptions +class TValidatorClientSide extends TClientSideOptions { /** * @return string javascript code for client-side OnValidate event. @@ -659,10 +659,8 @@ class TClientSideValidatorOptions extends TClientSideOptions */ public function getObserveChanges() { - if(($option=$this->getOption('ObserveChanges'))!==null) - return $option; - else - return true; + $changes = $this->getOption('ObserveChanges'); + return is_null($changes) ? true : $changes; } /** @@ -678,4 +676,4 @@ class TClientSideValidatorOptions extends TClientSideOptions } } -?> \ No newline at end of file +?> diff --git a/framework/Web/UI/WebControls/TBoundColumn.php b/framework/Web/UI/WebControls/TBoundColumn.php index 5975ec02..26fad32b 100644 --- a/framework/Web/UI/WebControls/TBoundColumn.php +++ b/framework/Web/UI/WebControls/TBoundColumn.php @@ -26,6 +26,15 @@ Prado::using('System.Web.UI.WebControls.TDataGridColumn'); * If {@link setReadOnly ReadOnly} is false, TBoundColumn will display cells in edit mode * with textboxes. Otherwise, a static text is displayed. * + * When a datagrid row is in edit mode, the textbox control in the TBoundColumn + * can be accessed by one of the following two methods: + * + * $datagridItem->BoundColumnID->TextBox + * $datagridItem->BoundColumnID->Controls[0] + * + * The second method is possible because the textbox control created within the + * datagrid cell is the first child. + * * @author Qiang Xue * @version $Revision: $ $Date: $ * @package System.Web.UI.WebControls @@ -103,6 +112,7 @@ class TBoundColumn extends TDataGridColumn { $textBox=Prado::createComponent('System.Web.UI.WebControls.TTextBox'); $cell->getControls()->add($textBox); + $cell->registerObject('TextBox',$textBox); $control=$textBox; } if(($dataField=$this->getDataField())!=='') diff --git a/framework/Web/UI/WebControls/TButtonColumn.php b/framework/Web/UI/WebControls/TButtonColumn.php index 20844f14..fdcf58d3 100644 --- a/framework/Web/UI/WebControls/TButtonColumn.php +++ b/framework/Web/UI/WebControls/TButtonColumn.php @@ -39,6 +39,14 @@ Prado::using('System.Web.UI.WebControls.TImageButton'); * The buttons' CausesValidation and ValidationGroup property values * are determined by the column's corresponding properties. * + * The buttons in the column can be accessed by one of the following two methods: + * + * $datagridItem->ButtonColumnID->Button + * $datagridItem->ButtonColumnID->Controls[0] + * + * The second method is possible because the button control created within the + * datagrid cell is the first child. + * * @author Qiang Xue * @version $Revision: $ $Date: $ * @package System.Web.UI.WebControls @@ -238,6 +246,7 @@ class TButtonColumn extends TDataGridColumn if($this->getDataTextField()!=='' || ($buttonType==='ImageButton' && $this->getDataImageUrlField()!=='')) $button->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); $cell->getControls()->add($button); + $cell->registerObject('Button',$button); } } diff --git a/framework/Web/UI/WebControls/TCheckBoxColumn.php b/framework/Web/UI/WebControls/TCheckBoxColumn.php index 4df1e695..d7fac8cf 100644 --- a/framework/Web/UI/WebControls/TCheckBoxColumn.php +++ b/framework/Web/UI/WebControls/TCheckBoxColumn.php @@ -28,6 +28,15 @@ Prado::using('System.Web.UI.WebControls.TCheckBox'); * TCheckBoxColumn will display an enabled checkbox provided the cells are * in edit mode. Otherwise, the checkboxes will be disabled to prevent from editting. * + * The checkbox control in the TCheckBoxColumn can be accessed by one of + * the following two methods: + * + * $datagridItem->CheckBoxColumnID->CheckBox + * $datagridItem->CheckBoxColumnID->Controls[0] + * + * The second method is possible because the checkbox control created within the + * datagrid cell is the first child. + * * @author Qiang Xue * @version $Revision: $ $Date: $ * @package System.Web.UI.WebControls @@ -87,6 +96,7 @@ class TCheckBoxColumn extends TDataGridColumn $checkBox->setEnabled(false); $cell->setHorizontalAlign('Center'); $cell->getControls()->add($checkBox); + $cell->registerObject('CheckBox',$checkBox); if($this->getDataField()!=='') $checkBox->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); } diff --git a/framework/Web/UI/WebControls/TDataGrid.php b/framework/Web/UI/WebControls/TDataGrid.php index e27f6ead..ad30ff3f 100644 --- a/framework/Web/UI/WebControls/TDataGrid.php +++ b/framework/Web/UI/WebControls/TDataGrid.php @@ -1075,6 +1075,11 @@ class TDataGrid extends TBaseDataList implements INamingContainer $cell=new TTableHeaderCell; else $cell=new TTableCell; + if(($id=$column->getID())!=='') + { + $cell->setID($id); + $item->registerObject($id,$cell); + } $column->initializeCell($cell,$index,$itemType); $cells->add($cell); $index++; diff --git a/framework/Web/UI/WebControls/TDatePicker.php b/framework/Web/UI/WebControls/TDatePicker.php index 001c7c46..0c3e0b9a 100644 --- a/framework/Web/UI/WebControls/TDatePicker.php +++ b/framework/Web/UI/WebControls/TDatePicker.php @@ -19,8 +19,6 @@ Prado::using('System.Web.UI.WebControls.TTextBox'); * * TDatePicker class. * - * Be aware, this control is EXPERIMENTAL and is not stablized yet. - * * TDatePicker displays a text box for date input purpose. * When the text box receives focus, a calendar will pop up and users can * pick up from it a date that will be automatically entered into the text box. @@ -69,14 +67,18 @@ Prado::using('System.Web.UI.WebControls.TTextBox'); class TDatePicker extends TTextBox { /** + * @var TDatePickerClientScript validator client-script options. + */ + private $_clientScript; + /** * AutoPostBack is not supported. */ public function setAutoPostBack($value) { throw new TNotSupportedException('tdatepicker_autopostback_unsupported', - get_class($this)); + get_class($this)); } - + /** * @return string the format of the date string */ @@ -278,7 +280,7 @@ class TDatePicker extends TTextBox { return $this->getText(); } - + /** * @param string date string */ @@ -287,6 +289,31 @@ class TDatePicker extends TTextBox $this->setText($value); } + /** + * Gets the TDatePickerClientScript to set the TDatePicker event handlers. + * + * The date picker on the client-side supports the following events. + * # OnDateChanged -- raised when the date is changed. + * + * You can attach custom javascript code to each of these events + * + * @return TDatePickerClientScript javascript validator event options. + */ + public function getClientSide() + { + if(is_null($this->_clientScript)) + $this->_clientScript = $this->createClientScript(); + return $this->_clientScript; + } + + /** + * @return TDatePickerClientScript javascript validator event options. + */ + protected function createClientScript() + { + return new TDatePickerClientScript; + } + /** * Returns the value to be validated. * This methid is required by IValidatable interface. @@ -394,7 +421,7 @@ class TDatePicker extends TTextBox $year = intval($values[$key.'$year']); else $year = $date['year']; - + $date = @mktime(0, 0, 0, $month, $day, $year); $pattern = $this->getDateFormat(); @@ -421,6 +448,9 @@ class TDatePicker extends TTextBox $options['Trigger'] = $this->getDatePickerButtonID(); $options = array_merge($options, $this->getCulturalOptions()); + if(!is_null($this->_clientScript)) + $options = array_merge($options, + $this->_clientScript->getOptions()->toArray()); return $options; } @@ -583,7 +613,7 @@ class TDatePicker extends TTextBox $writer->addAttribute('name', $this->getUniqueID().'$month'); $writer->addAttribute('class', 'datepicker_month_options'); if($this->getReadOnly() || !$this->getEnabled(true)) - $writer->addAttribute('disabled', 'disabled'); + $writer->addAttribute('disabled', 'disabled'); $writer->renderBeginTag('select'); $this->renderDropDownListOptions($writer, $this->getLocalizedMonthNames($info), $selected-1); @@ -604,8 +634,8 @@ class TDatePicker extends TTextBox switch($formatter->getMonthPattern()) { case 'MMM': return $info->getAbbreviatedMonthNames(); - case 'MM': - $array = array(); + case 'MM': + $array = array(); for($i=1;$i<=12;$i++) $array[$i-1] = $i < 10 ? '0'.$i : $i; return $array; @@ -629,7 +659,7 @@ class TDatePicker extends TTextBox $writer->addAttribute('id', $this->getClientID().'_year'); $writer->addAttribute('name', $this->getUniqueID().'$year'); if($this->getReadOnly() || !$this->getEnabled(true)) - $writer->addAttribute('disabled', 'disabled'); + $writer->addAttribute('disabled', 'disabled'); $writer->renderBeginTag('select'); $writer->addAttribute('class', 'datepicker_year_options'); $this->renderDropDownListOptions($writer, $years, $selected); @@ -705,7 +735,7 @@ class TDatePicker extends TTextBox else throw new TConfigurationException('datepicker_calendarstyle_invalid',$style); } - + /** * Publish the spacer.gif for IE iframe source. * @return string the URL for the spacer.gif. @@ -739,16 +769,54 @@ class TDatePicker extends TTextBox { $cs = $this->getPage()->getClientScript(); $cs->registerPradoScript("datepicker"); - + if(!$cs->isEndScriptRegistered('TDatePicker.spacer')) { $spacer = $this->publishIFrameSpacer(); $code = "Prado.WebUI.TDatePicker.spacer = '$spacer';"; $cs->registerEndScript('TDatePicker.spacer', $code); } - $cs->registerPostBackControl('Prado.WebUI.TDatePicker', $this->getDatePickerOptions()); + + $options = TJavaScript::encode($this->getDatePickerOptions()); + $code = "new Prado.WebUI.TDatePicker($options);"; + $cs->registerEndScript("prado:".$this->getClientID(), $code); } } } +/** + * TDatePickerClientScript class. + * + * Client-side date picker event {@link setOnDateChanged OnDateChanged} + * can be modified through the {@link TDatePicker:: getClientSide ClientSide} + * property of a date picker. + * + * The OnDateChanged event is raise when the date picker's date + * is changed. + * + * @author Wei Zhuo + * @version $Revision: $ $Date: $ + * @package System.Web.UI.WebControls + * @since 3.0.4 + */ +class TDatePickerClientScript extends TClientSideOptions +{ + /** + * Javascript code to execute when the date picker's date is changed. + * @param string javascript code + */ + public function setOnDateChanged($javascript) + { + $this->setFunction('OnDateChanged', $javascript); + } + + /** + * @return string javascript code to execute when the date picker's date is changed. + */ + public function getOnDateChanged() + { + return $this->getOption('OnDateChanged'); + } +} + ?> diff --git a/framework/Web/UI/WebControls/TEditCommandColumn.php b/framework/Web/UI/WebControls/TEditCommandColumn.php index 65e389bd..c742ef11 100644 --- a/framework/Web/UI/WebControls/TEditCommandColumn.php +++ b/framework/Web/UI/WebControls/TEditCommandColumn.php @@ -38,6 +38,14 @@ Prado::using('System.Web.UI.WebControls.TDataGridColumn'); * properties affect the corresponding properties of the edit and update buttons. * The cancel button does not cause validation by default. * + * The command buttons in the column can be accessed by one of the following methods: + * + * $datagridItem->ButtonColumnID->EditButton (or UpdateButton, CancelButton) + * $datagridItem->ButtonColumnID->Controls[0] + * + * The second method is possible because the button control created within the + * datagrid cell is the first child. + * * @author Qiang Xue * @version $Revision: $ $Date: $ * @package System.Web.UI.WebControls @@ -154,13 +162,21 @@ class TEditCommandColumn extends TDataGridColumn { parent::initializeCell($cell,$columnIndex,$itemType); if($itemType===TDataGrid::IT_ITEM || $itemType===TDataGrid::IT_ALTERNATINGITEM || $itemType===TDataGrid::IT_SELECTEDITEM) - $cell->getControls()->add($this->createButton('Edit',$this->getEditText(),false,'')); + { + $button=$this->createButton('Edit',$this->getEditText(),false,''); + $cell->getControls()->add($button); + $cell->registerObject('EditButton',$button); + } else if($itemType===TDataGrid::IT_EDITITEM) { $controls=$cell->getControls(); - $controls->add($this->createButton('Update',$this->getUpdateText(),$this->getCausesValidation(),$this->getValidationGroup())); + $button=$this->createButton('Update',$this->getUpdateText(),$this->getCausesValidation(),$this->getValidationGroup()); + $controls->add($button); + $cell->registerObject('UpdateButton',$button); $controls->add(' '); - $controls->add($this->createButton('Cancel',$this->getCancelText(),false,'')); + $button=$this->createButton('Cancel',$this->getCancelText(),false,''); + $controls->add($button); + $cell->registerObject('CancelButton',$button); } } diff --git a/framework/Web/UI/WebControls/THyperLinkColumn.php b/framework/Web/UI/WebControls/THyperLinkColumn.php index 9fc1e3d2..b8dbb607 100644 --- a/framework/Web/UI/WebControls/THyperLinkColumn.php +++ b/framework/Web/UI/WebControls/THyperLinkColumn.php @@ -36,6 +36,14 @@ Prado::using('System.Web.UI.WebControls.THyperLink'); * The same rule applies to {@link setNavigateUrl NavigateUrl} and * {@link setDataNavigateUrlField DataNavigateUrlField} properties. * + * The hyperlinks in the column can be accessed by one of the following two methods: + * + * $datagridItem->HyperLinkColumnID->HyperLink + * $datagridItem->HyperLinkColumnID->Controls[0] + * + * The second method is possible because the hyperlink control created within the + * datagrid cell is the first child. + * * @author Qiang Xue * @version $Revision: $ $Date: $ * @package System.Web.UI.WebControls @@ -178,6 +186,7 @@ class THyperLinkColumn extends TDataGridColumn if($this->getDataTextField()!=='' || $this->getDataNavigateUrlField()!=='') $link->attachEventHandler('OnDataBinding',array($this,'dataBindColumn')); $cell->getControls()->add($link); + $cell->registerObject('HyperLink',$link); } } diff --git a/framework/Web/UI/WebControls/TListControl.php b/framework/Web/UI/WebControls/TListControl.php index 8c1537b0..76c59903 100644 --- a/framework/Web/UI/WebControls/TListControl.php +++ b/framework/Web/UI/WebControls/TListControl.php @@ -94,6 +94,8 @@ abstract class TListControl extends TDataBoundControl */ private $_cachedSelectedIndex=-1; private $_cachedSelectedValue=null; + private $_cachedSelectedIndices=null; + private $_cachedSelectedValues=null; /** * @return string tag name of the list control @@ -213,20 +215,34 @@ abstract class TListControl extends TDataBoundControl // so we make them be effective now if($this->_cachedSelectedValue!==null) { - $index=$items->findIndexByValue($this->_cachedSelectedValue); - if($index===-1 || ($this->_cachedSelectedIndex!==-1 && $this->_cachedSelectedIndex!==$index)) - throw new TInvalidDataValueException('listcontrol_selection_invalid',get_class($this)); - $this->setSelectedIndex($index); - $this->_cachedSelectedValue=null; - $this->_cachedSelectedIndex=-1; + $this->setSelectedValue($this->_cachedSelectedValue); + $this->resetCachedSelections(); } else if($this->_cachedSelectedIndex!==-1) { $this->setSelectedIndex($this->_cachedSelectedIndex); - $this->_cachedSelectedIndex=-1; + $this->resetCachedSelections(); + } + else if($this->_cachedSelectedValues!==null) + { + $this->setSelectedValues($this->_cachedSelectedValues); + $this->resetCachedSelections(); + } + else if($this->_cachedSelectedIndices!==null) + { + $this->setSelectedIndices($this->_cachedSelectedIndices); + $this->resetCachedSelections(); } } + private function resetCachedSelections() + { + $this->_cachedSelectedValue=null; + $this->_cachedSelectedIndex=-1; + $this->_cachedSelectedValues=null; + $this->_cachedSelectedIndices=null; + } + /** * Creates a collection object to hold list items. * This method may be overriden to create a customized collection. @@ -461,16 +477,22 @@ abstract class TListControl extends TDataBoundControl */ public function setSelectedIndices($indices) { - if($this->_items) + if($this->getIsMultiSelect()) { - $this->clearSelection(); - $n=$this->_items->getCount(); - foreach($indices as $index) + if($this->_items) { - if($index>=0 && $index<$n) - $this->_items->itemAt($index)->setSelected(true); + $this->clearSelection(); + $n=$this->_items->getCount(); + foreach($indices as $index) + { + if($index>=0 && $index<$n) + $this->_items->itemAt($index)->setSelected(true); + } } + $this->_cachedSelectedIndices=$indices; } + else + throw new TNotSupportedException('listcontrol_multiselect_unsupported',get_class($this)); if($this->getAdapter() instanceof IListControlAdapter) $this->getAdapter()->setSelectedIndices($indices); @@ -544,20 +566,26 @@ abstract class TListControl extends TDataBoundControl */ public function setSelectedValues($values) { - if($this->_items) + if($this->getIsMultiSelect()) { - $this->clearSelection(); - $lookup=array(); - foreach($this->_items as $item) - $lookup[$item->getValue()]=$item; - foreach($values as $value) + if($this->_items) { - if(isset($lookup["$value"])) - $lookup["$value"]->setSelected(true); - else - throw new TInvalidDataValueException('listcontrol_selectedvalue_invalid',get_class($this),$value); + $this->clearSelection(); + $lookup=array(); + foreach($this->_items as $item) + $lookup[$item->getValue()]=$item; + foreach($values as $value) + { + if(isset($lookup["$value"])) + $lookup["$value"]->setSelected(true); + else + throw new TInvalidDataValueException('listcontrol_selectedvalue_invalid',get_class($this),$value); + } } + $this->_cachedSelectedValues=$values; } + else + throw new TNotSupportedException('listcontrol_multiselect_unsupported',get_class($this)); if($this->getAdapter() instanceof IListControlAdapter) $this->getAdapter()->setSelectedValues($values); diff --git a/framework/Web/UI/WebControls/TValidationSummary.php b/framework/Web/UI/WebControls/TValidationSummary.php index cfb57c5b..796a62f0 100644 --- a/framework/Web/UI/WebControls/TValidationSummary.php +++ b/framework/Web/UI/WebControls/TValidationSummary.php @@ -40,7 +40,7 @@ class TValidationSummary extends TWebControl * @var TClientSideValidationSummaryOptions validation client side options. */ private $_clientSide; - + /** * Constructor. * This method sets the foreground color to red. @@ -232,7 +232,7 @@ class TValidationSummary extends TWebControl if($this->getEnableClientScript() && !$cs->isEndScriptRegistered($scriptKey)) { $manager['FormID'] = $formID; - $options = TJavaScript::encode($manager); + $options = TJavaScript::encode($manager); $cs->registerPradoScript('validator'); $cs->registerEndScript($scriptKey, "new Prado.ValidationManager({$options});"); } @@ -262,7 +262,7 @@ class TValidationSummary extends TWebControl $options['Refresh'] = $this->getAutoUpdate(); $options['ValidationGroup'] = $this->getValidationGroup(); $options['Display'] = $this->getDisplay(); - + if(!is_null($this->_clientSide)) $options = array_merge($options,$this->_clientSide->getOptions()->toArray()); @@ -279,7 +279,7 @@ class TValidationSummary extends TWebControl $this->_clientSide = $this->createClientScript(); return $this->_clientSide; } - + /** * @return TClientSideValidationSummaryOptions javascript validation summary * event options. @@ -388,20 +388,20 @@ class TValidationSummary extends TWebControl /** * TClientSideValidationSummaryOptions class. - * + * * Client-side validation summary events such as {@link setOnHideSummary * OnHideSummary} and {@link setOnShowSummary OnShowSummary} can be modified * through the {@link TBaseValidator:: getClientSide ClientSide} property of a * validation summary. - * + * * The OnHideSummary event is raise when the validation summary * requests to hide the messages. - * + * * The OnShowSummary event is raised when the validation summary * requests to show the messages. - * + * * See the quickstart documentation for further details. - * + * * @author Wei Zhuo * @version $Revision: $ $Date: $ * @package System.Web.UI.WebControls @@ -416,7 +416,7 @@ class TClientSideValidationSummaryOptions extends TClientSideOptions { return $this->getOption('OnHideSummary'); } - + /** * Client-side OnHideSummary validation summary event is raise when all the * validators are valid. This will override the default client-side @@ -427,7 +427,7 @@ class TClientSideValidationSummaryOptions extends TClientSideOptions { $this->setFunction('OnHideSummary', $javascript); } - + /** * Client-side OnShowSummary event is raise when one or more validators are * not valid. This will override the default client-side validation summary @@ -438,7 +438,7 @@ class TClientSideValidationSummaryOptions extends TClientSideOptions { $this->setFunction('OnShowSummary', $javascript); } - + /** * @return string javascript code for client-side OnShowSummary event. */ @@ -460,4 +460,4 @@ class TClientSideValidationSummaryOptions extends TClientSideOptions } } -?> \ No newline at end of file +?> -- cgit v1.2.3