render : function()

in xf/ui/xf.ui.textinput.js [22:393]


    render : function (textInput, options) {
        var jQTextInput = $(textInput),
            
        // Events for pointer/touchs
        eventsHandler = {
            start : 'mousedown touchstart MSPointerDown',
            move : 'mousemove touchmove MSPointerMove',
            end : 'mouseup touchend MSPointerUp'
        };

        if (!textInput || !(jQTextInput instanceof $) || jQTextInput.attr('data-skip-enhance') == 'true') {
            return;
        }

        jQTextInput.attr({'data-skip-enhance':true});

        // For inputs of types:
        // text, search, tel, url, email, password, datetime, date, month,
        // week, time, datetime-local, number, color and also for TEXTAREA element
        // add class "xf-input-text".
        jQTextInput.addClass('xf-input-text');

        var isInputElement = (textInput.nodeName == 'INPUT'),
        textInputType = jQTextInput.attr('type'),
        newTextInput;

        // For inputs of types "range" and "search" change type to "text".
        if (textInputType == 'search') {
            newTextInput = $('<input type="text"/>');
            newTIAttrs = {};

            _.each(textInput.attributes, function (attribute) {

                if (attribute.name == 'type') {
                    return;
                }
                newTIAttrs[attribute.name] = attribute.value;
            });
            newTextInput.attr(newTIAttrs);

            if (jQTextInput.hasOwnProperty('outerHTML')) {
                jQTextInput.outerHtml(newTextInput);
            }
            jQTextInput = newTextInput;
            textInput = newTextInput[0];
                
        } else if (textInputType == 'number' || textInputType == 'range') {

            var minValue = jQTextInput.attr('min'),
            maxValue = jQTextInput.attr('max'),
            selValue = parseFloat(jQTextInput.attr('value')),
            step = parseFloat(jQTextInput.attr('step')) || 1;
            
            var newTIAttrs = {};
            
            newTextInput = $('<input type="text"/>');

            _.each(textInput.attributes, function (attribute) {

                if (attribute.name == 'type') {
                    return;
                }
                newTIAttrs[attribute.name] = attribute.value;
            });
            newTextInput.attr(newTIAttrs);
            newTextInput.attr({'data-skip-enhance':true});

            var numberWrapper = $('<div></div>').addClass('xf-input-number');
                
            // Add buttons to decrease number
            numberWrapper.append(
                $('<button type="button"></button>')
                .addClass('xf-input-number-control xf-input-number-control-decrease')
                .attr({'data-skip-enhance':true})
                .append(
                    $('<span></span>').addClass('xf-icon xf-icon-big xf-icon-circled-minus')
                )
            );
            numberWrapper.append(newTextInput);
                
            // Add buttons to increase number
            numberWrapper.append(
                $('<button type="button"></button>')
                .addClass('xf-input-number-control xf-input-number-control-increase')
                .attr({'data-skip-enhance':true})
                .append(
                    $('<span></span>').addClass('xf-icon xf-icon-big xf-icon-circled-plus')
                )
            );

            var rangeWrapper = null;

            if (textInputType == 'number') {

                jQTextInput.outerHtml(numberWrapper);
                jQTextInput = numberWrapper;
                textInput = numberWrapper[0];

            } else if (textInputType == 'range') {

                rangeWrapper = $('<div></div>').addClass('xf-range');
                rangeWrapper.append(numberWrapper);

                // If there is no either min or max attribute -- don't render the slider.
                if ((minValue || minValue === 0) && (maxValue || maxValue === 0)) {

                    minValue = parseFloat(minValue);
                    maxValue = parseFloat(maxValue);

                    var percValue = (selValue - minValue) * 100 / (maxValue - minValue),
                        
                    // Underscore template for slider
                    _template = _.template(
                        '<div class="xf-input-range">' +
                        '<div class="xf-range-wrap">' +
                        '<div class="xf-input-range-min"><%= minValue %></div>' +
                        '<div class="xf-input-range-slider">' +
                        '<div class="xf-input-range-track">' +
                        '<div class="xf-input-range-value" style="width: 0">' +
                        '<div class="xf-input-range-control" tabindex="0">' +
                        '<div class="xf-input-range-thumb" style="left:100%" title="<%= selValue %>"></div>' +
                        '</div>' +
                        '</div>' +
                        '</div>' +
                        '</div>' +
                        '<div class="xf-input-range-max"><%= maxValue %></div>' +
                        '</div>' +
                        '</div>'
                    );
                    rangeWrapper.append(_template({minValue : minValue, maxValue: maxValue, selValue: selValue}));

                    jQTextInput.outerHtml(rangeWrapper);
                    jQTextInput = rangeWrapper;
                    textInput = rangeWrapper[0];
                }

            }

            // Function to set new value for input
            var setNewValue = function (newValue) {

                var modulo = newValue % step;
                var steppedVal = newValue - modulo;
                if(modulo > step/2) {
                    steppedVal += step;
                }
                newValue = steppedVal;

                if((maxValue || maxValue === 0) && newValue > maxValue) {
                    newValue = maxValue;
                }

                if((minValue || minValue === 0) && newValue < minValue) {
                    newValue = minValue;
                }

                selValue = newValue;

                newTextInput.attr({'value':newValue});

                if (rangeWrapper) {
                    rangeWrapper.find('div.xf-input-range-thumb').attr({'title':newValue});

                    var percValue = (newValue - minValue) * 100 / (maxValue - minValue);
                    rangeWrapper.find('div.xf-input-range-value').css({'width':'' + percValue + '%'});
                }

            };

            // Function to increase value for input
            var stepUp = function () {
                var newValue = parseFloat(newTextInput.attr('value'));
                newValue += step;
                setNewValue(newValue);
            };

            // Function to decrease value for input
            var stepDown = function () {
                var newValue = parseFloat(newTextInput.attr('value'));
                newValue -= step;
                setNewValue(newValue);
            };

            // Initialing number stepper buttons (-) & (+) click handlers
            numberWrapper.find('button.xf-input-number-control-decrease').on('tap', stepDown);
            numberWrapper.find('button.xf-input-number-control-increase').on('tap', stepUp);

            var savedInputText = newTextInput.attr('value');
            var newInputText;
            var inputTextChange = function (event) {
                newInputText = newTextInput.attr('value');
                    
                // Prevent multiple recalculations in case when several events where triggered
                if (savedInputText == newInputText) {
                    return;
                }

                newInputText = parseFloat(newInputText);
                    
                if (isNaN(newInputText)) {
                    newInputText = minValue;
                }
                savedInputText = newInputText;
                setNewValue(newInputText);
            };

            newTextInput
            .change(inputTextChange)
            //.keyup(inputTextChange)
            //.keydown(inputTextChange)
            .focus(inputTextChange)
            .focusout(inputTextChange);

            if (rangeWrapper) {

                var trackW;
                var savedVal;
                var valueDiff;
                var mousePrevX;
                var mouseNewX;
                var mouseDiff;

                var trackDiffToValueDiff = function (trackDiff) {
                        
                    if (!trackW) {
                        trackW = rangeWrapper.find('div.xf-input-range-track')[0].clientWidth;
                    }
                    return (trackDiff / trackW * (maxValue - minValue));
                };

                var trackPointToValuePoint = function (trackPoint) {
                    return (trackDiffToValueDiff(trackPoint) + minValue);
                };

                var startThumbDrag = function (event) {
                    mousePrevX = XF.device.supports.touchEvents ? event.originalEvent.targetTouches[0].pageX : event.pageX || event.clientX || layerX || event.screenX;
                    savedVal = selValue;
                    $(document).bind(eventsHandler.end, stopThumbDrag);
                    $(document).bind(eventsHandler.move, doThumbDrag);
                };

                var doThumbDrag = function (event) {
                    mouseNewX = XF.device.supports.touchEvents ? event.originalEvent.targetTouches[0].pageX : event.pageX || event.clientX || layerX || event.screenX;
                    mouseDiff = mouseNewX - mousePrevX;
                    valueDiff = trackDiffToValueDiff(mouseDiff);
                    mousePrevX = mouseNewX;
                    savedVal += valueDiff;
                    setNewValue(savedVal);
                };

                var stopThumbDrag = function () {
                    $(document).bind(eventsHandler.end, stopThumbDrag);
                    $(document).bind(eventsHandler.move, doThumbDrag);
                };

                var startThumbPress = function () {
                    $(document).bind('keydown', doThumbPress);
                };

                var doThumbPress = function (event) {
                        
                    switch(event.keyCode) {
                        // PG Up
                    case 33:
                        setNewValue(selValue + 3*step);
                        break;
                        // PG Down
                    case 34:
                        setNewValue(selValue - 3*step);
                        break;
                        // End
                    case 35:
                        setNewValue(maxValue);
                        break;
                        // Home
                    case 36:
                        setNewValue(minValue);
                        break;
                        // arrow up
                    case 38:
                        // arrow right
                    case 39:
                        setNewValue(selValue + step);
                        break;
                        // arrow left
                    case 37:
                        // arrow down
                    case 40:
                        setNewValue(selValue - step);
                        break;
                    }
                };

                var stopThumbPress = function () {
                    $(document).unbind('keydown', doThumbPress);
                };

                // initialing slider thumb dragging handler
                rangeWrapper.find('div.xf-input-range-thumb').bind('mousedown touchstart', startThumbDrag);

                // initialing arrow keys press handling
                rangeWrapper.find('div.xf-input-range-control')
                .bind('focus', startThumbPress)
                .bind('focusout', stopThumbPress);

                var trackClick = function( event) {
                        
                    // skipping events fired by thumb dragging
                    if (event.target == rangeWrapper.find('div.xf-input-range-thumb')[0]) {
                        return;
                    }
                    setNewValue(trackPointToValuePoint(event.offsetX));
                };

                // initialing track click handler
                rangeWrapper.find('div.xf-input-range-track').bind('click', trackClick);
            }
        }

        // Some Text-based inputs (text, search, tel, url, email, password, datetime, date, month,
            // week, time, datetime-local, color) with data-appearance="split" attribute
            // are parsed specifically:
            var splitAppearance = false;
            if (options.appearance == 'split' && isInputElement) {

                var applicableTypes = ['text', 'search', 'tel', 'url', 'email',
                'password', 'datetime', 'date', 'month', 'week', 'time', 'datetime-local', 'color'];

                _.each(applicableTypes, function (applicableType) {
                    
                    if (textInputType == applicableType) {
                        splitAppearance = true;
                    }
                });
            }

            var textInputID = (jQTextInput[0].nodeName === 'INPUT') ? jQTextInput.attr('id') : jQTextInput.find('input').eq(0).attr('id');
            var textInputLabel = (textInputID && textInputID.length) ? $('label[for=' + textInputID + ']') : [];

            // If the input doesn't have an associated label, quit
            if (textInputLabel.length) {

                if (splitAppearance) {

                    // Add class xf-input-split-input to the input
                    jQTextInput.removeClass('xf-input-text').addClass('xf-input-split-input');

                    // Add class xf-input-split-label to the label
                    textInputLabel.addClass('xf-input-split-label');

                    // Wrap both in div.xf-input-split
                    var splitDiv = $('<div></div>').addClass('xf-input-split');

                    // Wrap the label in div.xf-grid-unit.xf-input-split-part1
                    splitDiv.append($('<div></div>').addClass('xf-grid-unit xf-input-split-part1').append(textInputLabel));

                    // Wrap the input in div.xf-grid-unit.xf-input-split-part2
                    splitDiv.append($('<div></div>').addClass('xf-grid-unit xf-input-split-part2').append(jQTextInput.clone()));

                    jQTextInput.outerHtml(splitDiv);
                    jQTextInput = splitDiv;
                    textInput = splitDiv[0];

                } else {

                    // If inputs of the named types and textarea have a label associated to them (with "for" attribute
                    // with a value equal to input "id" attribute), the label is assigned a class name of "xf-label"
                    textInputLabel.addClass('xf-label');

                }
            }
        }