/*
formBuilder - git@github.com:kevinchappell/formBuilder.git
Version: 1.6.2
Author: Kevin Chappell <kevin.b.chappell@gmail.com>
*/
'use strict';

(function($) {
    'use strict';

    var Toggle = function Toggle(element, options) {

        var defaults = {
            theme: 'fresh',
            labels: {
                off: 'Off',
                on: 'On'
            }
        };

        var opts = $.extend(defaults, options),
        $kcToggle = $('<div class="kc-toggle"/>').insertAfter(element).append(element);

        $kcToggle.toggleClass('on', element.is(':checked'));

        var kctOn = '<div class="kct-on">' + opts.labels.on + '</div>',
        kctOff = '<div class="kct-off">' + opts.labels.off + '</div>',
        kctHandle = '<div class="kct-handle"></div>',
        kctInner = '<div class="kct-inner">' + kctOn + kctHandle + kctOff + '</div>';

        $kcToggle.append(kctInner);

        $kcToggle.click(function() {
            element.attr('checked', !element.attr('checked'));
            $(this).toggleClass('on');
        });
    };

    $.fn.kcToggle = function(options) {
        var toggle = this;
        return toggle.each(function() {
            var element = $(this);
            if (element.data('kcToggle')) {
                return;
            }
            var kcToggle = new Toggle(element, options);
            element.data('kcToggle', kcToggle);
        });
    };
})(jQuery);
'use strict';

(function($) {
    'use strict';
    var FormBuilder = function FormBuilder(element, options) {

        var defaults = {
            // Uneditable fields or other content you would like to
            // appear before and after regular fields.
            disableFields: {
                // before: '<h2>Header</h2>',
                // after: '<h3>Footer</h3>'
            },
            // array of objects with fields values
            // ex:
            // defaultFields: [{
            //   label: 'First Name',
            //   name: 'first-name',
            //   required: 'true',
            //   description: 'Your first name',
            //   type: 'text'
            // }, {
            //   label: 'Phone',
            //   name: 'phone',
            //   description: 'How can we reach you?',
            //   type: 'text'
            // }],
            defaultFields: [],
            roles: {
                1 : 'Administrator'
            },
            showWarning: false,
            serializePrefix: 'frmb',
            messages: {
                add: 'Add Item',
                allowSelect: 'Allow Select',
                autocomplete: 'Autocomplete',
                cannotBeEmpty: 'This field cannot be empty',
                checkboxGroup: 'Checkbox Group',
                checkbox: 'Checkbox',
                checkboxes: 'Checkboxes',
                clearAllMessage: 'Are you sure you want to remove all items?',
                clearAll: 'Clear All',
                close: 'Close',
                copy: 'Copy To Clipboard',
                dateField: 'Date Field',
                description: 'Help Text',
                descriptionField: 'Description',
                devMode: 'Developer Mode',
                disableFields: 'These fields cannot be moved.',
                editNames: 'Edit Names',
                editorTitle: 'Form Elements',
                editXML: 'Edit XML',
                fieldVars: 'Field Variables',
                fieldRemoveWarning: 'Are you sure you want to remove this field?',
                getStarted: 'Drag a field from the right to this area',
                hide: 'Edit',
                hidden: 'Hidden Input',
                label: 'Label',
                labelEmpty: 'Field Label cannot be empty',
                limitRole: 'Limit access to one or more of the following roles:',
                mandatory: 'Mandatory',
                maxLength: 'Max Length',
                minOptionMessage: 'This field requires a minimum of 2 options',
                name: 'Name',
                no: 'No',
                off: 'Off',
                on: 'On',
                optional: 'optional',
                optionLabelPlaceholder: 'Label',
                optionValuePlaceholder: 'Value',
                optionEmpty: 'Option value required',
                paragraph: 'Paragraph',
                preview: 'Preview',
                radioGroup: 'Radio Group',
                radio: 'Radio',
                removeMessage: 'Remove Element',
                remove: '&#215;',
                required: 'Required',
                richText: 'Rich Text Editor',
                roles: 'Access',
                save: 'Save Template',
                selectOptions: 'Select Items',
                select: 'Select',
                selectionsMessage: 'Allow Multiple Selections',
                text: 'Text Field',
                textLabel: 'Title',
                textIntroduce: 'Description',
                toggle: 'Toggle',
                warning: 'Warning!',
                viewXML: 'View XML',
                yes: 'Yes',
                defaultValue : 'Default Value',
            }
        };

        var startIndex, doCancel, _helpers = {};

        /**
     * Callback for when a drag begins
     * @param  {object} event
     * @param  {object} ui
     */
        _helpers.startMoving = function(event, ui) {
            event = event;
            ui.item.addClass('moving');
            startIndex = $('li', this).index(ui.item);
        };

        /**
     * Callback for when a drag ends
     * @param  {object} event
     * @param  {object} ui
     */
        _helpers.stopMoving = function(event, ui) {
            event = event;
            ui.item.removeClass('moving');
            if (doCancel) {
                $(ui.sender).sortable('cancel');
                $(this).sortable('cancel');
            }
        };

     /**
      * Make strings safe to be used as classes
      * @param  {string} str string to be converted
      * @return {string}     converter string
     */
        _helpers.safename = function(str) {
            return str.replace(/\s/g, '-').replace(/[^a-zA-Z0-9\-\_]/g, '').toLowerCase();
        };

     /**
      * Strips non-numbers from a number only input
      * @param  {string} str string with possible number
      * @return {string}     string without numbers
     */
        _helpers.forceNumber = function(str) {
            return str.replace(/[^0-9]/g, '');
        };

        /**
     * hide and show mouse tracking tooltips, only used for disabled
     * fields in the editor.
     * @todo   remove or refactor to make better use
     * @param  {object} tt jQuery option with nexted tooltip
     * @return {void}
     */
        _helpers.initTooltip = function(tt) {
            var tooltip = tt.find('.tooltip');
            tt.mouseenter(function() {
                if (tooltip.outerWidth() > 200) {
                    tooltip.addClass('max-width');
                }
                tooltip.css('left', tt.width() + 14);
                tooltip.stop(true, true).fadeIn('fast');
            }).mouseleave(function() {
                tt.find('.tooltip').stop(true, true).fadeOut('fast');
            });
            tooltip.hide();
        };

        // saves the field data to our canvas (elem)
        _helpers.save = function() {
            $sortableFields.children('li').not('.disabled').each(function() {
                _helpers.updatePreview($(this));
            });
            elem.val($sortableFields.toXML());
        };

        // updatePreview will generate the preview for radio and checkbox groups
        _helpers.updatePreview = function(field) {
            var fieldClass = field.attr('class'),
            $prevHolder = $('.prev-holder', field);

            if (fieldClass.indexOf('ui-sortable-handle') !== -1) {
                return;
            }

            fieldClass = fieldClass.replace(' form-field', '');

            var preview, previewData = {
                type: fieldClass,
                label: $('.fld-label', field).val()
            };

            if (fieldClass === 'checkbox') {
                previewData.toggle = $('.checkbox-toggle', field).is(':checked');
            }

            if (fieldClass.match(/(select|checkbox-group|radio-group)/)) {
                previewData.values = [];

                $('.sortable-options li', field).each(function() {
                    var option = {};
                    option.selected = $('.select-option', $(this)).is(':checked');
                    option.value = $('.option-value', $(this)).val();
                    option.label = $('.option-label', $(this)).val();

                    previewData.values.push(option);
                });
            }
            preview = fieldPreview(previewData);

            $prevHolder.html(preview);

            $('input[toggle]', $prevHolder).kcToggle();
        };

        // update preview to label
        _helpers.updateMultipleSelect = function() {
            $sortableFields.delegate('input[name="multiple"]', 'change',
            function() {
                var options = $(this).parents('.fields:eq(0)').find('.sortable-options input.select-option');
                if (this.checked) {
                    options.each(function() {
                        $(this).prop('type', 'checkbox');
                    });
                } else {
                    options.each(function() {
                        $(this).removeAttr('checked').prop('type', 'radio');
                    });
                }
            });
        };

        _helpers.htmlEncode = function(value) {
            return $('<div/>').text(value).html();
        };

        _helpers.htmlDecode = function(value) {
            return $('<div/>').html(value).text();
        };

        _helpers.validateForm = function() {
            var errors = [];
            // check for empty field labels
            $('input[name="label"], input[type="text"].option', $sortableFields).each(function() {
                if ($(this).val() === '') {
                    var field = $(this).parents('li.form-field'),
                    fieldAttr = $(this);
                    errors.push({
                        field: field,
                        error: opts.messages.labelEmpty,
                        attribute: fieldAttr
                    });
                }
            });

            // @todo add error = { noVal: opts.messages.labelEmpty }
            if (errors.length) {
                alert('Error: ' + errors[0].error);
                $('html, body').animate({
                    scrollTop: errors[0].field.offset().top
                },
                1000,
                function() {
                    var targetID = $('.toggle-form', errors[0].field).attr('id');
                    $('.toggle-form', errors[0].field).addClass('open').parent().next('.prev-holder').slideUp(250);
                    $('#' + targetID + '-fld').slideDown(250,
                    function() {
                        errors[0].attribute.addClass('error');
                    });
                });
            }
        };

        _helpers.disabledTT = function(field) {
            var title = field.attr('data-tooltip');
            if (title) {
                field.removeAttr('title').data('tip_text', title);
                var tt = $('<p/>', {
                    'class': 'frmb-tt'
                }).html(title);
                field.append(tt);
                tt.css({
                    top: -tt.outerHeight(),
                    left: -15
                });
                field.mouseleave(function() {
                    $(this).attr('data-tooltip', field.data('tip_text'));
                    $('.frmb-tt').remove();
                });
            }
        };

        var opts = $.extend(defaults, options),
        elem = $(element),
        frmbID = 'frmb-' + $('ul[id^=frmb-]').length++;

        var field = '',
        lastID = 1,
        boxID = frmbID + '-control-box';

        // create array of field objects to cycle through
        var frmbFields = [{
            label: opts.messages.text,
            attrs: {
                type: 'text',
                className: 'text-input',
                name: 'text-input'
            }
        },
        {
            label: opts.messages.select,
            attrs: {
                type: 'select',
                className: 'select',
                name: 'select'
            }
        },
        {
            label: opts.messages.richText,
            attrs: {
                type: 'rich-text',
                className: 'rich-text',
                name: 'rich-text'
            }
        },
        {
            label: opts.messages.radioGroup,
            attrs: {
                type: 'radio-group',
                className: 'radio-group',
                name: 'radio-group'
            }
        },
        {
            label: opts.messages.hidden,
            attrs: {
                type: 'hidden',
                className: 'hidden-input',
                name: 'hidden-input'
            }
        },
        {
            label: opts.messages.dateField,
            attrs: {
                type: 'date',
                className: 'calendar',
                name: 'date-input'
            }
        },
        {
            label: opts.messages.checkboxGroup,
            attrs: {
                type: 'checkbox-group',
                className: 'checkbox-group',
                name: 'checkbox-group'
            }
        },
        {
            label: opts.messages.checkbox,
            attrs: {
                type: 'checkbox',
                className: 'checkbox',
                name: 'checkbox'
            }
        },
        {
            label: opts.messages.autocomplete,
            attrs: {
                type: 'autocomplete',
                className: 'autocomplete',
                name: 'autocomplete'
            }
        },
        {
            label: opts.messages.textLabel,
            attrs: {
                type: 'text-label',
                className: 'text-label',
                name: 'text'
            }
        }];

        // Create draggable fields for formBuilder
        var cbUL = $('<ul/>', {
            id: boxID,
            'class': 'frmb-control'
        });

        // Loop through
        for (var i = frmbFields.length - 1; i >= 0; i--) {
            var $field = $('<li/>', {
                'class': 'icon-' + frmbFields[i].attrs.className,
                'type': frmbFields[i].type,
                'name': frmbFields[i].className,
                'label': frmbFields[i].label
            });
            for (var attr in frmbFields[i]) {
                if (frmbFields[i].hasOwnProperty(attr)) {
                    $field.data(attr, frmbFields[i][attr]);
                }
            }
            $field.html(frmbFields[i].label).appendTo(cbUL);
        }

        // Build our headers and action links
        var cbHeader = $('<h4/>').html(opts.messages.editorTitle),
        viewXML = $('<a/>', {
            id: frmbID + '-export-xml',
            text: opts.messages.viewXML,
            href: '#',
            'class': 'view-xml'
        }),
        allowSelect = $('<a/>', {
            id: frmbID + '-allow-select',
            text: opts.messages.allowSelect,
            href: '#',
            'class': 'allow-select'
        }).prop('checked', 'checked'),
        editXML = $('<a/>', {
            id: frmbID + '-edit-xml',
            text: opts.messages.editXML,
            href: '#',
            'class': 'edit-xml'
        }),
        editNames = $('<a/>', {
            id: frmbID + '-edit-names',
            text: opts.messages.editNames,
            href: '#',
            'class': 'edit-names'
        }),
        clearAll = $('<a/>', {
            id: frmbID + '-clear-all',
            text: opts.messages.clearAll,
            href: '#',
            'class': 'clear-all'
        }),
        saveAll = $('<div/>', {
            id: frmbID + '-save',
            href: '#',
            'class': 'save-btn-wrap',
            title: opts.messages.save
        }).html('<a class="save fb-button primary"><span>' + opts.messages.save + '</span></a>'),
        actionLinksInner = $('<div/>', {
            id: frmbID + '-action-links-inner',
            'class': 'action-links-inner'
        }).append(editXML, ' | ', editNames, ' | ', allowSelect, ' | ', clearAll, ' |&nbsp;'),
        devMode = $('<span/>', {
            'class': 'dev-mode-link'
        }).html(opts.messages.devMode + ' ' + opts.messages.off),
        actionLinks = $('<div/>', {
            id: frmbID + '-action-links',
            'class': 'action-links'
        }).append(actionLinksInner, devMode);

        // Sortable fields
        var $sortableFields = $('<ul/>').attr('id', frmbID).addClass('frmb').sortable({
            cursor: 'move',
            opacity: 0.9,
            beforeStop: function beforeStop(event, ui) {
                var lastIndex = $('> li', $sortableFields).length - 1,
                curIndex = ui.placeholder.index();
                doCancel = curIndex <= 1 || curIndex === lastIndex;
            },
            start: _helpers.startMoving,
            stop: _helpers.stopMoving,
            cancel: 'input, .disabled, .sortable-options, .add, .btn, .no-drag',
            // items: 'li:not(.no-fields)',
            receive: function receive(event, ui) {
                // if (doCancel) {
                //   $('li:nth-child(' + curIndex + ')', $(this)).remove();
                // }
            },
            placeholder: 'frmb-placeholder'
        });

        // ControlBox with different fields
        cbUL.sortable({
            helper: 'clone',
            opacity: 0.9,
            connectWith: $sortableFields,
            cursor: 'move',
            placeholder: 'ui-state-highlight',
            start: _helpers.startMoving,
            stop: _helpers.stopMoving,
            revert: 150,
            change: function change(event, ui) {
                //fix the logic on this to only hide placeholder for disabledFields.before and after
                // if (ui.placeholder.index() === 0 || ui.placeholder.index() === $('> li', $sortableFields).last().index()) {
                //   $(ui.placeholder).hide();
                // } else {
                //   $(ui.placeholder).show();
                // }
            },
            remove: function remove(event, ui) {
                if (startIndex === 0) {
                    cbUL.prepend(ui.item);
                } else {
                    $('li:nth-child(' + startIndex + ')', cbUL).after(ui.item);
                }
            },
            beforeStop: function beforeStop(event, ui) {
                var lastIndex = $('> li', $sortableFields).length - 1,
                curIndex = ui.placeholder.index();
                doCancel = curIndex <= 1 || curIndex === lastIndex ? true: false;
                if (ui.placeholder.parent().hasClass('frmb-control')) {
                    doCancel = true;
                }
            },
            update: function update(event, ui) {
                // _helpers.stopMoving;
                elem.stopIndex = $('li', $sortableFields).index(ui.item) === 0 ? '0': $('li', $sortableFields).index(ui.item);
                if ($('li', $sortableFields).index(ui.item) < 0) {
                    $(this).sortable('cancel');
                } else {
                    prepFieldVars($(ui.item[0]), true);
                }
            },
            receive: function receive(event, ui) {
                if (ui.sender.hasClass('frmb') || ui.sender.hasClass('frmb-control')) {
                    $(ui.sender).sortable('cancel');
                }
            }
        });

        var $stageWrap = $('<div/>', {
            id: frmbID + '-stage-wrap',
            'class': 'stage-wrap'
        });

        var $formWrap = $('<div/>', {
            id: frmbID + '-form-wrap',
            'class': 'form-wrap'
        });

        elem.before($stageWrap).appendTo($stageWrap);

        // Replace the textarea with sortable list.
        //elem.before($sortableFields).parent().prepend(frmbHeader).addClass('frmb-wrap').append(actionLinks, viewXML, saveAll);
        var cbWrap = $('<div/>', {
            id: frmbID + '-cb-wrap',
            'class': 'cb-wrap'
        }).append(cbHeader, cbUL);

        $stageWrap.append($sortableFields, cbWrap, actionLinks, viewXML, saveAll);
        $stageWrap.before($formWrap);
        $formWrap.append($stageWrap, cbWrap);

        var doSave = function doSave() {
            if ($(this).parents('li.disabled').length === 0) {
                if ($(this).name === 'label' && $(this).val() === '') {
                    return alert('Error: ' + opts.messages.labelEmpty);
                }
                _helpers.save();
            }
        };

        // Not pretty but we need to save a lot so users don't have to keep clicking a save button
        $('input, select', $sortableFields).on('change', doSave);
        $('input, select', $sortableFields).on('blur', doSave);

        // Parse saved XML template data
        elem.getTemplate = function() {
            var xml = elem.val() !== '' ? $.parseXML(elem.val()) : false,
            fields = $(xml).find('field');

            if (fields.length > 0) {
                fields.each(function() {
                    prepFieldVars($(this));
                });
            } else if (!xml) {
                // Load default fields if none are set
                if (opts.defaultFields.length) {
                    for (var i = opts.defaultFields.length - 1; i >= 0; i--) {
                        appendNewField(opts.defaultFields[i]);
                    }
                } else {
                    $stageWrap.addClass('empty').attr('data-content', opts.messages.getStarted);
                }
                disabledBeforeAfter();
            }
        };

        var disabledBeforeAfter = function disabledBeforeAfter() {
            var li = '<li class="disabled __POSITION__">__CONTENT__</li>';
            if (opts.disableFields.before && !$('.disabled.before', $sortableFields).length) {
                $sortableFields.prepend(li.replace('__POSITION__', 'before').replace('__CONTENT__', opts.disableFields.before));
            }
            if (opts.disableFields.after && !$('.disabled.after', $sortableFields).length) {
                $sortableFields.append(li.replace('__POSITION__', 'after').replace('__CONTENT__', opts.disableFields.after));
            }
        };

        var nameAttr = function nameAttr(field) {
            var epoch = new Date().getTime();
            return field.data('attrs').name + '-' + epoch;
        };

        var prepFieldVars = function prepFieldVars($field, isNew) {
            isNew = isNew || false;
            var fieldAttrs = $field.data('attrs') || {},
            fType = fieldAttrs.type || $field.attr('type'),
            isMultiple = fType.match(/(select|checkbox-group|radio-group)/),
            values = {};

            values.label = _helpers.htmlEncode($field.attr('label'));
            values.name = isNew ? nameAttr($field) : fieldAttrs.name || $field.attr('name');
            values.name = typeof values.name == 'undefined' ? '' : values.name;
            values.defaultVal = $field.attr('defaultVal') !== undefined ? _helpers.htmlEncode($field.attr('defaultVal')) : '';
            values.role = $field.attr('role');
            values.required = $field.attr('required');
            values.maxLength = $field.attr('max-length');
            values.toggle = $field.attr('toggle');
            values.type = fType;
            values.description = $field.attr('description') !== undefined ? _helpers.htmlEncode($field.attr('description')) : '';
            values.introduce = $field.attr('introduce') !== undefined ? _helpers.htmlEncode($field.attr('introduce')) : '';
            if (isMultiple) {
                values.multiple = true;
                values.values = [];
                $field.children().each(function(i) {
                    var value = {
                        label: $(this).text(),
                        value: $(this).attr('value'),
                        selected: $field.attr('default') === i ? true: false
                    };
                    values.values.push(value);
                });
            }

            appendNewField(values);
            $stageWrap.removeClass('empty');
            disabledBeforeAfter();
        };

        // multi-line textarea
        var appendTextarea = function appendTextarea(values) {
            appendFieldLi(opts.messages.richText, advFields(values), values);
        };

        var appendInput = function appendInput(values) {
            var type = values.type || 'text';
            appendFieldLi(opts.messages[type], advFields(values), values);
        };
        var textLabel = function textLabel(values) {
            var type = values.type || 'text';
            values.introduce = values.introduce || opts.messages.textIntroduce;
            appendFieldLi(opts.messages[type], advFields(values), values);
        }
        // add select dropdown
        var appendSelectList = function appendSelectList(values) {

            if (!values.values || !values.values.length) {
                values.values = [{
                    selected: 'false',
                    label: 'Option 1',
                    value: 'option-1'
                },
                {
                    selected: 'false',
                    label: 'Option 2',
                    value: 'option-2'
                }];
            }

            var field = '',
            name = _helpers.safename(values.name),
            multiDisplay = values.type === 'checkbox-group' ? 'none': 'none';

            field += advFields(values);
            field += '<div class="false-label">' + opts.messages.selectOptions + '</div>';
            field += '<div class="fields">';

            field += '<div class="allow-multi" style="display:' + multiDisplay + '">';
            field += '<input type="checkbox" id="multiple_' + lastID + '" name="multiple"' + (values.multiple ? 'checked="checked"': '') + '>';
            field += '<label class="multiple" for="multiple_' + lastID + '">' + opts.messages.selectionsMessage + '</label>';
            field += '</div>';
            field += '<ol class="sortable-options">';
            for (i = 0; i < values.values.length; i++) {
                field += selectFieldOptions(values.values[i], name, values.values[i].selected, values.multiple);
            }
            field += '</ol>';
            field += '<div class="field_actions"><a href="#" class="add add_opt"><strong>' + opts.messages.add + '</strong></a> | <a href="#" class="close_field">' + opts.messages.close + '</a></div>';
            field += '</div>';
            appendFieldLi(opts.messages.select, field, values);

            $('.sortable-options').sortable(); // making the dynamically added option fields sortable.
        };

        var appendNewField = function appendNewField(values) {
            if (values === undefined) {
                values = '';
            }

            // TODO: refactor to move functions into this object
            var appendFieldType = {
                // 'text': appendTextInput(values),
                // 'checkbox': appendCheckbox(values),
                // 'select': appendSelectList(values),
                // 'textarea': appendTextarea(values),
                'text-label': textLabel,
                '2': appendInput,
                'date': appendInput,
                'autocomplete': appendInput,
                'checkbox': appendInput,
                'select': appendSelectList,
                'rich-text': appendTextarea,
                'textarea': appendTextarea,
                'radio-group': appendSelectList,
                'checkbox-group': appendSelectList,
                'text': appendInput,
                'hidden': appendInput
            };

            if (typeof appendFieldType[values.type] === 'function') {
                appendFieldType[values.type](values);
            }
        };

        /**
     * Build the editable properties for the field
     * @param  {object} values configuration object for advanced fields
     * @return {string}        markup for advanced fields
     */
        var advFields = function advFields(values) {
            var advFields = '',
            key, roles = values.role !== undefined ? values.role.split(',') : [];
            var fieldLabel = $('<div>', {
                'class': 'frm-fld label-wrap'
            });
            $('<label/>').html(opts.messages.label + ' *').appendTo(fieldLabel);
            $('<input>', {
                type: 'text',
                name: 'label',
                value: values.label,
                'class': 'fld-label'
            }).appendTo(fieldLabel);
            advFields += fieldLabel[0].outerHTML;

            var fieldDesc = $('<div>', {
                'class': 'frm-fld description-wrap'
            });
            $('<label/>').html(opts.messages.description + ' *').appendTo(fieldDesc);

            if (values.type == 'text-label') { //text-label
                advFields += '<div class="frm-fld description-wrap"><label>' + opts.messages.textIntroduce + '</label>';
                advFields += '<input type="text" name="introduce" value="' + values.introduce + '" class="fld-introduce" id="introduce-' + lastID + '" /></div>';
            }
            if (values.type != 'text-label') {
                advFields += '<div class="frm-fld description-wrap"><label>' + opts.messages.description + '</label>';
                advFields += '<input type="text" name="description" value="' + values.description + '" class="fld-description" id="description-' + lastID + '" /></div>';
                advFields += '<div class="frm-fld name-wrap"><label>' + opts.messages.name + ' <span class="required">*</span></label>';
                advFields += '<input type="text" name="name" value="' + values.name + '" class="fld-name" id="title-' + lastID + '" /></div>';
            }

            advFields += '<div class="frm-fld access-wrap"><label>' + opts.messages.roles + '</label>';

            advFields += '<input type="checkbox" name="enable_roles" value="" ' + (values.role !== undefined ? 'checked': '') + ' id="enable_roles-' + lastID + '"/> <label for="enable_roles-' + lastID + '" class="roles_label">' + opts.messages.limitRole + '</label>';
            advFields += '<div class="frm-fld available-roles" ' + (values.role !== undefined ? 'style="display:block"': '') + '>';
            for (key in opts.roles) {
                if ($.inArray(key, ['date', '4']) === -1) {
                    advFields += '<input type="checkbox" name="roles[]" value="' + key + '" id="fld-' + lastID + '-roles-' + key + '" ' + ($.inArray(key, roles) !== -1 ? 'checked': '') + ' class="roles-field" /><label for="fld-' + lastID + '-roles-' + key + '">' + opts.roles[key] + '</label><br/>';
                }
            }
            advFields += '</div></div>';

            // if field type is not checkbox, checkbox/radio group or select list, add max length
            if ($.inArray(values.type, ['checkbox', 'select', 'checkbox-group', 'date', 'autocomplete', 'radio-group', 'text-label']) < 0) {
                advFields += '<div class="frm-fld"><label class="max-length-label">' + opts.messages.maxLength + '</label>';
                advFields += '<input type="text" name="max-length" max-length="4" value="' + (values.maxLength !== undefined ? values.maxLength: '') + '" class="fld-max-length" id="max-length-' + lastID + '" /></div>';
            }

            return advFields;
        };

        // Append the new field to the editor
        var appendFieldLi = function appendFieldLi(title, field, values) {
            var label = $(field).find('input[name="label"]').val() !== '' ? $(field).find('input[name="label"]').val() : title;
            var defaultVal = typeof values.defaultVal != 'undefined' ? values.defaultVal : '';
            var introduce = $(field).find('input[name="introduce"]').val() !== '' ? $(field).find('input[name="introduce"]').val() : '';
            var li = '',
            delBtn = '<a id="del_' + lastID + '" class="del-button btn delete-confirm" href="#" title="' + opts.messages.removeMessage + '">' + opts.messages.remove + '</a>',
            toggleBtn = '<a id="frm-' + lastID + '" class="toggle-form btn icon-pencil" href="#" title="' + opts.messages.hide + '"></a> ',
            required = values.required,
            toggle = values.toggle || undefined,
            tooltip = values.description !== '' ? '<span class="tooltip-element" tooltip="' + values.description + '">?</span>': '';

            li += '<li id="frm-' + lastID + '-item" class="' + values.type + ' form-field">';
            li += '<div class="legend">';
            li += delBtn;
            li += '<span id="txt-title-' + lastID + '" class="field-label">' + label + '</span>' + tooltip + '<span class="required-asterisk" ' + (required === 'true' ? 'style="display:inline"': '') + '> *</span>' + toggleBtn;
            li += '</div>';
            if (values.type == 'text-label') {
                li += '<div class="frm-fld">';
                li += '<label class="field-introduce">' + introduce + '</label>';
                li += '</div>';
            }
            li += '<div class="prev-holder">' + fieldPreview(values) + '</div>';
            li += '<div id="frm-' + lastID + '-fld" class="frm-holder">';
            li += '<div class="form-elements">';

            if (values.type != 'text-label') {
                li += '<div class="frm-fld">';
                li += '<label>&nbsp;</label>';
                li += '<input class="required" type="checkbox" value="1" name="required-' + lastID + '" id="required-' + lastID + '"' + (required === 'true' ? ' checked="checked"': '') + ' /><label class="required_label" for="required-' + lastID + '">' + opts.messages.required + '</label>';

                if (values.type === 'checkbox') {
                    li += '<div class="frm-fld">';
                    li += '<label>&nbsp;</label>';
                    li += '<input class="checkbox-toggle" type="checkbox" value="1" name="toggle-' + lastID + '" id="toggle-' + lastID + '"' + (toggle === 'true' ? ' checked="checked"': '') + ' /><label class="toggle-label" for="toggle-' + lastID + '">' + opts.messages.toggle + '</label>';
                    li += '</div>';
                }
                li += '</div>';
            }

            if (values.type == 'hidden') {
                li += '<div class="frm-fld">';
                li += '<label class="field-defaultVal">'+opts.messages.defaultValue+'</label>';
                li += '<input type="text" class="fld-defaultVal" name="defaultVal" id="defaultVal-'+lastID+'" value="'+ defaultVal +'" />';
                li += '</div>';
            }
            li += field;
            li += '</div>';
            li += '</div>';
            li += '</li>';

            if (elem.stopIndex) {
                $('li', $sortableFields).eq(elem.stopIndex).after(li);
            } else {
                $sortableFields.append(li);
            }

            $(document.getElementById('frm-' + lastID + '-item')).hide().slideDown(250);

            lastID++;
            _helpers.save();
        };

        /**
     * Generate preview markup
     * @param  {object} attrs
     * @return {string}       preview markup for field
     */
        var fieldPreview = function fieldPreview(attrs) {
            var i, preview = '',
            epoch = new Date().getTime();
            switch (attrs.type) {
            case 'textarea':
                preview = '<' + attrs.type + '></' + attrs.type + '>';
                break;
            case 'select':
                var options;
                attrs.values.reverse();
                for (i = attrs.values.length - 1; i >= 0; i--) {
                    options += '<option value="' + attrs.values[i].value + '">' + attrs.values[i].label + '</option>';
                }
                preview = '<' + attrs.type + ' class="no-drag">' + options + '</' + attrs.type + '>';
                break;
            case 'checkbox-group':
            case 'radio-group':
                var type = attrs.type.replace('-group', '');
                attrs.values.reverse();
                for (i = attrs.values.length - 1; i >= 0; i--) {
                    preview += '<div class="inner-checkbox"><input type="' + type + '" id="' + type + '-' + epoch + '-' + i + '" value="' + attrs.values[i].value + '" /><label for="' + type + '-' + epoch + '-' + i + '">' + attrs.values[i].label + '</label></div>';
                }
                break;
            case 'text':
            case 'password':
            case 'hidden':
            case 'email':
            case 'date':
            case 'checkbox':
                var toggle = attrs.toggle ? 'toggle': '';
                preview = '<input type="' + attrs.type + '" ' + toggle + ' placeholder="">';
                break;
            case 'autocomplete':
                preview = '<input class="ui-autocomplete-input" autocomplete="on" placeholder="">';
                break;
            default:
                preview = '<' + attrs.type + '></' + attrs.type + '>';
            }

            return preview;
        };

        // Select field html, since there may be multiple
        var selectFieldOptions = function selectFieldOptions(values, name, selected, multipleSelect) {
            var selectedType = multipleSelect ? 'checkbox': 'radio';

            if (typeof values !== 'object') {
                values = {
                    label: '',
                    value: ''
                };
            } else {
                values.label = values.label || '';
                values.value = values.value || '';
            }

            field = '<li>';
            field += '<input type="' + selectedType + '" ' + selected + ' class="select-option" name="' + name + '" />';
            field += '<input type="text" class="option-label" placeholder="' + opts.messages.optionLabelPlaceholder + '" value="' + values.label + '" />';
            field += '<input type="text" class="option-value" placeholder="' + opts.messages.optionValuePlaceholder + '" value="' + values.value + '" />';
            field += '<a href="#" class="remove btn" title="' + opts.messages.removeMessage + '">' + opts.messages.remove + '</a>';
            field += '</li>';

            return field;
        };

        // ---------------------- UTILITIES ---------------------- //
        // delete options
        $sortableFields.delegate('.remove', 'click',
        function(e) {
            e.preventDefault();
            var optionsCount = $(this).parents('.sortable-options:eq(0)').children('li').length;
            if (optionsCount <= 2) {
                alert('Error: ' + opts.messages.minOptionMessage);
            } else {
                $(this).parent('li').slideUp('250',
                function() {
                    $(this).remove();
                });
            }
        });

        // toggle fields
        $sortableFields.on('click', '.toggle-form',
        function(e) {
            e.preventDefault();
            var targetID = $(this).attr('id');
            $(this).toggleClass('open').parent().next('.prev-holder').slideToggle(250);
            $(document.getElementById(targetID + '-fld')).slideToggle(250,
            function() {
                _helpers.save();
            });
        });

        // update preview to label
        $sortableFields.delegate('input[name="label"]', 'keyup',
        function() {
            $('.field-label', $(this).closest('li')).text($(this).val());
        });

        $sortableFields.delegate('input[name="introduce"]', 'keyup',
        function() {
            $('.field-introduce', $(this).closest('li')).text($(this).val());
        });
        $sortableFields.delegate('input[name="defaultVal"]', 'keyup',
        function() {
            _helpers.save();
        });
        // remove error styling when users tries to correct mistake
        $sortableFields.delegate('input.error', 'keyup',
        function() {
            $(this).removeClass('error');
        });

        // update preview for description
        $sortableFields.delegate('input[name="description"]', 'keyup',
        function() {
            var closestToolTip = $('.tooltip-element', $(this).closest('li'));
            if ($(this).val() !== '') {
                if (!closestToolTip.length) {
                    var tt = '<span class="tooltip-element" tooltip="' + $(this).val() + '">?</span>';
                    $('.toggle-form', $(this).closest('li')).before(tt);
                    // _helpers.initTooltip(tt);
                } else {
                    closestToolTip.attr('tooltip', $(this).val()).css('display', 'inline-block');
                }
            } else {
                if (closestToolTip.length) {
                    closestToolTip.css('display', 'none');
                }
            }
        });

        _helpers.updateMultipleSelect();

        // format name attribute
        $sortableFields.delegate('input[name="name"]', 'keyup',
        function() {
            $(this).val(_helpers.safename($(this).val()));
            if ($(this).val() === '') {
                $(this).addClass('field_error').attr('placeholder', opts.messages.cannotBeEmpty);
            } else {
                $(this).removeClass('field_error');
            }
        });

        $sortableFields.delegate('input.fld-max-length', 'keyup',
        function() {
            $(this).val(_helpers.forceNumber($(this).val()));
        });

        // Delete field
        $sortableFields.delegate('.delete-confirm', 'click',
        function(e) {
            e.preventDefault();

            // lets see if the user really wants to remove this field... FOREVER
            var fieldWarnH3 = $('<h3/>').html('<span></span>' + opts.messages.warning),
            deleteID = $(this).attr('id').replace(/del_/, ''),
            delBtn = $(this),
            $field = $(document.getElementById('frm-' + deleteID + '-item')),
            toolTipPageX = delBtn.offset().left - $(window).scrollLeft(),
            toolTipPageY = delBtn.offset().top - $(window).scrollTop();

            if (opts.showWarning) {
                jQuery('<div />').append(fieldWarnH3, opts.messages.fieldRemoveWarning).dialog({
                    modal: true,
                    resizable: false,
                    width: 300,
                    dialogClass: 'ite-warning',
                    open: function open() {
                        $('.ui-widget-overlay').css({
                            'opacity': 0.0
                        });
                    },
                    position: [toolTipPageX - 282, toolTipPageY - 178],
                    buttons: [{
                        text: opts.messages.yes,
                        click: function click() {
                            $field.slideUp(250,
                            function() {
                                $(this).remove();
                                _helpers.save();
                            });
                            $(this).dialog('close');
                        }
                    },
                    {
                        text: opts.messages.no,
                        'class': 'cancel',
                        click: function click() {
                            $(this).dialog('close');
                        }
                    }]
                });
            } else {
                $field.slideUp(250,
                function() {
                    $(this).remove();
                    _helpers.save();
                });
            }

            if ($('.form-field', $sortableFields).length === 1) {
                $stageWrap.addClass('empty');
            }
        });

        // Attach a callback to toggle required asterisk
        $sortableFields.delegate('input.required', 'click',
        function() {
            var requiredAsterisk = $(this).parents('li.form-field').find('.required-asterisk');
            requiredAsterisk.toggle();
        });

        // Attach a callback to toggle roles visibility
        $sortableFields.delegate('input[name="enable_roles"]', 'click',
        function() {
            var roles = $(this).siblings('div.available-roles'),
            enableRolesCB = $(this);
            roles.slideToggle(250,
            function() {
                if (!enableRolesCB.is(':checked')) {
                    $('input[type="checkbox"]', roles).removeAttr('checked');
                }
            });
        });

        // Attach a callback to add new checkboxes
        $sortableFields.delegate('.add_ck', 'click',
        function() {
            $(this).parent().before(selectFieldOptions());
            return false;
        });

        $sortableFields.delegate('li.disabled .form-element', 'mouseenter',
        function() {
            _helpers.disabledTT($(this));
        });

        // Attach a callback to add new options
        $sortableFields.delegate('.add_opt', 'click',
        function(e) {
            e.preventDefault();
            var isMultiple = $(this).parents('.fields').first().find('input[name="multiple"]')[0].checked,
            name = $(this).parents('.fields').find('.select-option:eq(0)').attr('name');
            $(this).parents('.fields').first().find('.sortable-options').append(selectFieldOptions(false, name, false, isMultiple));
            _helpers.updateMultipleSelect();
        });

        // Attach a callback to close link
        $sortableFields.delegate('.close_field', 'click',
        function(e) {
            e.preventDefault();
            $(this).parents('li.form-field').find('.toggle-form').trigger('click');
        });

        // Attach a callback to add new radio fields
        $sortableFields.delegate('.add_rd', 'click',
        function(e) {
            e.preventDefault();
            $(this).parent().before(selectFieldOptions(false, $(this).parents('.frm-holder').attr('id')));
        });

        $('.form-elements .fields .remove, .frmb .del-button').on('hover',
        function() {
            $(this).parents('li.form-field').toggleClass('delete');
        });

        // View XML
        $(document.getElementById(frmbID + '-export-xml')).click(function(e) {
            e.preventDefault();
            var xml = elem.val(),
            $pre = $('<pre />').text(xml);
            $pre.dialog({
                resizable: false,
                modal: true,
                width: 720,
                dialogClass: 'frmb-xml',
                overlay: {
                    color: '#333333'
                }
            });
        });

        // Clear all fields in form editor
        $(document.getElementById(frmbID + '-clear-all')).click(function(e) {
            e.preventDefault();
            if (window.confirm(opts.messages.clearAllMessage)) {
                $sortableFields.empty();
                elem.val('');
                _helpers.save();
                var values = {
                    label: [opts.messages.descriptionField],
                    name: ['content'],
                    required: 'true',
                    description: opts.messages.mandatory
                };

                appendNewField(values);
                $sortableFields.prepend(opts.disableFields.before);
                $sortableFields.append(opts.disableFields.after);
            }
        });

        // Save Idea Template
        $(document.getElementById(frmbID + '-save')).click(function(e) {
            if ($(this).find('.ldkInlineEdit').length === 0) {
                e.preventDefault();
                if (!$stageWrap.hasClass('edit-xml')) {
                    _helpers.save();
                }
                _helpers.validateForm(e);
            }
        });

        var triggerDevMode = false,
        keys = [],
        devCode = '68,69,86';
        // Super secret Developer Tools
        $('.save.fb-button').mouseover(function() {
            triggerDevMode = true;
        }).mouseout(function() {
            triggerDevMode = false;
        });
        $(document.documentElement).keydown(function(e) {
            keys.push(e.keyCode);
            if (keys.toString().indexOf(devCode) >= 0) {
                $('.action-links').toggle();
                $('.view-xml').toggle();
                keys = [];
            }
        });
        // Toggle Developer Mode
        $('.dev-mode-link').click(function(e) {
            e.preventDefault();
            var dml = $(this);
            $stageWrap.toggleClass('dev-mode');
            dml.parent().css('opacity', 1);
            if ($stageWrap.hasClass('dev-mode')) {
                dml.siblings('.action-links-inner').css('width', '100%');
                dml.html(opts.messages.devMode + ' ' + opts.messages.on).css('color', '#8CC63F');
            } else {
                dml.siblings('.action-links-inner').css('width', 0);
                dml.html(opts.messages.devMode + ' ' + opts.messages.off).css('color', '#666666');
                triggerDevMode = false;
                $('.action-links').toggle();
                $('.view-xml').toggle();
            }
        });

        // Toggle Edit Names
        $(document.getElementById(frmbID + '-edit-names')).click(function(e) {
            e.preventDefault();
            $(this).toggleClass('active');
            $('.name-wrap', $sortableFields).slideToggle(250,
            function() {
                $stageWrap.toggleClass('edit-names');
            });
        });

        // Toggle Allow Select
        $(document.getElementById(frmbID + '-allow-select')).click(function(e) {
            e.preventDefault();
            $(this).toggleClass('active');
            $('.allow-multi, .select-option', $sortableFields).slideToggle(250,
            function() {
                $stageWrap.toggleClass('allow-select');
            });
        });

        // Toggle Edit XML
        $(document.getElementById(frmbID + '-edit-xml')).click(function(e) {
            e.preventDefault();
            $(this).toggleClass('active');
            $('textarea.idea-template').show();
            $('.template-textarea-wrap').slideToggle(250);
            $stageWrap.toggleClass('edit-xml');
        });

        elem.parent().find('p[id*="ideaTemplate"]').remove();
        elem.wrap('<div class="template-textarea-wrap"/>');
        elem.getTemplate();
    };

    $.fn.formBuilder = function(options) {
        var form = this;
        return form.each(function() {
            var element = $(this);
            if (element.data('formBuilder')) {
                return;
            }
            var formBuilder = new FormBuilder(this, options);
            element.data('formBuilder', formBuilder);
        });
    };
})(jQuery);

// toXML is a jQuery plugin that turns our form editor into XML
(function($) {
    'use strict';
    $.fn.toXML = function(options) {
        var defaults = {
            prepend: '',
            attributes: ['class']
        };
        var opts = $.extend(defaults, options);

        var serialStr = '';

        // Begin the core plugin
        this.each(function() {
            var liCount = 0;
            var c = 1;

            if ($(this).children().length >= 1) {
                serialStr += '<form-template>\n\t<fields>';

                // build new xml
                $(this).children().each(function() {
                    var $field = $(this);
                    if (! ($field.hasClass('moving') || $field.hasClass('disabled'))) {
                        for (var att = 0; att < opts.attributes.length; att++) {
                            var required = $('input.required', $field).is(':checked') ? 'required="true" ': 'required="false" ',
                            multipleChecked = $('input[name="multiple"]', $field).is(':checked'),
                            multiple = multipleChecked ? 'style="multiple" ': '',
                            t = $field.attr(opts.attributes[att]).replace(' form-field', ''),
                            // field type
                            multipleField = t.match(/(select|checkbox-group|radio-group)/),
                            type = 'type="' + t + '" ',
                            fName = $('input.fld-name', $field).length > 0 ? ('name="' + $('input.fld-name', $field).val() + '" ') : '',
                            fLabel = 'label="' + $('input.fld-label', $field).val() + '" ',
                            roleVals = $.map($('input.roles-field:checked', $field),
                            function(n) {
                                return n.value;
                            }).join(','),
                            roles = roleVals !== '' ? 'role="' + roleVals + '" ': '',

                            desc = $('input.fld-description', $field).length > 0 ? ('description="' + $('input.fld-description', $field).val() + '" ') : '',
                            introduce = $('input.fld-introduce', $field).length > 0 ? ('introduce="' + $('input.fld-introduce', $field).val() + '" ') : '',
							defaultVal = $('input.fld-defaultVal', $field).length > 0 ? ('defaultVal="' + $('input.fld-defaultVal', $field).val() + '" ') : '',
                            maxLengthVal = $('input.fld-max-length', $field).val(),
                            maxLength = 'max-length="' + (maxLengthVal !== undefined ? maxLengthVal: '') + '" ',
                            fSlash = !multipleField ? '/': '';
                            var fToggle = $('.checkbox-toggle', $field).is(':checked') ? 'toggle="true" ': '';

                            serialStr += '\n\t\t<field ' + fName + fLabel + defaultVal + fToggle + multiple + roles + desc + introduce + (maxLengthVal !== '' ? maxLengthVal !== undefined ? maxLength: '': '') + required + type + fSlash +'>';
                            if (multipleField) {
                                c = 1;
                                $('.sortable-options li', $field).each(function() {
                                    var $option = $(this),
                                    optionValue = 'value="' + $('.option-value', $option).val() + '"',
                                    optionLabel = $('.option-label', $option).val(),
                                    selected = $('.select-option', $option).is(':checked') ? ' selected="true"': '';
                                    serialStr += '\n\t\t\t<option' + selected + ' ' + optionValue + '>' + optionLabel + '</option>';
                                    c++;
                                });
                                serialStr += '\n\t\t</field>';
                            }
                        }
                    }
                    liCount++;
                });
                serialStr += '\n\t</fields>\n</form-template>';
            } // if "$(this).children().length >= 1"
        });
        return serialStr;
    };
})(jQuery);