form-dateselector MDL-23096 Converted the date selector to a YUI-Moodle module
authorSam Hemelryk <sam@moodle.com>
Wed, 4 Aug 2010 09:13:27 +0000 (09:13 +0000)
committerSam Hemelryk <sam@moodle.com>
Wed, 4 Aug 2010 09:13:27 +0000 (09:13 +0000)
lib/form/yui/dateselector/assets/skins/sam/dateselector.css [new file with mode: 0644]
lib/form/yui/dateselector/dateselector.js [new file with mode: 0644]
lib/formslib.php
lib/javascript-static.js
theme/base/style/core.css

diff --git a/lib/form/yui/dateselector/assets/skins/sam/dateselector.css b/lib/form/yui/dateselector/assets/skins/sam/dateselector.css
new file mode 100644 (file)
index 0000000..d5b39ba
--- /dev/null
@@ -0,0 +1,5 @@
+.dateselector-select-mask-point {background-color:transparent;position:absolute;width:0;display:inline;}
+.dateselector-select-mask-point .dateselector-select-mask {background-color:#FFF;position:relative;}
+#dateselector-calendar-panel {background-color:#999;border-bottom:3px solid #999;border-right:3px solid #999;}
+#dateselector-calendar-content {border:1px solid #666;margin-top:-3px;margin-left:-3px;}
+body.ie #dateselector-calendar-panel.yui3-overlay-hidden table {display:none;}
\ No newline at end of file
diff --git a/lib/form/yui/dateselector/dateselector.js b/lib/form/yui/dateselector/dateselector.js
new file mode 100644 (file)
index 0000000..b1d5ced
--- /dev/null
@@ -0,0 +1,285 @@
+YUI.add('moodle-form-dateselector', function(Y) {
+
+    /**
+     * Add some custom methods to the node class to make our lives a little
+     * easier within this module.
+     */
+    Y.mix(Y.Node.prototype, {
+        /**
+         * Gets the value of the first option in the select box
+         */
+        firstOptionValue : function() {
+            if (this.get('nodeName').toLowerCase() != 'select') {
+                return false;
+            }
+            return this.one('option').get('value');
+        },
+        /**
+         * Gets the value of the last option in the select box
+         */
+        lastOptionValue : function() {
+            if (this.get('nodeName').toLowerCase() != 'select') {
+                return false;
+            }
+            return this.all('option').item(this.optionSize()-1).get('value');
+        },
+        /**
+         * Gets the number of options in the select box
+         */
+        optionSize : function() {
+            if (this.get('nodeName').toLowerCase() != 'select') {
+                return false;
+            }
+            return parseInt(this.all('option').size());
+        },
+        /**
+         * Gets the value of the selected option in the select box
+         */
+        selectedOptionValue : function() {
+            if (this.get('nodeName').toLowerCase() != 'select') {
+                return false;
+            }
+            return this.all('option').item(this.get('selectedIndex')).get('value');
+        }
+    });
+
+    /**
+     * Override the default inDoc method as it is broken in IE
+     *
+     * YUI Bug: http://yuilibrary.com/projects/yui3/ticket/2529157
+     * Example location: lib/yui/3.1.1/build/dom/dom-debug.js
+     */
+    Y.DOM.inDoc = function(element, doc) {
+        // there may be multiple elements with the same ID
+        doc = doc || element['ownerDocument'];
+        var nodes = [],
+            ret = false,
+            i,
+            node;
+
+        if (!element.getAttribute('id')) {
+            element.setAttribute('id', Y.guid());
+        }
+        nodes = Y.DOM.allById(element.id, doc);
+        for (i = 0; node = nodes[i++];) { // check for a match
+            if (node === element) {
+                ret = true;
+                break;
+            }
+        }
+        return ret;
+    }
+
+    /**
+     * Calendar class
+     *
+     * This is our main class
+     */
+    var CALENDAR = function(config) {
+        CALENDAR.superclass.constructor.apply(this, arguments);
+    }
+    CALENDAR.prototype = {
+        panel : null,
+        yearselect : null,
+        yearselectchange : null,
+        monthselect : null,
+        monthselectchange : null,
+        dayselect : null,
+        dayselectchange : null,
+        enablecheckbox : null,
+        initializer : function(config) {
+            var controls = this.get('node').all('select');
+            controls.each(function(node){
+                if (node.get('name').match(/\[year]/)) {
+                    this.yearselect = node;
+                } else if (node.get('name').match(/\[month\]/)) {
+                    this.monthselect = node;
+                } else if (node.get('name').match(/\[day]/)) {
+                    this.dayselect = node;
+                } else {
+                    node.on('focus', this.cancel_any_timeout, this);
+                    node.on('blur', this.blur_event, this);
+                    return;
+                }
+                node.maskDiv = Y.Node.create('<div class="dateselector-select-mask"></div>');
+                node.maskDiv.on('click', this.focus_event, this);
+                node.ancestor().insert(
+                    Y.Node.create('<div class="dateselector-select-mask-point"></div>').append(
+                        node.maskDiv.setStyles({
+                            width : node.get('offsetWidth')+'px',
+                            height : node.get('offsetHeight')+'px',
+                            opacity : 0
+                        })), node);
+            }, this);
+
+            if (this.yearselect && this.monthselect && this.dayselect) {
+                this.enablecheckbox = this.get('node').one('input');
+                if (this.enablecheckbox) {
+                    this.enablecheckbox.on('focus', this.focus_event, this);
+                    this.enablecheckbox.on('change', this.focus_event, this);
+                    this.enablecheckbox.on('blur', this.blur_event, this);
+                }
+            }
+        },
+        focus_event : function(e) {
+            M.form.dateselector.cancel_any_timeout();
+            if (this.enablecheckbox == null || this.enablecheckbox.get('checked')) {
+                this.claim_calendar();
+            } else {
+                if (M.form.dateselector.currentowner) {
+                    M.form.dateselector.currentowner.release_calendar();
+                }
+            }
+        },
+        blur_event : function(e) {
+            M.form.dateselector.hidetimeout = setTimeout(M.form.dateselector.release_current, 300);
+        },
+        handle_select_change : function(e) {
+            this.set_date_from_selects();
+        },
+        claim_calendar : function() {
+            M.form.dateselector.cancel_any_timeout();
+            if (M.form.dateselector.currentowner == this) {
+                return;
+            }
+            if (M.form.dateselector.currentowner) {
+                M.form.dateselector.currentowner.release_calendar();
+            }
+
+            if (M.form.dateselector.currentowner != this) {
+                this.connect_handlers();
+                this.set_date_from_selects();
+            }
+            M.form.dateselector.currentowner = this;
+            M.form.dateselector.calendar.cfg.setProperty('mindate', new Date(this.yearselect.firstOptionValue(), 0, 1));
+            M.form.dateselector.calendar.cfg.setProperty('maxdate', new Date(this.yearselect.lastOptionValue(), 11, 31));
+            M.form.dateselector.panel.set('constrain', this.get('node').ancestor('form'));
+            M.form.dateselector.panel.show();
+            M.form.dateselector.fix_position();
+            setTimeout(function(){M.form.dateselector.cancel_any_timeout()}, 100);
+        },
+        set_date_from_selects : function() {
+            var year = parseInt(this.yearselect.get('value'));
+            var month = parseInt(this.monthselect.get('value')) - 1;
+            var day = parseInt(this.dayselect.get('value'));
+            M.form.dateselector.calendar.select(new Date(year, month, day));
+            M.form.dateselector.calendar.setMonth(month);
+            M.form.dateselector.calendar.setYear(year);
+            M.form.dateselector.calendar.render();
+        },
+        set_selects_from_date : function(eventtype, args) {
+            var date = args[0][0];
+            var newyear = date[0];
+            var newindex = newyear - this.yearselect.firstOptionValue();
+            this.yearselect.set('selectedIndex', newindex);
+            this.monthselect.set('selectedIndex', date[1] - this.monthselect.firstOptionValue());
+            this.dayselect.set('selectedIndex', date[2] - this.dayselect.firstOptionValue());
+        },
+        connect_handlers : function() {
+            M.form.dateselector.calendar.selectEvent.subscribe(this.set_selects_from_date, this, true);
+        },
+        release_calendar : function() {
+            M.form.dateselector.panel.hide();
+            M.form.dateselector.currentowner = null;
+            M.form.dateselector.calendar.selectEvent.unsubscribe(this.set_selects_from_date, this);
+        }
+    }
+    Y.extend(CALENDAR, Y.Base, CALENDAR.prototype, {
+        NAME : 'Date Selector',
+        ATTRS : {
+            firstdayofweek  : {
+                validator : Y.Lang.isString
+            },
+            node : {
+                setter : function(node) {
+                    return Y.one(node);
+                }
+            }
+        }
+    });
+
+    M.form = M.form || {};
+    M.form.dateselector = {
+        panel : null,
+        calendar : null,
+        currentowner : null,
+        hidetimeout : null,
+        repositiontimeout : null,
+        init_date_selectors : function(config) {
+            if (this.panel === null) {
+                this.initPanel(config);
+            }
+            Y.all('fieldset.fdate_time_selector').each(function(){
+                config.node = this;
+                new CALENDAR(config);
+            });
+            Y.all('fieldset.fdate_selector').each(function(){
+                config.node = this;
+                new CALENDAR(config);
+            });
+        },
+        initPanel : function(config) {
+            this.panel = new Y.Overlay({
+                visible : false,
+                constrain : true,
+                bodyContent : Y.Node.create('<div id="dateselector-calendar-content"></div>'),
+                id : 'dateselector-calendar-panel'
+            });
+            this.panel.render(document.body);
+            this.panel.on('heightChange', this.fix_position, this);
+
+            Y.one('#dateselector-calendar-panel').on('click', function(e){e.halt();});
+            Y.one(document.body).on('click', this.document_click, this);
+
+            this.calendar = new YAHOO.widget.Calendar(document.getElementById('dateselector-calendar-content'), {
+                iframe: false,
+                hide_blank_weeks: true,
+                start_weekday: config.firstdayofweek
+            });
+            this.calendar.changePageEvent.subscribe(function(){
+                this.fix_position();
+            }, this);
+        },
+        cancel_any_timeout : function() {
+            if (this.hidetimeout) {
+                clearTimeout(this.hidetimeout);
+                this.hidetimeout = null;
+            }
+            if (this.repositiontimeout) {
+                clearTimeout(this.repositiontimeout);
+                this.repositiontimeout = null;
+            }
+        },
+        delayed_reposition : function() {
+            if (this.repositiontimeout) {
+                clearTimeout(this.repositiontimeout);
+                this.repositiontimeout = null;
+            }
+            this.repositiontimeout = setTimeout(this.fix_position, 500);
+        },
+        fix_position : function() {
+            if (this.currentowner) {
+                this.panel.set('constrain', this.currentowner.get('node').ancestor('form'));
+                this.panel.set('align', {
+                    node:this.currentowner.get('node').one('select'),
+                    points:[Y.WidgetPositionAlign.BL, Y.WidgetPositionAlign.TL]
+                });
+            }
+        },
+        release_current : function() {
+            if (this.currentowner) {
+                this.currentowner.release_calendar();
+            }
+        },
+        document_click : function(e) {
+            if (this.currentowner) {
+                if (this.currentowner.get('node').ancestor('div').contains(e.target)) {
+                    setTimeout(function() {M.form.dateselector.cancel_any_timeout()}, 100);
+                } else {
+                    this.currentowner.release_calendar();
+                }
+            }
+        }
+    }
+    
+}, '@VERSION@', {requires:['base','node','overlay', 'yui2-calendar', 'moodle-form-dateselector-skin']});
\ No newline at end of file
index b5bf9fc..bd5bef3 100644 (file)
@@ -66,15 +66,16 @@ if (!empty($CFG->debug) and $CFG->debug >= DEBUG_ALL){
 /**
  *
  * @staticvar bool $done
+ * @global moodle_page $PAGE
  */
 function form_init_date_js() {
     global $PAGE;
     static $done = false;
     if (!$done) {
-        $PAGE->requires->yui2_lib('calendar');
-        $PAGE->requires->yui2_lib('container');
-        $PAGE->requires->js_function_call('init_date_selectors',
-                array(get_string('firstdayofweek', 'langconfig')));
+        $module   = 'moodle-form-dateselector';
+        $function = 'M.form.dateselector.init_date_selectors';
+        $config = array(array('firstdayofweek'=>get_string('firstdayofweek', 'langconfig')));
+        $PAGE->requires->yui_module($module, $function, $config);
         $done = true;
     }
 }
index 51f0739..09e37e8 100644 (file)
@@ -1025,218 +1025,6 @@ function unmaskPassword(id) {
   pw.parentNode.replaceChild(newpw, pw);
 }
 
-/**
- * Search a Moodle form to find all the fdate_time_selector and fdate_selector
- * elements, and add date_selector_calendar instance to each.
- */
-function init_date_selectors(firstdayofweek) {
-    var els = YAHOO.util.Dom.getElementsByClassName('fdate_time_selector', 'fieldset');
-    for (var i = 0; i < els.length; i++) {
-        new date_selector_calendar(els[i], firstdayofweek);
-    }
-    els = YAHOO.util.Dom.getElementsByClassName('fdate_selector', 'fieldset');
-    for (i = 0; i < els.length; i++) {
-        new date_selector_calendar(els[i], firstdayofweek);
-    }
-}
-
-/**
- * Constructor for a JavaScript object that connects to a fdate_time_selector
- * or a fdate_selector in a Moodle form, and shows a popup calendar whenever
- * that element has keyboard focus.
- * @param el the fieldset class="fdate_time_selector" or "fdate_selector".
- */
-function date_selector_calendar(el, firstdayofweek) {
-    // Ensure that the shared div and calendar exist.
-    if (!date_selector_calendar.panel) {
-        date_selector_calendar.panel = new YAHOO.widget.Panel('date_selector_calendar_panel',
-                {visible: false, draggable: false});
-        var div = document.createElement('div');
-        date_selector_calendar.panel.setBody(div);
-        date_selector_calendar.panel.render(document.body);
-
-        YAHOO.util.Event.addListener(document, 'click', date_selector_calendar.document_click);
-        date_selector_calendar.panel.showEvent.subscribe(function() {
-            date_selector_calendar.panel.fireEvent('changeContent');
-        });
-        date_selector_calendar.panel.hideEvent.subscribe(date_selector_calendar.release_current);
-
-        date_selector_calendar.calendar = new YAHOO.widget.Calendar(div,
-                {iframe: false, hide_blank_weeks: true, start_weekday: firstdayofweek});
-        date_selector_calendar.calendar.renderEvent.subscribe(function() {
-            date_selector_calendar.panel.fireEvent('changeContent');
-            date_selector_calendar.delayed_reposition();
-        });
-    }
-
-    this.fieldset = el;
-    var controls = el.getElementsByTagName('select');
-    for (var i = 0; i < controls.length; i++) {
-        if (/\[year\]$/.test(controls[i].name)) {
-            this.yearselect = controls[i];
-        } else if (/\[month\]$/.test(controls[i].name)) {
-            this.monthselect = controls[i];
-        } else if (/\[day\]$/.test(controls[i].name)) {
-            this.dayselect = controls[i];
-        } else {
-            YAHOO.util.Event.addFocusListener(controls[i], date_selector_calendar.cancel_any_timeout, this);
-            YAHOO.util.Event.addBlurListener(controls[i], this.blur_event, this);
-        }
-    }
-    if (!(this.yearselect && this.monthselect && this.dayselect)) {
-        throw 'Failed to initialise calendar.';
-    }
-    YAHOO.util.Event.addFocusListener([this.yearselect, this.monthselect, this.dayselect], this.focus_event, this);
-    YAHOO.util.Event.addBlurListener([this.yearselect, this.monthselect, this.dayselect], this.blur_event, this);
-
-    this.enablecheckbox = el.getElementsByTagName('input')[0];
-    if (this.enablecheckbox) {
-        YAHOO.util.Event.addFocusListener(this.enablecheckbox, this.focus_event, this);
-        YAHOO.util.Event.addListener(this.enablecheckbox, 'change', this.focus_event, this);
-        YAHOO.util.Event.addBlurListener(this.enablecheckbox, this.blur_event, this);
-    }
-}
-
-/** The pop-up calendar that contains the calendar. */
-date_selector_calendar.panel = null;
-
-/** The shared YAHOO.widget.Calendar used by all date_selector_calendars. */
-date_selector_calendar.calendar = null;
-
-/** The date_selector_calendar that currently owns the shared stuff. */
-date_selector_calendar.currentowner = null;
-
-/** Used as a timeout when hiding the calendar on blur - so we don't hide the calendar
- * if we are just jumping from on of our controls to another. */
-date_selector_calendar.hidetimeout = null;
-
-/** Timeout for repositioning after a delay after a change of months. */
-date_selector_calendar.repositiontimeout = null;
-
-/** Member variables. Pointers to various bits of the DOM. */
-date_selector_calendar.prototype.fieldset = null;
-date_selector_calendar.prototype.yearselect = null;
-date_selector_calendar.prototype.monthselect = null;
-date_selector_calendar.prototype.dayselect = null;
-date_selector_calendar.prototype.enablecheckbox = null;
-
-date_selector_calendar.cancel_any_timeout = function() {
-    if (date_selector_calendar.hidetimeout) {
-        clearTimeout(date_selector_calendar.hidetimeout);
-        date_selector_calendar.hidetimeout = null;
-    }
-    if (date_selector_calendar.repositiontimeout) {
-        clearTimeout(date_selector_calendar.repositiontimeout);
-        date_selector_calendar.repositiontimeout = null;
-    }
-}
-
-date_selector_calendar.delayed_reposition = function() {
-    if (date_selector_calendar.repositiontimeout) {
-        clearTimeout(date_selector_calendar.repositiontimeout);
-        date_selector_calendar.repositiontimeout = null;
-    }
-    date_selector_calendar.repositiontimeout = setTimeout(date_selector_calendar.fix_position, 500);
-}
-
-date_selector_calendar.fix_position = function() {
-    if (date_selector_calendar.currentowner) {
-        date_selector_calendar.panel.cfg.setProperty('context', [date_selector_calendar.currentowner.fieldset, 'bl', 'tl']);
-    }
-}
-
-date_selector_calendar.release_current = function() {
-    if (date_selector_calendar.currentowner) {
-        date_selector_calendar.currentowner.release_calendar();
-    }
-}
-
-date_selector_calendar.prototype.focus_event = function(e, me) {
-    date_selector_calendar.cancel_any_timeout();
-    if (me.enablecheckbox == null || me.enablecheckbox.checked) {
-        me.claim_calendar();
-    } else {
-        if (date_selector_calendar.currentowner) {
-            date_selector_calendar.currentowner.release_calendar();
-        }
-    }
-}
-
-date_selector_calendar.prototype.blur_event = function(e, me) {
-    date_selector_calendar.hidetimeout = setTimeout(date_selector_calendar.release_current, 300);
-}
-
-date_selector_calendar.prototype.handle_select_change = function(e, me) {
-    me.set_date_from_selects();
-}
-
-date_selector_calendar.document_click = function(event) {
-    if (date_selector_calendar.currentowner) {
-        var currentcontainer = date_selector_calendar.currentowner.fieldset;
-        var eventarget = YAHOO.util.Event.getTarget(event);
-        if (YAHOO.util.Dom.isAncestor(currentcontainer, eventarget)) {
-            setTimeout(function() {date_selector_calendar.cancel_any_timeout()}, 100);
-        } else {
-            date_selector_calendar.currentowner.release_calendar();
-        }
-    }
-}
-
-date_selector_calendar.prototype.claim_calendar = function() {
-    date_selector_calendar.cancel_any_timeout();
-    if (date_selector_calendar.currentowner == this) {
-        return;
-    }
-    if (date_selector_calendar.currentowner) {
-        date_selector_calendar.currentowner.release_calendar();
-    }
-
-    if (date_selector_calendar.currentowner != this) {
-        this.connect_handlers();
-    }
-    date_selector_calendar.currentowner = this;
-
-    date_selector_calendar.calendar.cfg.setProperty('mindate', new Date(this.yearselect.options[0].value, 0, 1));
-    date_selector_calendar.calendar.cfg.setProperty('maxdate', new Date(this.yearselect.options[this.yearselect.options.length - 1].value, 11, 31));
-    this.fieldset.insertBefore(date_selector_calendar.panel.element, this.yearselect.nextSibling);
-    this.set_date_from_selects();
-    date_selector_calendar.panel.show();
-    var me = this;
-    setTimeout(function() {date_selector_calendar.cancel_any_timeout()}, 100);
-}
-
-date_selector_calendar.prototype.set_date_from_selects = function() {
-    var year = parseInt(this.yearselect.value);
-    var month = parseInt(this.monthselect.value) - 1;
-    var day = parseInt(this.dayselect.value);
-    date_selector_calendar.calendar.select(new Date(year, month, day));
-    date_selector_calendar.calendar.setMonth(month);
-    date_selector_calendar.calendar.setYear(year);
-    date_selector_calendar.calendar.render();
-    date_selector_calendar.fix_position();
-}
-
-date_selector_calendar.prototype.set_selects_from_date = function(eventtype, args) {
-    var date = args[0][0];
-    var newyear = date[0];
-    var newindex = newyear - this.yearselect.options[0].value;
-    this.yearselect.selectedIndex = newindex;
-    this.monthselect.selectedIndex = date[1] - this.monthselect.options[0].value;
-    this.dayselect.selectedIndex = date[2] - this.dayselect.options[0].value;
-}
-
-date_selector_calendar.prototype.connect_handlers = function() {
-    YAHOO.util.Event.addListener([this.yearselect, this.monthselect, this.dayselect], 'change', this.handle_select_change, this);
-    date_selector_calendar.calendar.selectEvent.subscribe(this.set_selects_from_date, this, true);
-}
-
-date_selector_calendar.prototype.release_calendar = function() {
-    date_selector_calendar.panel.hide();
-    date_selector_calendar.currentowner = null;
-    YAHOO.util.Event.removeListener([this.yearselect, this.monthselect, this.dayselect], this.handle_select_change);
-    date_selector_calendar.calendar.selectEvent.unsubscribe(this.set_selects_from_date, this);
-}
-
 function filterByParent(elCollection, parentFinder) {
     var filteredCollection = [];
     for (var i = 0; i < elCollection.length; ++i) {
index e6f0c20..c1512e4 100644 (file)
@@ -201,12 +201,6 @@ a.skip:active {position: static;display: block;}
 .ie .mform .fitem .felement {margin-left:0;text-align:left;float:left;}
 /** Fix IE double margin + float bugs **/
 .ie .mform .fitem .fitemtitle {padding-right:1em;}
-#date_selector_calendar_panel .bd {padding: 0;}
-#date_selector_calendar_panel .yui-calcontainer {border: none;float: none;}
-/* Prevent border-collapse:collapse from bleeding through in IE6, IE7 */
-#date_selector_calendar_panel.yui-overlay-hidden table {*display:none;}
-#date_selector_calendar_panel th.calhead {padding-right: 2em;}
-#date_selector_calendar_panel a.container-close {top: 10px;}
 #portfolio-add-button {display:inline;}
 
 /**