/* eslint-disable */
import $ from 'jquery';
/*
 * This file is part of the Arnapou jqCron package.
 *
 * (c) Arnaud Buathier <arnaud@arnapou.net>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Default settings
 */
var jqCronDefaultSettings = {
    texts: {},
    monthdays: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31],
    hours: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
    hour_labels: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23"],
    minutes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
    lang: 'en',
    enabled_minute: false,
    enabled_hour: true,
    enabled_day: true,
    enabled_week: true,
    enabled_month: true,
    enabled_year: true,
    multiple_dom: false,
    multiple_month: false,
    multiple_mins: false,
    multiple_dow: false,
    multiple_time_hours: false,
    multiple_time_minutes: false,
    numeric_zero_pad: false,
    default_period: 'day',
    default_value: '',
    no_reset_button: true,
    disabled: false,
    bind_to: null,
    bind_method: {
        set: function($element, value) {
            $element.is(':input') ? $element.val(value) : $element.data('jqCronValue', value);
        },
        get: function($element) {
            return $element.is(':input') ? $element.val() : $element.data('jqCronValue');
        }
    }
};

/**
 * Custom extend of json for jqCron settings.
 * We don't use jQuery.extend because simple extend does not fit our needs, and deep extend has a bad
 * feature for us : it replaces keys of "Arrays" instead of replacing the full array.
 */
(function($){
    var extend = function(dst, src) {
        for(var i in src) {
            if($.isPlainObject(src[i])) {
                dst[i] = extend(dst[i] && $.isPlainObject(dst[i]) ? dst[i] : {}, src[i]);
            }
            else if($.isArray(src[i])) {
                dst[i] = src[i].slice(0);
            }
            else if(src[i] !== undefined) {
                dst[i] = src[i];
            }
        }
        return dst;
    };
    this.jqCronMergeSettings = function(obj) {
        return extend(extend({}, jqCronDefaultSettings), obj || {});
    };
}).call(window, $);

/**
 * Shortcut to get the instance of jqCron instance from one jquery object
 */
(function($){
    $.fn.jqCronGetInstance = function() {
        return this.data('jqCron');
    };
}).call(window, $);

/**
 * Main plugin
 */
(function($){
    $.fn.jqCron = function(settings) {
        var saved_settings = settings;
        return this.each(function() {
            var cron, saved;
            var $this = $(this);
            var settings = jqCronMergeSettings(saved_settings); // clone settings
            var translations = settings.texts[settings.lang];

            if (typeof(translations) !== 'object' || $.isEmptyObject(translations)) {
                console && console.error(
                    'Missing translations for language "' + settings.lang + '". ' +
                    'Please include jqCron.' + settings.lang + '.js or manually provide ' +
                    'the necessary translations when calling $.fn.jqCron().'
                );
                return;
            }

            if(!settings.jquery_container) {
                if($this.is(':container')) {
                    settings.jquery_element = $this.uniqueId('jqCron');
                }
                else if($this.is(':autoclose')) {
                    // delete already generated dom if exists
                    if($this.next('.jqCron').length == 1) {
                        $this.next('.jqCron').remove();
                    }
                    // generate new
                    settings.jquery_element = $('<span class="jqCron"></span>').uniqueId('jqCron').insertAfter($this);
                }
                else {
                    console && console.error(settings.texts[settings.lang].error1.replace('%s', this.tagName));
                    return;
                }
            }

            // autoset bind_to if it is an input
            if($this.is(':input')) {
                settings.bind_to = settings.bind_to || $this;
            }

            // init cron object
            if(settings.bind_to){
                if(settings.bind_to.is(':input')) {
                    // auto bind from input to object if an input, textarea ...
                    settings.bind_to.blur(function(){
                        var value = settings.bind_method.get(settings.bind_to);
                        $this.jqCronGetInstance().setCron(value);
                    });
                }
                saved = settings.bind_method.get(settings.bind_to);
                cron = new jqCron(settings);
                cron.setCron(saved);
            }
            else {
                cron = new jqCron(settings);
            }
            $(this).data('jqCron', cron);
        });
    };
}).call(window, $);

/**
 * jqCron class
 */
(function($){
    var jqCronInstances = [];

    function jqCron(settings) {
        var _initialized  = false;
        var _self         = this;
        var _$elt         = this;
        var _$obj         = $('<span class="jqCron-container"></span>');
        var _$blocks      = $('<span class="jqCron-blocks"></span>');
        var _$blockPERIOD = $('<span class="jqCron-period"></span>');
        var _$blockDOM    = $('<span class="jqCron-dom"></span>');
        var _$blockMONTH  = $('<span class="jqCron-month"></span>');
        var _$blockMINS   = $('<span class="jqCron-mins"></span>');
        var _$blockDOW    = $('<span class="jqCron-dow"></span>');
        var _$blockTIME   = $('<span class="jqCron-time"></span>');
        var _$cross       = $('<span class="jqCron-cross">&#10008;</span>');
        var _selectors    = [];
        var _selectorPeriod, _selectorMins, _selectorTimeH, _selectorTimeM, _selectorDow, _selectorDom, _selectorMonth;

        // instanciate a new selector
        function newSelector($block, multiple, type){
            var selector = new jqCronSelector(_self, $block, multiple, type);
            selector.$.bind('selector:open', function(){
                // we close all opened selectors of all other jqCron
                for(var n = jqCronInstances.length; n--; ){
                    if(jqCronInstances[n] != _self) {
                        jqCronInstances[n].closeSelectors();
                    }
                    else {
                        // we close all other opened selectors of this jqCron
                        for(var o = _selectors.length; o--; ){
                            if(_selectors[o] != selector) {
                                _selectors[o].close();
                            }
                        }
                    }
                }
            });
            selector.$.bind('selector:change', function(){
                var boundChanged = false;
                // don't propagate if not initialized
                if(!_initialized) return;
                // bind data between two minute selectors (only if they have the same multiple settings)
                if(settings.multiple_mins == settings.multiple_time_minutes) {
                    if(selector == _selectorMins) {
                        boundChanged = _selectorTimeM.setValue(_selectorMins.getValue());
                    }
                    else if(selector == _selectorTimeM) {
                        boundChanged = _selectorMins.setValue(_selectorTimeM.getValue());
                    }
                }
                // we propagate the change event to the main object
                boundChanged || _$obj.trigger('cron:change', _self.getCron());
            });
            _selectors.push(selector);
            return selector;
        }

        // disable the selector
        this.disable = function(){
            _$obj.addClass('disable');
            settings.disable = true;
            _self.closeSelectors();
        };

        // return if the selector is disabled
        this.isDisabled = function() {
            return settings.disable == true;
        };

        // enable the selector
        this.enable = function(){
            _$obj.removeClass('disable');
            settings.disable = false;
        };

        // get cron value
        this.getCron = function(){
            var period = _selectorPeriod.getValue();
            var items = ['*', '*', '*', '*', '*'];
            if(period == 'hour') {
                items[0] = _selectorMins.getCronValue();
            }
            if(period == 'day' || period == 'week' || period == 'month' || period == 'year') {
                items[0] = _selectorTimeM.getCronValue();
                items[1] = _selectorTimeH.getCronValue();
            }
            if(period == 'month' || period == 'year') {
                items[2] = _selectorDom.getCronValue();
            }
            if(period == 'year') {
                items[3] = _selectorMonth.getCronValue();
            }
            if(period == 'week') {
                items[4] = _selectorDow.getCronValue();
            }
            return items.join(' ');
        };

        // set cron (string like * * * * *)
        this.setCron = function(str) {
            if(!str) return;
            try {
                str = str.replace(/\s+/g, ' ').replace(/^ +/, '').replace(/ +$/, ''); // sanitize
                var mask = str.replace(/[^\* ]/g, '-').replace(/-+/g, '-').replace(/ +/g, '');
                var items = str.split(' ');
                if (items.length != 5) _self.error(_self.getText('error2'));
                if(mask == '*****') {						// 1 possibility
                    _selectorPeriod.setValue('minute');
                }
                else if(mask == '-****') {					// 1 possibility
                    _selectorPeriod.setValue('hour');
                    _selectorMins.setCronValue(items[0]);
                    _selectorTimeM.setCronValue(items[0]);
                }
                else if(mask.substring(2, mask.length) == '***') {			// 4 possibilities
                    _selectorPeriod.setValue('day');
                    _selectorMins.setCronValue(items[0]);
                    _selectorTimeM.setCronValue(items[0]);
                    _selectorTimeH.setCronValue(items[1]);
                }
                else if(mask.substring(2, mask.length) == '-**') {			// 4 possibilities
                    _selectorPeriod.setValue('month');
                    _selectorMins.setCronValue(items[0]);
                    _selectorTimeM.setCronValue(items[0]);
                    _selectorTimeH.setCronValue(items[1]);
                    _selectorDom.setCronValue(items[2]);
                }
                else if(mask.substring(2, mask.length) == '**-') {			// 4 possibilities
                    _selectorPeriod.setValue('week');
                    _selectorMins.setCronValue(items[0]);
                    _selectorTimeM.setCronValue(items[0]);
                    _selectorTimeH.setCronValue(items[1]);
                    _selectorDow.setCronValue(items[4]);
                }
                else if (mask.substring(3, mask.length) == '-*') {			// 8 possibilities
                    _selectorPeriod.setValue('year');
                    _selectorMins.setCronValue(items[0]);
                    _selectorTimeM.setCronValue(items[0]);
                    _selectorTimeH.setCronValue(items[1]);
                    _selectorDom.setCronValue(items[2]);
                    _selectorMonth.setCronValue(items[3]);
                }
                else {
                    _self.error(_self.getText('error4'));
                }
                _self.clearError();
            } catch(e) {}
        };

        // close all child selectors
        this.closeSelectors = function(){
            for(var n = _selectors.length; n--; ){
                _selectors[n].close();
            }
        };

        // get the main element id
        this.getId = function(){
            return _$elt.attr('id');
        }

        // get the translated text
        this.getText = function(key) {
            var text = settings.texts[settings.lang][key] || null;
            if(typeof(text) == "string" && text.match('<b')){
                text = text.replace(/(<b *\/>)/gi, '</span><b /><span class="jqCron-text">');
                text = '<span class="jqCron-text">' + text + '</span>';
            }
            return text;
        };

        // get the human readable text
        this.getHumanText = function() {
            var texts=[];
            _$obj
                .find('> span > span:visible')
                .find('.jqCron-text, .jqCron-selector > span')
                .each(function() {
                    var text = $(this).text().replace(/\s+$/g, '').replace(/^\s+/g, '');
                    text && texts.push(text);
                });
            return texts.join(' ').replace(/\s:\s/g, ':');
        }

        // get settings
        this.getSettings = function(){
            return settings;
        };

        // display an error
        this.error = function(msg) {
            console && console.error('[jqCron Error] ' + msg);
            _$obj.addClass('jqCron-error').attr('title', msg);
            throw msg;
        };

        // clear error
        this.clearError = function(){
            _$obj.attr('title', '').removeClass('jqCron-error');
        };

        // clear
        this.clear = function() {
            _selectorDom.setValue([]);
            _selectorDow.setValue([]);
            _selectorMins.setValue([]);
            _selectorMonth.setValue([]);
            _selectorTimeH.setValue([]);
            _selectorTimeM.setValue([]);
            _self.triggerChange();
        };

        // init (called in constructor)
        this.init = function(){
            var n,i,labelsList,list;
            if(_initialized) return;

            settings = jqCronMergeSettings(settings);
            settings.jquery_element || _self.error(_self.getText('error3'));
            _$elt = settings.jquery_element;
            _$elt.append(_$obj);
            _$obj.data('id', settings.id);
            _$obj.data('jqCron', _self);
            _$obj.append(_$blocks);
            settings.no_reset_button || _$obj.append(_$cross);
            (!settings.disable) || _$obj.addClass('disable');
            _$blocks.append(_$blockPERIOD);

            if ( /^(ko)$/i.test(settings.lang) )
            {
                _$blocks.append(_$blockMONTH, _$blockDOM);
            }
            else
            {
                _$blocks.append(_$blockDOM, _$blockMONTH);
            }

            _$blocks.append(_$blockMINS);
            _$blocks.append(_$blockDOW);
            _$blocks.append(_$blockTIME);

            // various binding
            _$cross.click(function(){
                _self.isDisabled() || _self.clear();
            });

            // binding from cron to target
            _$obj.bind('cron:change', function(evt, value){
                if(!settings.bind_to) return;
                settings.bind_method.set && settings.bind_method.set(settings.bind_to, value);
                _self.clearError();
            });

            // PERIOD
            _$blockPERIOD.append(_self.getText('text_period'));
            _selectorPeriod = newSelector(_$blockPERIOD, false, 'period');
            settings.enabled_minute && _selectorPeriod.add('minute', _self.getText('name_minute'));
            settings.enabled_hour   && _selectorPeriod.add('hour',   _self.getText('name_hour'));
            settings.enabled_day    && _selectorPeriod.add('day',    _self.getText('name_day'));
            settings.enabled_week   && _selectorPeriod.add('week',   _self.getText('name_week'));
            settings.enabled_month  && _selectorPeriod.add('month',  _self.getText('name_month'));
            settings.enabled_year   && _selectorPeriod.add('year',   _self.getText('name_year'));
            _selectorPeriod.$.bind('selector:change', function(e, value){
                _$blockDOM.hide();
                _$blockMONTH.hide();
                _$blockMINS.hide();
                _$blockDOW.hide();
                _$blockTIME.hide();
                if(value == 'hour') {
                    _$blockMINS.show();
                }
                else if(value == 'day') {
                    _$blockTIME.show();
                }
                else if(value == 'week') {
                    _$blockDOW.show();
                    _$blockTIME.show();
                }
                else if(value == 'month') {
                    _$blockDOM.show();
                    _$blockTIME.show();
                }
                else if(value == 'year') {
                    _$blockDOM.show();
                    _$blockMONTH.show();
                    _$blockTIME.show();
                }
            });
            _selectorPeriod.setValue(settings.default_period);

            // MINS  (minutes)
            _$blockMINS.append(_self.getText('text_mins'));
            _selectorMins = newSelector(_$blockMINS, settings.multiple_mins, 'minutes');
            for(i=0, list=settings.minutes; i<list.length; i++){
                _selectorMins.add(list[i], list[i]);
            }

            // TIME  (hour:min)
            _$blockTIME.append(_self.getText('text_time'));
            _selectorTimeH = newSelector(_$blockTIME, settings.multiple_time_hours, 'time_hours');
            for(i=0, list=settings.hours, labelsList=settings.hour_labels; i<list.length; i++){
                _selectorTimeH.add(list[i], labelsList[i]);
            }
            _selectorTimeM = newSelector(_$blockTIME, settings.multiple_time_minutes, 'time_minutes');
            for(i=0, list=settings.minutes; i<list.length; i++){
                _selectorTimeM.add(list[i], list[i]);
            }

            // DOW  (day of week)
            _$blockDOW.append(_self.getText('text_dow'));
            _selectorDow = newSelector(_$blockDOW, settings.multiple_dow, 'day_of_week');
            for(i=0, list=_self.getText('weekdays'); i<list.length; i++){
                _selectorDow.add(i+1, list[i]);
            }

            // DOM  (day of month)
            _$blockDOM.append(_self.getText('text_dom'));
            _selectorDom = newSelector(_$blockDOM, settings.multiple_dom, 'day_of_month');
            for(i=0, list=settings.monthdays; i<list.length; i++){
                _selectorDom.add(list[i], list[i]);
            }

            // MONTH  (day of week)
            _$blockMONTH.append(_self.getText('text_month'));
            _selectorMonth = newSelector(_$blockMONTH, settings.multiple_month, 'month');
            for(i=0, list=_self.getText('months'); i<list.length; i++){
                _selectorMonth.add(i+1, list[i]);
            }

            // close all selectors when we click in body
            $('body').click(function(){
                var i, n = _selectors.length;
                for(i = 0; i < n; i++){
                    _selectors[i].close();
                }
            });
            _initialized = true;

            // default value
            if(settings.default_value) {
                _self.setCron(settings.default_value);
            }
        };

        // trigger a change event
        this.triggerChange = function(){
            _$obj.trigger('cron:change', _self.getCron());
        };

        // store instance in array
        jqCronInstances.push(this);

        // expose main jquery object
        this.$ = _$obj;

        // init
        try {
            this.init();
            _self.triggerChange();
        } catch(e){}
    }
    this.jqCron = jqCron;
}).call(window, $);


/**
 * jqCronSelector class
 */
(function($){
    function jqCronSelector(_cron, _$block, _multiple, _type){
        var _self      = this;
        var _$list     = $('<ul class="jqCron-selector-list"></ul>');
        var _$title    = $('<span class="jqCron-selector-title"></span>');
        var _$selector = $('<span class="jqCron-selector"></span>');
        var _values    = {};
        var _value     = [];
        var _hasNumericTexts = true;
        var _numeric_zero_pad = _cron.getSettings().numeric_zero_pad;

        // return an array without doublon
        function array_unique(l){
            var i=0,n=l.length,k={},a=[];
            while(i<n) {
                k[l[i]] || (k[l[i]] = 1 && a.push(l[i]));
                i++;
            }
            return a;
        }

        // get the value (an array if multiple, else a single value)
        this.getValue = function(){
            return _multiple ? _value : _value[0];
        };

        // get a correct string for cron
        this.getCronValue = function(){
            if(_value.length == 0) return '*';
            var cron = [_value[0]], i, s = _value[0], c = _value[0], n = _value.length;
            for(i=1; i<n; i++) {
                if(_value[i] == c+1) {
                    c = _value[i];
                    cron[cron.length-1] = s+'-'+c;
                }
                else {
                    s = c = _value[i];
                    cron.push(c);
                }
            }
            return cron.join(',');
        };

        // set the cron value
        this.setCronValue = function(str) {
            var values = [], m ,i, n;
            if(str !== '*') {
                while(str != '') {
                    // test "*/n" expression
                    m = str.match(/^\*\/([0-9]+),?/);
                    if(m && m.length == 2) {
                        for(i=0; i<=59; i+=(m[1]|0)) {
                            values.push(i);
                        }
                        str = str.replace(m[0], '');
                        continue;
                    }
                    // test "a-b/n" expression
                    m = str.match(/^([0-9]+)-([0-9]+)\/([0-9]+),?/);
                    if(m && m.length == 4) {
                        for(i=(m[1]|0); i<=(m[2]|0); i+=(m[3]|0)) {
                            values.push(i);
                        }
                        str = str.replace(m[0], '');
                        continue;
                    }
                    // test "a-b" expression
                    m = str.match(/^([0-9]+)-([0-9]+),?/);
                    if(m && m.length == 3) {
                        for(i=(m[1]|0); i<=(m[2]|0); i++) {
                            values.push(i);
                        }
                        str = str.replace(m[0], '');
                        continue;
                    }
                    // test "c" expression
                    m = str.match(/^([0-9]+),?/);
                    if(m && m.length == 2) {
                        values.push(m[1]|0);
                        str = str.replace(m[0], '');
                        continue;
                    }
                    // something goes wrong in the expression
                    return ;
                }
            }
            _self.setValue(values);
        };

        // close the selector
        this.close = function(){
            _$selector.trigger('selector:close');
        };

        // open the selector
        this.open = function(){
            _$selector.trigger('selector:open');
        };

        // whether the selector is open
        this.isOpened = function() {
            return _$list.is(':visible');
        };

        // add a selected value to the list
        this.addValue = function(key) {
            var values = _multiple ? _value.slice(0) : []; // clone array
            values.push(key);
            _self.setValue(values);
        };

        // remove a selected value from the list
        this.removeValue = function(key) {
            if(_multiple) {
                var i, newValue = [];
                for(i=0; i<_value.length; i++){
                    if(key != [_value[i]]) {
                        newValue.push(_value[i]);
                    }
                }
                _self.setValue(newValue);
            }
            else {
                _self.clear();
            }
        };

        // set the selected value(s) of the list
        this.setValue = function(keys){
            var i, newKeys = [], saved = _value.join(' ');
            if(!$.isArray(keys)) keys = [keys];
            _$list.find('li').removeClass('selected');
            keys = array_unique(keys);
            keys.sort(function(a, b){
                var ta = typeof(a);
                var tb = typeof(b);
                if(ta==tb && ta=="number") return a-b;
                else return String(a) == String(b) ? 0 : (String(a) < String(b) ? -1 : 1);
            });
            if(_multiple) {
                for(i=0; i<keys.length; i++){
                    if(keys[i] in _values) {
                        _values[keys[i]].addClass('selected');
                        newKeys.push(keys[i]);
                    }
                }
            }
            else {
                if(keys[0] in _values) {
                    _values[keys[0]].addClass('selected');
                    newKeys.push(keys[0]);
                }
            }
            // remove unallowed values
            _value = newKeys;
            if(saved != _value.join(' ')) {
                _$selector.trigger('selector:change', _multiple ? keys : keys[0]);
                return true;
            }
            return false;
        };

        // get the title text
        this.getTitleText = function(){
            var getValueText = function(key) {
                return (key in _values) ? _values[key].text() : key;
            };

            if(_value.length == 0) {
                return _cron.getText('empty_' + _type) || _cron.getText('empty');
            }
            var cron = [getValueText(_value[0])], i, s = _value[0], c = _value[0], n = _value.length;
            for(i=1; i<n; i++) {
                if(_value[i] == c+1) {
                    c = _value[i];
                    cron[cron.length-1] = getValueText(s)+'-'+getValueText(c);
                }
                else {
                    s = c = _value[i];
                    cron.push(getValueText(c));
                }
            }
            return cron.join(',');
        };

        // clear list
        this.clear = function() {
            _values = {};
            _self.setValue([]);
            _$list.empty();
        };

        // add a (key, value) pair
        this.add = function(key, value) {
            if(!(value+'').match(/^[0-9]+$/)) _hasNumericTexts = false;
            if(_numeric_zero_pad && _hasNumericTexts && value < 10) {
                value = '0'+value;
            }
            var $item = $('<li>' + value + '</li>');
            _$list.append($item);
            _values[key] = $item;
            $item.click(function(){
                if(_multiple && $(this).hasClass('selected')) {
                    _self.removeValue(key);
                }
                else {
                    _self.addValue(key);
                    if(!_multiple) _self.close();
                }
            });
        };

        // expose main jquery object
        this.$ = _$selector;

        // constructor
        _$block.find('b:eq(0)').after(_$selector).remove();
        _$selector
            .addClass('jqCron-selector-' + _$block.find('.jqCron-selector').length)
            .append(_$title)
            .append(_$list)
            .bind('selector:open', function(){
                if(_hasNumericTexts) {
                    var nbcols = 1, n = _$list.find('li').length;
                    if(n > 5 && n <= 16) nbcols = 2;
                    else if(n > 16 && n <= 23) nbcols = 3;
                    else if(n > 23 && n <= 40) nbcols = 4;
                    else if(n > 40) nbcols = 5;
                    _$list.addClass('cols'+nbcols);
                }
                _$list.show();
            })
            .bind('selector:close', function(){
                _$list.hide();
            })
            .bind('selector:change', function(){
                _$title.html(_self.getTitleText());
            })
            .click(function(e){
                e.stopPropagation();
            })
            .trigger('selector:change')
        ;
        $.fn.disableSelection && _$selector.disableSelection(); // only work with jQuery UI
        _$title.click(function(e){
            (_self.isOpened() || _cron.isDisabled()) ? _self.close() : _self.open();
        });
        _self.close();
        _self.clear();
    }
    this.jqCronSelector = jqCronSelector;
}).call(window, $);

/**
 * Generate unique id for each element.
 * Skip elements which have already an id.
 */
(function($){
    var jqUID = 0;
    var jqGetUID = function(prefix){
        var id;
        while(1) {
            jqUID++;
            id = ((prefix || 'JQUID')+'') + jqUID;
            if(!document.getElementById(id)) return id;
        }
    };
    $.fn.uniqueId =  function(prefix) {
        return this.each(function(){
            if($(this).attr('id')) return;
            var id = jqGetUID(prefix);
            $(this).attr('id', id);
        });
    };
}).call(window, $);


/**
 * Extends jQuery selectors with new block selector
 */
(function($){
    $.extend($.expr[':'], {
        container: function(a) {
            return (a.tagName+'').toLowerCase() in {
                a:1,
                abbr:1,
                acronym:1,
                address:1,
                b:1,
                big:1,
                blockquote:1,
                button:1,
                cite:1,
                code:1,
                dd: 1,
                del:1,
                dfn:1,
                div:1,
                dt:1,
                em:1,
                fieldset:1,
                form:1,
                h1:1,
                h2:1,
                h3:1,
                h4:1,
                h5:1,
                h6: 1,
                i:1,
                ins:1,
                kbd:1,
                label:1,
                li:1,
                p:1,
                pre:1,
                q:1,
                samp:1,
                small:1,
                span:1,
                strong:1,
                sub: 1,
                sup:1,
                td:1,
                tt:1
            };
        },
        autoclose: function(a) {
            return (a.tagName+'').toLowerCase() in {
                area:1,
                base:1,
                basefont:1,
                br:1,
                col:1,
                frame:1,
                hr:1,
                img:1,
                input:1,
                link:1,
                meta:1,
                param:1
            };
        }
    });
}).call(window, $);
