PageSourceSearch

https://help.tabletopia.com/wp-content/plugins/font/js/jquery.fontPlugin.js?pver=7.5.1&ver=6.0.16

js tabletopia.com collected 2026-09-24 08:54:06 UTC 127,606 bytes, 2,943 lines download raw bytes

1//font control panel plugin
2(function ($) {
3    /*globals document, setTimeout, setInterval, clearTimeout, window, alert, prompt, confirm, navigator, jQuery, console, $, ajaxproxy, location, tinyMCE, fontBlogUrl, fontBlogName, clearInterval, fontPluginVersion, ajaxproxy, ajaxproxy2, ajaxproxy3*/
4    $.fontPlugin = function (el, options) {
5        var self = this;
6        self.$el = $(el);
7        self.el = el;
8        self.baseSelector = '#' + self.$el.attr('id');
9        self.$el.data('fontPlugin', self);
10        //modal windows array
11        self.modals = [];
12        self.options = options;
13        self.options = $.extend({}, $.fontPlugin.defaultOptions, self.options);
14        self.version = fontPluginVersion;
15        self.settingFields = [];
16        self.effectSliders = [];
17        /*
18        Initialize
19        */
20        self.init = function () {
21            self.detectMode();
22            self.showLoading();
23            self.loadSettings();
24            if (!$.fn.on) {
25                alert('WARNING: You are using an old Wordpress version. It is not guaranteed that the plugin will work. The oldest tested Wordpress version is 3.1 but at least 3.3.1 is recommended.');
26            }
27        };
28        /*
29         *
30         * MARKUP
31         *
32         */
33        /*
34         * Create presets panel
35         */
36        self.createPresetsPanel = function () {
37            var i,
38                presetName,
39                presetId,
40                field,
41                paramObj,
42                upgrade,
43                modalOptions,
44                modal;
45            /*
46            PRESETS
47            */
48            modalOptions = {
49                id: 'presetsPanel',
50                name: 'stdSettings',
51                title: 'Font ' + self.version,
52                nobar: false,
53                left: '10px',
54                top: '50px',
55                width: '98px',
56                show: true
57            };
58            modal = self.createNewModal(modalOptions);
59            modal.show();
60            self.$presets = modal;
61            //add presets list
62            self.settingFields.presets = $('<fieldset id="presetsDropdownFieldset"><label>Selections<select id="presetsDropdown" name="presetsDropdown"></select></label></fieldset>');
63            self.$presetsDropdown = self.settingFields.presets.find('select');
64            self.$presets.append(self.settingFields.presets);
65            self.$presetsDropdown.bind('change', function () {
66                self.loadPreset($(this).val());
67                self.scrollToSelection();
68            });
69            //populate presets list
70            for (i = 0; i < self.options.presets.length; i += 1) {
71                presetName = self.options.presets[i].name;
72                presetId = i;
73                self.$presetsDropdown.append('<option value="' + presetId + '">' + presetName + '</option>');
74            }
75            // Delete current preset
76            self.settingFields.deletePreset = $('<button id="deletePreset" title="Delete">x</button>');
77            self.settingFields.presets.append(self.settingFields.deletePreset);
78            self.settingFields.deletePreset.on('click', function () {
79                self.deleteCurrentPreset();
80            });
81            // Rename current preset
82            self.settingFields.renamePreset = $('<button id="renamePreset">Rename</button>');
83            self.settingFields.presets.append(self.settingFields.renamePreset);
84            self.settingFields.renamePreset.on('click', function () {
85                self.renamePreset();
86            });
87        };
88        /**
89        Like? Yes or No
90        **/
91        self.addLikeYesNo = function () {
92            //add like not/like
93            self.$yesno = $('<div class="yesno">Like the plugin?<br><a href="http://fontsforweb.com/purchase/howtosupport?url=' + fontBlogUrl + '&name=' + fontBlogName + '" class="overlay_url yesbutton" data-upgrade="true">Yes!</a> or <a href="http://www.fontsforweb.com/contact/support" class="overlay_url nobutton">No</a></div>');
94            self.$yesno.appendTo(self.$actions);
95            self.$yesno.css({
96                'color': '#fff',
97                'font-size': '10px',
98                'text-align': 'right',
99                'font-weight': 'bold',
100                'float': 'left',
101                'margin-right': '10px'
102            });
103            self.$yesno.find('a').css({
104                'font-size': '10px',
105                'color': '#fff'
106            });
107        };
108        /*
109         * Create appearance panel - size and color
110         *
111         * @returns {unresolved}
112         */
113        self.createAppearancePanel = function () {
114            var i,
115                presetName,
116                presetId,
117                field,
118                paramObj,
119                saveButton,
120                upgrade,
121                modalOptions,
122                modal,
123                settingWindows;
124            /*
125            SELECTORS
126            */
127            //get standard fields in a new window
128            modalOptions = {
129                id: 'appearancePanel',
130                name: 'stdSettings',
131                title: 'Size and color settings',
132                left: '0',
133                top: '0px',
134                width: '100%',
135                nobar: true,
136                show: true
137            };
138            modal = self.createNewModal(modalOptions);
139            self.$apr = modal;
140            self.$apr.hide();
141            //create a fieldset
142            self.$standardFields = $('<div id="appearanceStandardFields"></div>').appendTo(modal);
143            //get standard fields in a new window
144            modalOptions = {
145                title: 'Extended settings',
146                left: '0',
147                top: '0'
148            };
149            modal = self.createNewModal(modalOptions);
150            self.$extendedFields = $('<div id="appearanceExtendedFields"></div>').appendTo(modal);
151            //get extended fields not ready
152            /*var switcher = $('<a href="#" id="showExtendedFields">Load extended fields from FontsForWeb.com</a>').appendTo(self.$apr);
153            self.$extendedFields.hide();
154            switcher.on('click', function () { self.$extendedFields.slideToggle(); self.loadExtendedFields(); return false; });*/
155            /*
156            Add all setting fields
157            */
158            for (i = 0; i < self.options.settingFields.length; i += 1) {
159                field = self.options.settingFields[i];
160                paramObj = {};
161                paramObj.field = field;
162                paramObj.type = 'lite';
163                self.addSettingsField(paramObj);
164            }
165            //add window opening buttons
166            settingWindows = $('<fieldset id="settingsButtons"></fieldset>');
167            //bind toggle modal
168            $('body').on('click', '.toggleModal', function () {
169                if (self.mode === 'global') {
170                    var selector = self.currentPreset.selector;
171                    if (selector.indexOf('ELEMENT NOW') != -1 || selector.indexOf('PICK ELEMENT') != -1) {
172                        alert('First pick an element from the page to customize it.');
173                        return;
174                    }
175                } else if (self.mode === 'postPage') {
176                    //VERSION FOR POST PAGE
177                    //get selection
178                    var selection = tinyMCE.activeEditor.selection.getContent();
179                    //only apply if something is selected
180                    if (!selection) {
181                        alert('Make a selection in the editor window first.');
182                        return;
183                    }
184                }
185                var modalName = $(this).attr('data-modal-name');
186                self.toggleModal(modalName);
187            });
188            $('body').on('mousedown click', '.getApiKey', function () {
189                $(this).attr('href', 'http://fontsforweb.com/purchase?url=' + fontBlogUrl);
190                $(this).attr('target', '_blank');
191            });
192            //add keyup to change field
193            self.getField('selector').bind('keyup', function () {
194                $(this).trigger('change');
195                self.hoverSelector = self.currentPreset.selector;
196                self.hoverHighlightCurrent();
197            });
198        };
199        self.createActionButtons = function () {
200            var saveButton,
201                actions,
202                closeMod;
203            //add buttons fieldset
204            actions = $('<fieldset id="actionsFieldset"></fieldset>');
205            closeMod = $('<a class="closeModal" href="#">x</a>');
206            actions.append(closeMod);
207            self.$actions = actions;
208            closeMod.click(function () {
209                self.$presets.hide();
210                self.$apr.hide();
211            });
212            //add different fields buttons
213            var fontfaceSettings = $('<button id="fontfaceSettings">Font</button>').prependTo(self.$apr);
214            //add actions
215            fontfaceSettings.on('click', function () {
216                if (self.mode === 'global') {
217                    var selector = self.currentPreset.selector;
218                    if (selector.indexOf('ELEMENT NOW') !== -1 || selector.indexOf('PICK ELEMENT') !== -1) {
219                        alert('First pick an element from the page to customize it.');
220                        return;
221                    }
222                } else if (self.mode === 'postPage') {
223                    //VERSION FOR POST PAGE
224                    //get selection
225                    var selection = tinyMCE.activeEditor.selection.getContent();
226                    //only apply if something is selected
227                    if (!selection) {
228                        alert('Make a selection in the editor window first.');
229                        return;
230                    }
231                }
232                self.showFontsBrowser();
233            });
234            // PRO settings window
235            var proSettings = $('<a href="http://fontsforweb.com/purchase/pluginupgradesubscribe?url=' + fontBlogUrl + '&name=' + fontBlogName + '" class="overlay_url" id="upgradeToProButton" data-upgrade="true">Pro settings</a>').appendTo(self.$apr);
236            // extra fields
237            var extrafieldsSettings = $('<button id="extrafieldsSettings" class="toggleModal" data-modal-name="extrafields">PRO</button>').appendTo(self.$apr);
238            // hide extra fields
239            extrafieldsSettings.hide();
240            // effects button
241            var effects = $('<button id="effectsButton" class="toggleModal" data-modal-name="effects">Effects</button>').appendTo(self.$apr);
242            // styles button
243            var styles = $('<button id="stylesButton" class="toggleModal" data-modal-name="styles">Styles</button>').appendTo(self.$apr);
244
245            //add save button
246            saveButton = $('<button id="fontSaveSettings">Save settings</button>').appendTo(actions);
247            saveButton.click(function (e) {
248                // blink few fimes
249                saveButton.fadeTo(500, 0.3).fadeTo(500, 1).fadeTo(500, 0.3).fadeTo(500, 1).fadeTo(500, 0.3).fadeTo(500, 1).fadeTo(500, 0.3).fadeTo(500, 1).fadeTo(500, 0.3).fadeTo(500, 1);
250                //unbind click
251                saveButton.unbind('click');
252                saveButton.css({
253                    'cursor': 'wait'
254                });
255                self.saveSettings();
256            });
257            self.$apr.append(actions);
258        };
259        /*
260         * API FUNCTIONS
261         */
262        /*
263         * check if api key is present in the database
264         */
265        self.checkForApikey = function () {
266            self.xhrPost({
267                url: self.options.FFW_baseUrl + '/api/getkey',
268                data: {
269                    blogurl: fontBlogUrl,
270                    apikey: self.options.apikey,
271                    version: self.version
272                }
273            }, function (data) {
274                if (!data || data.success !== 'true') {
275                    if (console !== undefined) {
276                        console.log(data.message);
277                    }
278                    alert('An error has occured and the plugin is not working. If you\'re working on local server, make sure \n that your computer has an internet connection.');
279                } else {
280                    if(data.keytype === 'FREE' && self.loaded === true) {
281                        return;
282                    }
283                    self.loadEffectsPanel(data.effects);
284                    if (data.extended.length) {
285                        self.loadExtendedFields(data.extended);
286                        $('#upgradeToProButton').remove();
287                        $('#extrafieldsSettings').show();
288                        clearInterval(self.checkForApikeyInterval);
289                    }
290                    self.loadStylesPanel(data.styles);
291                    self.options.apikey = data.apikey;
292                    self.loaded = true;
293                }
294            });
295        };
296        //setup api key interval
297        self.setupApikeyCheckingInterval = function () {
298            if (!self.checkForApikeyInterval) {
299                self.checkForApikeyInterval = setInterval(function () {
300                    self.checkForApikey();
301                }, 5000);
302            }
303        };
304        /*
305         * SETTINGS - SAVE AND LOAD
306         */
307        /*
308         * Load settings from wordpress options
309         */
310        self.loadSettings = function () {
311            var data = {
312                action: 'get_font_settings'
313            };
314            $.ajax({
315                type: "POST",
316                url: ajaxproxy,
317                data: data,
318                success: function (response) {
319                    // json is coming parsed so no $.parseJSON(response);
320                    try {
321                        response = $.parseJSON(response);
322                    } catch (e) {
323                        self.tryAnotherAjaxproxy();
324                        return;
325                    }
326                    //console.log(self.options);
327                    if (response && response.settingFields) {
328                        self.options = $.extend({}, response, self.options);
329                    } else {
330                        self.tryAnotherAjaxproxy();
331                        return;
332                    }
333                    self.loadConfig();
334                },
335                error: function (response) {
336                    self.tryAnotherAjaxproxy();
337                }
338            });
339        };
340        self.tryAnotherAjaxproxy = function () {
341            alert('Sorry, but the plugin couldn\'t start. Please contact your server administrator to allow AJAX use. Power users: Use console (F12 in Chrome) to see what else could be the problem.');
342            return;
343            /*if (ajaxproxy === ajaxproxy3) {
344                alert('Sorry, but the plugin couldn\'t start. Please contact your server administrator to allow AJAX use. Power users: Use console (F12 in Chrome) to see what else could be the problem.');
345                return;
346            }
347            else if (ajaxproxy === ajaxproxy2) {
348                ajaxproxy = ajaxproxy3;
349            } else {
350                ajaxproxy = ajaxproxy2;
351            }*/
352            self.loadSettings();
353        };
354
355        /**
356        *
357        * continue loading with config file
358        *
359        **/
360        self.loadConfig = function () {
361            // try loading the plugin
362            try {
363                //console.log(self.options);
364                self.hideLoading();
365                // markup
366                self.createPresetsPanel();
367                self.createAppearancePanel();
368                self.createActionButtons();
369                self.loadPreset(0);
370                self.bindShowCategoriesAction();
371                self.bindShowFontsAction();
372                self.bindLinkOverlay();
373                self.setFontOnclick();
374                self.initUploadForm();
375                self.adaptOptionsToMode();
376                self.addLikeYesNo();
377                self.checkForApikey();
378                if (self.options.presets && self.options.presets[1]) {
379                    self.loadPreset(1);
380                }
381            } catch (e) {
382                console.dir(e);
383                alert('The plugin will not work');
384            }
385        },
386        /*
387         * Save settings by ajax
388         *
389         * @returns {unresolved}
390         */
391        self.saveSettings = function () {
392            var savingOptions = {},
393                data;
394            if (self.mode === 'postPage') {
395                alert('Click on "Update" or "Publish" to save');
396                return;
397            }
398            self.$presets.hide();
399            self.$apr.hide();
400            self.showLoading();
401            savingOptions.settingFields = self.options.settingFields;
402            savingOptions.presets = self.options.presets;
403            savingOptions.apikey = self.options.apikey;
404            data = {
405                action: 'set_font_settings',
406                fontPluginSettings: JSON.stringify(savingOptions)
407            };
408            $.post(ajaxproxy, data, function (response) {
409                if (response.success !== 'true') {
410                    alert('Error: ' + response.message);
411                    return;
412                } else {
413                    window.onbeforeunload = null;
414                    location.reload(true);
415                }
416            }, 'json').error(function (xhr, textStatus, errorThrown) {
417                alert("Unknown error: \n" + xhr.responseText);
418            });
419        };
420        //update internal settings - value
421        self.updateSettings = function (settingName, value) {
422            if (self.mode !== 'global') {
423                return;
424            }
425            self.options.presets[self.currentPresetNo][settingName] = value;
426        };
427        //update internal settings - css value
428        self.updateCssSettings = function (settingName, value) {
429            if (self.mode !== 'global') {
430                return;
431            }
432            self.options.presets[self.currentPresetNo].styles[settingName] = value;
433        };
434        /*
435        update selected element CSS
436        global - update element from selector
437        postPage - update selected text
438        */
439        self.updateSelectedElement = function (settingName, value) {
440            var selection,
441                selector,
442                selectorValid,
443                $selectorContents,
444                element,
445                node,
446                justInsertedClass,
447                newSpan,
448                newHTML,
449                inserted,
450                $tempDiv;
451            if (!value || value == 'px') return;
452            if (self.mode === 'global') {
453                //VERSION FOR GENERAL SETTINGS PAGE
454                selector = self.currentPreset.selector;
455                //exit when default selector
456                if (selector.indexOf('ELEMENT NOW') != -1 || selector.indexOf('PICK ELEMENT') != -1) {
457                    return;
458                }
459                //get the element from iframe or current page
460                if ($('#header1preview').length) {
461                    element = $('#header1preview').contents().find(selector);
462                } else {
463                    element = $(selector);
464                }
465                element.each(function () {
466                    //CSS exceptions and general action
467                    switch (settingName) {
468                    case 'rotate':
469                        $(this).style('-webkit-transform', 'rotate(' + value + 'deg)', 'important');
470                        $(this).style('-moz-transform', 'rotate(' + value + 'deg)', 'important');
471                        $(this).style('-o-transform', 'rotate(' + value + 'deg)', 'important');
472                        $(this).style('-ms-transform', 'rotate(' + value + 'deg)', 'important');
473                        $(this).style('transform', 'rotate(' + value + 'deg)', 'important');
474                        break;
475                        //general action
476                    default:
477                        $(this).style(settingName, value, 'important');
478                    }
479                });
480            } else if (self.mode === 'postPage') {
481                //VERSION FOR POST PAGE
482                //get selection
483                selection = tinyMCE.activeEditor.selection.getContent();
484                //only apply if something is selected
485                if (!selection) {
486                    return;
487                }
488                //selectorValid - validate selector created from selected text. 
489                //required because for example "." with nothing following breaks function execution
490                selectorValid = true;
491                try {
492                    $selectorContents = $(selection);
493                } catch (e) {
494                    selectorValid = false;
495                }
496                if (selectorValid && $selectorContents.length > 1) {
497                    selectorValid = false;
498                }
499                //get node
500                node = tinyMCE.activeEditor.selection.getNode();
501                if (selectorValid && ($.trim($(node).html()) === $.trim(selection) || $.trim($(node).html()) === $.trim($(selection).html()))) {
502                    //console.log('already isolated');
503                    tinyMCE.activeEditor.dom.setStyle(node, settingName, value);
504                } else {
505                    //console.log('isolating: ' + $(node).html() + ' - ' + selection);
506                    justInsertedClass = 'inserted' + Math.floor(Math.random() * 10000);
507                    newSpan = $('<span id="' + justInsertedClass + '">' + selection + '</span>');
508                    newSpan.css(settingName, value);
509                    $tempDiv = $('<div>').append(newSpan.clone());
510                    $tempDiv.find('*').each(function () {
511                        //if ($(this).inlineStyle && $(this).inlineStyle(settingName)) {
512                            $(this).css(settingName, value);
513                        //}
514                    });
515                    //get span html together with span itself
516                    newHTML = $tempDiv.html();
517                    inserted = tinyMCE.activeEditor.selection.setContent(newHTML);
518                    tinyMCE.activeEditor.selection.select(tinyMCE.activeEditor.dom.select('span#' + justInsertedClass)[0]);
519                }
520            }
521        };
522        /*
523         *
524         * FIELDS
525         *
526         */
527        /*
528         * Load extended fields
529         */
530        self.loadExtendedFields = function (data) {
531            var i,
532                field,
533                paramObj,
534                modalOptions,
535                modal;
536            //create a new modal for it
537            modalOptions = {
538                name: 'extrafields',
539                title: 'Pro options',
540                left: '5px',
541                top: '40px',
542                width: '270px'
543            };
544            modal = self.createNewModal(modalOptions);
545            for (i = 0; i < data.length; i += 1) {
546                field = data[i];
547                paramObj = {};
548                paramObj.field = field;
549                paramObj.type = 'extrafields';
550                self.addSettingsField(paramObj);
551            }
552            self.reloadPreset();
553        };
554        /*
555         * Add a field to selected place
556         * paramObj
557         *
558         * @returns {unresolved}
559         *  .field - field definition - no data or assignations here
560         *    .settings - contain field settings
561         */
562        self.addSettingsField = function (paramObj, loadDefaults) {
563            var field = paramObj.field,
564                thisField,
565                thisInput,
566                i,
567                manipulator,
568                fieldset,
569                fieldParam,
570                container,
571                inputFields = [];
572            if (loadDefaults === undefined) {
573                loadDefaults = false;
574            }
575            //add support for multi value field
576            if (field.settingType === 'cssmultival') {
577                if (!paramObj.target) {
578                    //create a new modal for it
579                    var modalOptions = {
580                        name: field.name,
581                        title: field.label,
582                        left: '0',
583                        top: '50px',
584                        width: '270px'
585                    };
586                    container = self.createNewModal(modalOptions);
587                } else {
588                    container = paramObj.target;
589                }
590                //create a fieldset
591                fieldset = self.settingFields[field.name] = $('<fieldset id="' + field.name + 'Fieldset"></fieldset>');
592                //add fieldset to extended fields
593                container.append(fieldset);
594                var XYSliderOptions = false;
595                //add all controls
596                for (i = 0; i < field.values.length; i += 1) {
597                    fieldParam = field.values[i];
598                    var privateInputField = thisInput;
599                    var subfield = fieldParam;
600                    //create a field with label around
601                    var manipulator = $('<label>' + subfield.label + '<a class="resetField">reset</a><input type="' + subfield.type + '" title="' + subfield.settingName + '"></label>');
602                    manipulator.appendTo(fieldset);
603                    privateInputField = manipulator.find('input');
604                    privateInputField.data('fieldInfo', subfield);
605                    privateInputField.data('settings', paramObj.settings);
606                    // ADD CORRESPONDING ENTRY IN FIELD SETTINGS
607                    if (paramObj.settings && !paramObj.settings.params) {
608                        var params = {};
609                        paramObj.settings.params = params;
610                    }
611                    //append field to extended list
612                    fieldset.append(thisField);
613                    //extend field with whatever
614                    privateInputField = self.extendField(privateInputField, subfield.extendWith);
615                    // if handle is assigned add actions
616                    if (subfield.handle) {
617                        if (!XYSliderOptions) {
618                            XYSliderOptions = {};
619                            XYSliderOptions.actions = [];
620                        }
621                        // action can be x, y, deg, distance
622                        // stop - last action don't trigger input change
623                        XYSliderOptions.id = privateInputField.data('settings').uniqueId;
624                        inputFields[subfield.handle.linkTo] = privateInputField;
625                    }
626                }
627                var XYSlider = false;
628                if (XYSliderOptions) {
629                    var rand = Math.random() * 1000,
630                        stop = true;
631                    // if xy slider set then add it
632                    XYSliderOptions.label = field.label;
633                    if (field.invertX) {
634                        XYSliderOptions.invertX = true;
635                    }
636                    XYSliderOptions.settingName = paramObj.settings.uniqueId;
637                    var Mathra = Math.random();
638                    XYSliderOptions.actions = function (x, y, deg, distance) {
639                        inputFields['x'] ? (
640                            inputFields['x'].val(x),
641                            inputFields['x'].trigger('change', stop)
642                        ) : false;
643                        inputFields['y'] ? (
644                            inputFields['y'].val(y),
645                            inputFields['y'].trigger('change', stop)
646                        ) : false;
647                        inputFields['deg'] ? (
648                            inputFields['deg'].val(deg),
649                            inputFields['deg'].trigger('change', stop)
650                        ) : false;
651                        inputFields['distance'] ? (
652                            inputFields['distance'].val(distance / 2),
653                            inputFields['distance'].trigger('change', stop)
654                        ) : false;
655                    };
656                    //XYSliderOptions.target = paramObj.target;
657                    var $XYSlider = self.currentElement.XYSlider(XYSliderOptions);
658                    XYSlider = $XYSlider.data('XYSlider');
659                    //add to effect handles
660                    self.effectSliders[paramObj.settings.uniqueId] = XYSlider.$handleHolder;
661                    if (!paramObj.settings.enabled) {
662                        self.effectSliders[paramObj.settings.uniqueId].hide();
663                    }
664                }
665                //bind action to the inputs
666                var inputs = fieldset.find('input');
667                //bind change to inputs
668                inputs.bind('change click keyup', function (event, stop) {
669                    // settings 
670                    var settings = $(this).data('settings'),
671                        fieldInfo = $(this).data('fieldInfo'),
672                        storage = $(this).data('storage'),
673                        val = $(this).val(),
674                        XYSliderX,
675                        XYSliderY;
676                    if (stop === undefined) stop = false;
677                    // add arrow up - down support
678                    //37 - left, 38 - up, 39 - right, 40 - down
679                    if (event.which === 38 && !isNaN(val)) {
680                        $(this).val(parseFloat(val) + 1);
681                        $(this).trigger('change');
682                        return false;
683                    } else if (event.which === 40 && !isNaN(val)) {
684                        $(this).val(parseFloat(val) - 1);
685                        $(this).trigger('change');
686                        return false;
687                    }
688                    // save param setting
689                    settings.params[fieldInfo.settingName] = val;
690                    
691                    //console.log($(this).data('fieldInfo').settingName + ', ' + $(this).val() + ', ' + ($(this).data('fieldInfo').unit || ''))
692                    //get values of all siblings and create one value for the element
693                    var completeValue = '';
694                    inputs.each(function () {
695                        var fieldInfo = $(this).data('fieldInfo'),
696                            unit = '';
697                        if (fieldInfo.unit) {
698                            unit = fieldInfo.unit;
699                        }
700                        //get field info
701                        completeValue += ' ' + $(this).val() + unit;
702                        if ($(this).val() == '') {
703                            $(this).val(fieldInfo['default']);
704                        }
705                        if (XYSlider && !stop && fieldInfo.handle) {
706                            if (fieldInfo.handle.linkTo === 'x') {
707                                XYSliderX = $(this).val();
708                            } else if (fieldInfo.handle.linkTo === 'y') {
709                                XYSliderY = $(this).val();
710                            }
711                        }
712                    });
713                    // when changing value of the input move the handle too
714                    // if this input is linked to a handle
715                    if (XYSlider && !stop && fieldInfo.handle) {
716                        // get "linked to" of a handle and move x or y accordingly
717                        if (XYSliderX && XYSliderY) {
718                            XYSlider.setPosition(XYSliderX, XYSliderY);
719                        } else if (XYSliderX) {
720                            XYSlider.setPosition(XYSliderX, false);
721                        } else if (XYSliderY) {
722                            XYSlider.setPosition(false, XYSliderY);
723                        }
724                    }
725                    // if this is text-shadow
726                    if (field.settingName === 'text-shadow') {
727                        //var effect = self.getElementsEffect(settings.uniqueId);
728                        completeValue = completeValue.replace(/, +$/, '');
729                        var XYSliderValues = XYSlider.getCurrentValues();
730                        self.drawEffects({
731                            completeValue: completeValue,
732                            target: self.currentElement,
733                            color: settings.params.color,
734                            x: -XYSliderValues.x,
735                            y: XYSliderValues.y,
736                            deg: 180 - XYSliderValues.deg * -1,
737                            distance: XYSliderValues.distance,
738                            effectId: settings.uniqueId,
739                            effectsList: ['textShadow']
740                        });
741                    } else if (field.settingName === 'text-stroke') {
742                        // go through all settings
743                        //var effect = self.getElementsEffect(settings.uniqueId);
744                        completeValue = completeValue.replace(/, +$/, '');
745                        //var XYSliderValues = XYSlider.getCurrentValues();
746                        self.drawEffects({
747                            target: self.currentElement,
748                            color: settings.params.color,
749                            distance: settings.params.width,
750                            effectId: settings.uniqueId,
751                            spread: settings.params.spread,
752                            effectsList: ['stroke']
753                        });
754                    } else if (field.settingName === 'text-extrude') {
755                        var XYSliderValues = XYSlider.getCurrentValues();
756                        self.drawEffects({
757                            target: self.currentElement,
758                            color: settings.params.color,
759                            deg: 180 - XYSliderValues.deg * -1,
760                            distance: XYSliderValues.distance,
761                            effectId: settings.uniqueId,
762                            effectsList: ['extrude3d']
763                        });
764                    } else if (field.settingName === 'text-extrude-shadow') {
765                        var XYSliderValues = XYSlider.getCurrentValues();
766                        self.drawEffects({
767                            color: settings.params.color,
768                            target: self.currentElement,
769                            deg: 180 - XYSliderValues.deg * -1,
770                            distance: XYSliderValues.distance,
771                            effectId: settings.uniqueId,
772                            effectsList: ['extrude3d', 'shadow3d']
773                        });
774                    } else if (field.settingName === 'text-shadow-multiple') {
775                        var XYSliderValues = XYSlider.getCurrentValues();
776                        self.drawEffects({
777                            target: self.currentElement,
778                            color: settings.params.color,
779                            opacity: settings.params.opacity,
780                            deg: 180 - XYSliderValues.deg * -1,
781                            distance: XYSliderValues.distance,
782                            effectId: settings.uniqueId,
783                            effectsList: ['shadow3d']
784                        });
785                    } else {
786                        //console.log(field.settingName + ': ' + completeValue);
787                        self.updateCssSettings(field.settingName, completeValue);
788                        self.updateSelectedElement(field.settingName, completeValue);
789                    }
790                    return false;
791                });
792                
793                // load defaults
794                if (loadDefaults) {
795                    for(var i = 0; i < inputs.length; i++) {
796                        inputs.eq(i).trigger('change');
797                    }
798                    //
799                    inputs.eq(0).trigger('change');
800                }
801                
802                //reset field button action
803                fieldset.find('a.resetField').click(function () {
804                    thisInput.val('');
805                    thisInput.trigger('change');
806                    alert('When resetting settings or deleting preset you have to save to see the change');
807                });
808            } else {
809                self.settingFields[field.name] = $('<fieldset id="' + field.name + 'Fieldset"><label>' + field.label + '<a class="resetField">reset</a><input type="' + field.type + '" name="' + field.name + '" title="' + field.settingName + '" id="' + field.name + 'Field"></label></fieldset>');
810                thisField = self.settingFields[field.name];
811                thisInput = self.getField(field.name);
812                thisInput.data('fieldInfo', field);
813                if (paramObj.target) {
814                    paramObj.target.append(thisField);
815                } else if (paramObj.type === 'premium') {
816                    self.$extendedFields.append(thisField);
817                } else if (paramObj.type === 'extrafields') {
818                    self.modals['extrafields'].append(thisField);
819                } else {
820                    if (field.name === 'selector') {
821                        thisField.insertAfter('#presetsDropdownFieldset');
822                    } else {
823                        self.$standardFields.append(thisField);
824                    }
825                }
826                //reset field button action
827                thisField.find('a.resetField').click(function () {
828                    thisInput.val(0);
829                    thisInput.trigger('change');
830                    thisInput.val('none');
831                    thisInput.trigger('change');
832                    thisInput.val('inherit');
833                    thisInput.trigger('change');
834                    thisInput.val('');
835                    thisInput.trigger('change');
836                });
837                if (field.settingType === 'dropdown') {
838                    thisInput = self.extendField(thisInput, 'dropdown');
839                } else {
840                    thisInput = self.extendField(thisInput, field.extendWith);
841                }
842                //general actions - apply settings
843                if (field.settingType === 'css' || field.settingType === 'dropdown') {
844                    //bind change to color picker
845                    thisInput.bind('change click keyup', function () {
846                        //console.log($(this).data('fieldInfo').settingName + ', ' + $(this).val() + ', ' + ($(this).data('fieldInfo').unit || ''))
847                        self.updateCssSettings($(this).data('fieldInfo').settingName, $(this).val());
848                        self.updateSelectedElement($(this).data('fieldInfo').settingName, $(this).val() + ($(this).data('fieldInfo').unit || ''));
849                        return false;
850                    });
851                } else if (field.settingType === 'csstransform') {
852                    //bind change to color picker
853                    thisInput.bind('change click keyup', function () {
854                        //console.log($(this).data('fieldInfo').settingName + ', ' + $(this).val() + ', ' + ($(this).data('fieldInfo').unit || ''))
855                        self.updateCssSettings($(this).data('fieldInfo').settingName, $(this).val());
856                        self.updateSelectedElement($(this).data('fieldInfo').settingName, $(this).val() + ($(this).data('fieldInfo').unit || ''));
857                        return false;
858                    });
859                } else if (field.settingType === 'general') {
860                    //bind change to color picker
861                    thisInput.bind('change click', function () {
862                        self.updateSettings($(this).data('fieldInfo').settingName, $(this).val());
863                        self.updateSelectedElement($(this).data('fieldInfo').settingName, $(this).val());
864                        return false;
865                    });
866                }
867            }
868        };
869        // extend field with slider, color picker etc
870        self.extendField = function (field, extendWith) {
871            if (extendWith === 'slider') {
872                var input = (function () {
873                    var min = 1,
874                        max = 100,
875                        step = 1,
876                        value = 40,
877                        orientation = 'horizontal',
878                        reverse = false,
879                        $slider = $('<div>'),
880                        input = field,
881                        fieldset = input.closest('fieldset');
882                    fieldset.addClass('slider-extended');
883                    fieldset.append($slider);
884                    if (input.data('fieldInfo').slider) {
885                        min = parseFloat(input.data('fieldInfo').slider.min);
886                        max = parseFloat(input.data('fieldInfo').slider.max);
887                        step = parseFloat(input.data('fieldInfo').slider.step);
888                        if (input.data('fieldInfo').slider.orientation) {
889                            orientation = input.data('fieldInfo').slider.orientation;
890                        }
891                        if (input.data('fieldInfo').slider.value) {
892                            value = input.data('fieldInfo').slider.value;
893                        }
894                        if (input.data('fieldInfo').slider.reverse) {
895                            reverse = true;
896                        }
897                    }
898                    //init slider
899                    $slider.slider({
900                        range: 'min',
901                        value: value,
902                        min: min,
903                        max: max,
904                        step: step,
905                        orientation: orientation,
906                        slide: function (event, ui) {
907                            if (!reverse) {
908                                input.val(ui.value);
909                            } else {
910                                //reversed values
911                                input.val(max + min - ui.value);
912                            }
913                            input.trigger('change');
914                        }
915                    });
916                    //bind value change to the size slider
917                    input.bind('change click', function () {
918                        var val = $(this).val();
919                        if (val) {
920                            $slider.slider('value', parseFloat(val));
921                        }
922                        return false;
923                    });
924                    return input;
925                }());
926            } else if (extendWith === 'dropdown') {
927                var input = (function () {
928                    var $dropdown = $('<select><option></option></select>'),
929                        input = field,
930                        fieldset = input.parents('fieldset'),
931                        key;
932                    fieldset.addClass('dropdown-extended');
933                    fieldset.find('label').append($dropdown);
934                    for(key in input.data('fieldInfo').dropdown) {
935                        var val = input.data('fieldInfo').dropdown[key];
936                        $dropdown.append('<option value="' + key + '">' + val + '</option>');
937                    }
938                    $dropdown.bind('change', function () {
939                        input.val($(this).val());
940                        input.trigger('change');
941                    });
942                    //bind value change to the size slider
943                    input.bind('change click', function () {
944                        var val = $(this).val();
945                        if (val) {
946                            $dropdown.val(val);
947                        }
948                        return false;
949                    });
950                    return input;
951                }());
952            } else if (extendWith === 'colorPicker') {
953                var input = (function () {
954                    var input = field;
955                    input.change(function () {
956                        $(this).css('backgroundColor', $(this).val());
957                    });
958                    //init color picker
959                    input.ColorPicker({
960                        color: '#0000ff',
961                        onShow: function (colpkr) {
962                            $(colpkr).show();
963                            return false;
964                        },
965                        onHide: function (colpkr) {
966                            $(colpkr).hide();
967                            return false;
968                        },
969                        onChange: function (hsb, hex, rgb) {
970                            input.val('#' + hex);
971                            input.trigger('change');
972                            input.trigger('change').css('backgroundColor', '#' + hex);
973                        },
974                        onBeforeShow: function () {
975                            $(this).ColorPickerSetColor(this.value);
976                        }
977                    }).bind('keyup', function () {
978                        $(this).ColorPickerSetColor(this.value);
979                    });
980                    return input;
981                }());
982            } else if (extendWith === 'selectorPicker') {
983                var input = (function (field) {
984                    var replacementField,
985                        thisInput,
986                        selectorPicker,
987                        selectorPicker2,
988                        input = field,
989                        field = field.data('fieldInfo'),
990                        fieldset = input.parents('fieldset');
991                    self.settingFields[field.name] = $('<fieldset id="selectorFieldset"><label><input type="' + field.type + '" name="' + field.name + '" title="' + field.settingName + '">CSS selecor editor (advanced)</label></fieldset>');
992                    replacementField = self.settingFields[field.name];
993                    thisInput = self.getField(field.name);
994                    selectorPicker = $('<button class="pickElement">Pick element</button>');
995                    selectorPicker2 = $('<button class="pickElement pickAddElement">Pick element(add)</button>');
996                    self.$selectorPicker = selectorPicker;
997                    self.$selectorPicker2 = selectorPicker2;
998                    //
999                    //setTimeout(function () {
1000                    self.$selectorPicker.insertBefore(self.settingFields.presets);
1001                    //}, 100);
1002                    //replacementField.prepend(selectorPicker);
1003                    replacementField.prepend(selectorPicker2);
1004                    selectorPicker.on('click', function () {
1005                        self.bindGetSelector();
1006                        return false;
1007                    });
1008                    selectorPicker2.on('click', function () {
1009                        self.bindGetSelector(true);
1010                        return false;
1011                    });
1012                    //replace default field
1013                    fieldset.replaceWith(replacementField);
1014                    replacementField.find('label').hide();
1015                    thisInput.data('fieldInfo', field);
1016                    //return replaced input
1017                    return thisInput;
1018                }(field));
1019            }
1020            return input;
1021        };
1022        // get field value
1023        self.getField = function (name) {
1024            var input = self.settingFields[name].find('input');
1025            return input;
1026        };
1027        // set field value
1028        self.setField = function (name, value) {
1029            var input = self.getField(name);
1030            input.val(value);
1031            input.trigger('change');
1032        };
1033        /*
1034         * EFFECTS
1035         */
1036        self.loadEffectsPanel = function (data) {
1037            var i,
1038                field,
1039                paramObj,
1040                dropdown,
1041                option,
1042                field,
1043                effectsList = $('<div class="effectsList">');
1044            // save effects to array
1045            self.effects = data;
1046            // Create effects modal
1047            var modalOptions = {
1048                id: 'effectsPanel',
1049                name: 'effects', // name conflict was here
1050                title: 'Effects',
1051                left: '50%',
1052                top: '50px',
1053                width: '190px'
1054            };
1055            var modal = self.createNewModal(modalOptions);
1056            dropdown = $('<select>');
1057            dropdown.append($('<option>Add new effect</option>'));
1058            // add options to the modal
1059            for (i = 0; i < data.length; i += 1) {
1060                field = data[i];
1061                option = $('<option value="' + field.settingName + '">' + field.label + '</option>');
1062                dropdown.append(option);
1063            }
1064            if (data.length < 3) {
1065                dropdown.append('<option value="upgrade">More options</option>');
1066            }
1067            // add dropdown to modal
1068            modal.append(dropdown);
1069            // change
1070            dropdown.change(function () {
1071                if ($(this).val() == 'upgrade') {
1072                    var link = $('<a href="http://fontsforweb.com/purchase/pluginupgradesubscribe?url=' + fontBlogUrl + '&name=' + fontBlogName + '" class="overlay_url" id="upgradeToProButton" data-upgrade="true">Pro settings</a>').appendTo('body');
1073                    link.click();
1074                    link.remove();
1075                    return;
1076                }
1077                self.addEffect($(this).val());
1078                dropdown.find('option').eq(0).attr('selected', 'selected');
1079            });
1080            modal.append(effectsList);
1081            effectsList.sortable({
1082                stop: function () {
1083                    // get new order of items
1084                    var items = effectsList.find('> div'),
1085                        newEffectsOrder = [];
1086                    // for each item
1087                    items.each(function (ind) {
1088                        var effectSettings = $(this).data('settings');
1089                        effectSettings.effectNo = ind;
1090                        newEffectsOrder.push(effectSettings);
1091                    });
1092                    self.currentPreset.effects = newEffectsOrder;
1093                    // refresh text shadow effects
1094                    self.updateTextShadowEffects(self.currentElement);
1095                }
1096            });
1097        };
1098        /*
1099         * Styles
1100         */
1101        self.loadStylesPanel = function (data) {
1102            var i,
1103                field,
1104                paramObj,
1105                $list,
1106                field,
1107                $li,
1108                $div;
1109            // save effects to array
1110            self.styles = data;
1111            // Create styles modal
1112            var modalOptions = {
1113                id: 'stylesPanel',
1114                name: 'styles', // name conflict was here
1115                title: 'Style presets',
1116                right: '0',
1117                top: '50px',
1118                width: '400px'
1119            };
1120            var modal = self.createNewModal(modalOptions);
1121            $list = $('<ul>');
1122            // add dropdown to modal
1123            modal.append($list);
1124            // add options to the modal
1125            for (i = 0; i < data.length; i += 1) {
1126                field = data[i];
1127                $div = $('<div>');
1128                $li = $('<li>').append($div)
1129                $list.append($li);
1130
1131                $div.text(field.label);
1132                $div.attr('data-value', field.settingName);
1133                
1134                // create temporary preset
1135                self.createPreset(field.settingName, '[data-value="' + field.settingName + '"]');
1136                self.loadStylePreset(field.settingName);
1137                self.unhighlightCurrent();
1138                self.deletePreset(self.currentPresetNo, true);
1139            }
1140            if (data.length < 3) {
1141                var button = $('<button>More options</button>');
1142                $list.append(button);
1143                button.click(function () {
1144                    var link = $('<a href="http://fontsforweb.com/purchase/pluginupgradesubscribe?url=' + fontBlogUrl + '&name=' + fontBlogName + '" class="overlay_url" id="upgradeToProButton" data-upgrade="true">Pro settings</a>').appendTo('body');
1145                    link.click();
1146                    link.remove();
1147                    return;
1148                });
1149            }
1150            modal.append(button);
1151            // change
1152            $list.on('click', 'li', function () {
1153                var value = $(this).find('div').data('value');
1154                if (value === 'upgrade') {
1155                    var link = $('<a href="http://fontsforweb.com/purchase/pluginupgradesubscribe?url=' + fontBlogUrl + '&name=' + fontBlogName + '" class="overlay_url" id="upgradeToProButton" data-upgrade="true">Pro settings</a>').appendTo('body');
1156                    link.click();
1157                    link.remove();
1158                    return;
1159                } else if (!value) {
1160                    return;
1161                }
1162                self.loadStylePreset(value);
1163            });
1164        };
1165        /**
1166        * Load style from style presets
1167        **/
1168        self.loadStylePreset = function (settingName) {
1169            var stylePreset = self.getStyleDefinitionByName(settingName),
1170                presetSettings = stylePreset.presetSettings;
1171            
1172            self.unloadCurrentElementEffects();
1173            self.currentPreset.effects = [];
1174
1175            self.populateStyles(presetSettings);
1176            
1177            $.extend(true, self.currentPreset, presetSettings);
1178            self.loadEffectSettings();
1179            self.applyFont();
1180            self.loadFontPreview();
1181        };
1182        /*
1183         * load effect settings
1184         */
1185        self.loadEffectSettings = function () {
1186            // if no effects loaded yet quit
1187            if (!self.effects) return;
1188            if (!self.currentPreset.effects) return;
1189
1190            // get effect saved settings
1191            for (var i = 0; i < self.currentPreset.effects.length; i++) {
1192                var effect = self.currentPreset.effects[i],
1193                    // add effect in order to the effects list
1194                    $effectItem = self.createEffectEntry(effect);
1195                if (effect.enabled) {
1196                    $effectItem.find('input[name=enabled]').attr("checked", "true");
1197                }
1198                // go through all the parameters and set them accordingly
1199                // clone object beforehand to avoid properties overwriting inside effect
1200                var params = $.extend(true, {}, effect.params);
1201                for (var property in params) {
1202                    var value = params[property];
1203                    // get field from current effect
1204                    var found = $effectItem.find('input[title=' + property + ']');
1205                    if (found.length) {
1206                        found.val(value);
1207                        found.trigger('change');
1208                    }
1209                }
1210            }
1211        };
1212        /*
1213         * Unload current effect
1214         *
1215         * @returns {unresolved}
1216         */
1217        self.unloadCurrentElementEffects = function () {
1218            //console.log(self.effectSliders, self.currentPreset, self.currentPreset.effects);
1219            if (self.currentPreset === undefined || self.currentPreset.effects === undefined) return;
1220            if (self.effectSliders && Object.size(self.effectSliders)) {
1221                // unload all handles
1222                for (var i = 0; i < self.currentPreset.effects.length; i++) {
1223                    var effect = self.currentPreset.effects[i];
1224                    if (self.effectSliders[effect.uniqueId]) {
1225                        self.effectSliders[effect.uniqueId].hide();
1226                    }
1227                }
1228            }
1229
1230            if (!self.modals['effects']) return;
1231            self.modals['effects'].find('.effectsList').html('');
1232        };
1233        /*
1234         * add effect to effects list
1235         */
1236        self.addEffect = function (settingName) {
1237            var effectSettings = {},
1238                loadDefaults = 1;
1239            if (!self.currentPreset.effects) {
1240                self.currentPreset.effects = [];
1241                effectSettings.effectNo = 0;
1242            } else {
1243                effectSettings.effectNo = self.currentPreset.effects.length;
1244            }
1245            effectSettings.settingName = settingName;
1246            effectSettings.uniqueId = effectSettings.effectNo + Math.round(Math.random() * 1000000000) + settingName;
1247            effectSettings.enabled = 1;
1248            // add effect settings to preset
1249            self.currentPreset.effects.splice(0, 0, effectSettings);
1250            // create entry
1251            return self.createEffectEntry(effectSettings, loadDefaults);
1252        };
1253        /*
1254         * add effect to the list
1255         *
1256         * @param {type} effectId
1257         * @returns {effect}
1258         */
1259        self.createEffectEntry = function (effectSettings, loadDefaults) {
1260            //if effects modal not created yet don't load effects
1261            if (!self.modals['effects']) return;
1262            var effectItem,
1263                effectDefinition = self.getEffectDefinitionByName(effectSettings.settingName),
1264                effectsModal = self.modals['effects'].find('.effectsList'),
1265                effectParams = $('<div class="params"></div>'),
1266                enabledCheckbox = $('<input type="checkbox" name="enabled">'),
1267                arrow = $('<a href="#" class="effectArrow"></a>'),
1268                label = $('<a href="#" class="effectName">' + effectDefinition.label + '</a>'),
1269                closeButton = $('<a href="#" class="removeEffect">x</a>');
1270            if (loadDefaults === undefined) {
1271                loadDefaults = 0;
1272            }
1273            // create item markup
1274            effectItem = $('<div>');
1275            effectItem.append('<div class="label">');
1276            effectItem.find('.label').append(arrow);
1277            effectItem.find('.label').append(enabledCheckbox);
1278            effectItem.find('.label').append(label);
1279            effectItem.find('.label').append(closeButton);
1280            effectItem.append(effectParams);
1281            effectParams.hide();
1282            if (effectSettings.enabled) {
1283                enabledCheckbox.attr('checked', 'true');
1284            }
1285            // add settings
1286            effectsModal.prepend(effectItem);
1287            var paramObj = [];
1288            paramObj.field = effectDefinition;
1289            paramObj.settings = effectSettings;
1290            paramObj.target = effectParams;
1291            paramObj.type = 'extrafields';
1292            
1293            // actions
1294            label.click(function (e) {
1295                if (effectParams.is(':visible')) {
1296                    arrow.animateRotate(0);
1297                } else {
1298                    arrow.animateRotate(90);
1299                }
1300                e.preventDefault();
1301                effectParams.slideToggle();
1302            });
1303            arrow.click(function (e) {
1304                if (effectParams.is(':visible')) {
1305                    arrow.animateRotate(0);
1306                } else {
1307                    arrow.animateRotate(90);
1308                }
1309                e.preventDefault();
1310                effectParams.slideToggle();
1311            });
1312            // close button
1313            closeButton.click(function (e) {
1314                e.preventDefault();
1315                // delete effect from 
1316                self.removeEffect(effectSettings.uniqueId, effectItem);
1317            });
1318            // enable disable checkbox
1319            enabledCheckbox.click(function (e) {
1320                if ($(this).is(':checked')) {
1321                    self.enableEffect(effectSettings.uniqueId, effectItem);
1322                } else {
1323                    self.disableEffect(effectSettings.uniqueId, effectItem);
1324                }
1325            });
1326            self.addSettingsField(paramObj, loadDefaults);
1327            //add item reference in settings and settings in item
1328            //effectSettings.el = effectItem; ERROR on stringify
1329            effectItem.data('settings', effectSettings);
1330            return effectItem;
1331        };
1332        /*
1333         * get effect definition by id
1334         */
1335        self.getEffectDefinitionByName = function (effectId) {
1336            if (!self.effects) return false;
1337            // go thorugh all effects and find the ONE
1338            for (var i = 0; i < self.effects.length; i++) {
1339                var effect = self.effects[i];
1340                if (effect.settingName === effectId) {
1341                    return effect;
1342                }
1343            }
1344            return false;
1345        };
1346        /*
1347         * get style
1348         */
1349        self.getStyleDefinitionByName = function (styleId) {
1350            if (!self.styles) return false;
1351            // go thorugh all effects and find the ONE
1352            for (var i = 0; i < self.styles.length; i++) {
1353                var style = self.styles[i];
1354                if (style.settingName === styleId) {
1355                    return style;
1356                }
1357            }
1358            return false;
1359        };
1360        /*
1361         * Enable effect
1362         *
1363         * @param {type} effectId
1364         */
1365        self.enableEffect = function (effectId, $effectItem) {
1366            if (!self.currentPreset.effects) return false;
1367            var effect = self.getElementsEffect(effectId);
1368            effect.enabled = 1;
1369            // if has handle show it
1370            if (self.effectSliders[effectId]) {
1371                self.effectSliders[effectId].show();
1372            }
1373            // update text shadow effects
1374            self.updateTextShadowEffects(self.currentElement);
1375        };
1376        /*
1377         * Disable effect
1378         *
1379         * @param {type} effectId
1380         */
1381        self.disableEffect = function (effectId, $effectItem) {
1382            if (!self.currentPreset.effects) return false;
1383            var effect = self.getElementsEffect(effectId);
1384            effect.enabled = 0;
1385            // delete shadow css
1386            self.removeShadowEffect(effectId);
1387            // if has handle hide it
1388            if (self.effectSliders[effectId]) {
1389                self.effectSliders[effectId].hide();
1390            }
1391        };
1392        /*
1393         * Delete effect from an element
1394         *
1395         * @param {type} effectId
1396         */
1397        self.removeEffect = function (effectId, $effectItem) {
1398            if (!confirm('Remove the effect?')) return;
1399            if (!self.currentPreset.effects) return false;
1400            // go thorugh all effects and find the ONE
1401            for (var i = 0; i < self.currentPreset.effects.length; i++) {
1402                var effect = self.currentPreset.effects[i];
1403                if (effect.uniqueId === effectId) {
1404                    self.currentPreset.effects.splice(i, 1);
1405                }
1406            }
1407            // delete shadow css
1408            self.removeShadowEffect(effectId);
1409            if (self.effectSliders[effectId]) {
1410                // remove handle
1411                self.effectSliders[effectId].remove();
1412            }
1413            // delete entry
1414            $effectItem.remove();
1415        };
1416        /*
1417         * Get effect of an element
1418         *
1419         * @param {type} effectId
1420         */
1421        self.getElementsEffect = function (effectId) {
1422            if (!self.currentPreset.effects) return false;
1423            // go thorugh all effects and find the ONE
1424            for (var i = 0; i < self.currentPreset.effects.length; i++) {
1425                var effect = self.currentPreset.effects[i];
1426                if (effect.uniqueId === effectId) {
1427                    return effect;
1428                }
1429            }
1430        };
1431        /*
1432        draw effects
1433        options.target;
1434        options.deg;
1435        options.distance;
1436        options.effectId;
1437        options.effectsList;
1438        */
1439        self.drawEffects = function (options) {
1440            if (!self.shadowSupport) return false;
1441            var depth = options.distance / 10,
1442                subjectColor = options.target.css('color'),
1443                color = options.color,
1444                subjectColorRgb = self.parseRgb(subjectColor),
1445                rColor = subjectColorRgb.r,
1446                gColor = subjectColorRgb.g,
1447                bColor = subjectColorRgb.b,
1448                i = 0,
1449                j = 0,
1450                speed = i,
1451                xDist = 0,
1452                yDist = 0,
1453                //get effect
1454                effect = self.getElementsEffect(options.effectId),
1455                angle = options.deg;
1456            if (color) {
1457                var colorRgb = self.html2rgb(color.replace('#', ''));
1458            } else {
1459                colorRgb = subjectColorRgb;
1460            }
1461            // set default angle
1462            if (angle === undefined) {
1463                angle = 290;
1464            }
1465            // reset current elements calculated effect value
1466            effect.calculated = '';
1467            // go through effects which should be applied
1468            for (i = 0; i < options.effectsList.length; i++) {
1469                depth = options.distance / 10;
1470                var effectName = options.effectsList[i];
1471                switch (effectName) {
1472                case 'textShadow':
1473                    effect.calculated += options.completeValue;
1474                    //console.log(effect.calculated);
1475                    //completeValue += ' ' + $(this).val() + unit;
1476                    //effect.calculated = completeValue.replace(/, +$/,'')
1477                    break;
1478                case 'stroke':
1479                    var spread = 2;
1480                    if (options.spread) {
1481                        spread = options.spread;
1482                    }
1483                    // if distance is bigger than 4 add
1484                    if (options.distance > 4) {
1485                        // slash
1486                        effect.calculated += options.distance + 'px ' + options.distance + 'px ' + spread + 'px ' + color + ', ';
1487                        effect.calculated += -options.distance + 'px ' + options.distance + 'px ' + spread + 'px ' + color + ', ';
1488                        effect.calculated += options.distance + 'px ' + -options.distance + 'px ' + spread + 'px ' + color + ', ';
1489                        effect.calculated += -options.distance + 'px ' + -options.distance + 'px ' + spread + 'px ' + color + ', ';
1490                        // top and down
1491                        effect.calculated += options.distance + 'px 0px ' + spread + 'px ' + color + ', ';
1492                        effect.calculated += -options.distance + 'px 0px ' + spread + 'px ' + color + ', ';
1493                        effect.calculated += '0px ' + options.distance + 'px ' + spread + 'px ' + color + ', ';
1494                        effect.calculated += '0px ' + -options.distance + 'px ' + spread + 'px ' + color + ', ';
1495                    } else {
1496                        // only slash
1497                        effect.calculated += options.distance + 'px ' + options.distance + 'px ' + spread + 'px ' + color + ', ';
1498                        effect.calculated += -options.distance + 'px ' + options.distance + 'px ' + spread + 'px ' + color + ', ';
1499                        effect.calculated += options.distance + 'px ' + -options.distance + 'px ' + spread + 'px ' + color + ', ';
1500                        effect.calculated += -options.distance + 'px ' + -options.distance + 'px ' + spread + 'px ' + color + ', ';
1501                    }
1502                case 'extrude3d':
1503                    // get color color
1504                    rColor = colorRgb.r;
1505                    gColor = colorRgb.g;
1506                    bColor = colorRgb.b;
1507                    //initially take 20% off
1508                    rColor = rColor / 100 * 80,
1509                    gColor = gColor / 100 * 80,
1510                    bColor = bColor / 100 * 80;
1511                    //loop to depth
1512                    for (j = 0; j < depth; j++) {
1513                        //calculate x and y of next shadow
1514                        speed = 1.2;
1515                        xDist += speed * Math.sin(angle * Math.PI / 180);
1516                        yDist += speed * -Math.cos(angle * Math.PI / 180);
1517                        xDist = xDist;
1518                        yDist = yDist;
1519                        //percentage darkened
1520                        var percentageDone = j / depth;
1521                        //darkening strength - the bigger depth the lower sstrength
1522                        var light = 10 - percentageDone * 10;
1523                        //darken the next shadow a bit take 10%
1524                        rColor = Math.floor(rColor / 100 * 91 + light);
1525                        gColor = Math.floor(gColor / 100 * 91 + light);
1526                        bColor = Math.floor(bColor / 100 * 91 + light);
1527                        color = 'rgb(' + rColor + ', ' + gColor + ', ' + bColor + ')';
1528                        //put together shadow setting
1529                        effect.calculated += xDist.toFixed(2) + 'px ' + yDist.toFixed(2) + 'px ' + 0.3 + 'px ' + color + ', ';
1530                    }
1531                    break;
1532                case 'shadow3d': //generate text shadow value
1533                    // get color color
1534                    rColor = colorRgb.r;
1535                    gColor = colorRgb.g;
1536                    bColor = colorRgb.b;
1537                    //loop to depth
1538                    for (j = 0; j < depth; j++) {
1539                        if (j > 3 && i % 3) continue;
1540                        //calculate x and y of next shadow
1541                        speed = 6;
1542                        xDist += speed * Math.sin(angle * Math.PI / 180);
1543                        yDist += speed * -Math.cos(angle * Math.PI / 180);
1544                        xDist = xDist;
1545                        yDist = yDist;
1546                        //lighten the next shadow a bit take 10%
1547                        rColor = Math.floor(rColor / 90 * 100);
1548                        gColor = Math.floor(gColor / 90 * 100);
1549                        bColor = Math.floor(bColor / 90 * 100);
1550                        var opacity = options.opacity;
1551                        if (!opacity) opacity = 0.3;
1552                        color = 'rgba(' + rColor + ', ' + gColor + ', ' + bColor + ', ' + opacity + ')';
1553                        //put together shadow setting
1554                        effect.calculated += xDist.toFixed(2) + 'px ' + yDist.toFixed(2) + 'px ' + ((j + 1) * 2) + 'px ' + color + ', ';
1555                    }
1556                    break;
1557                }
1558            }
1559            // filter result
1560            effect.calculated = effect.calculated.replace(/, +$/, '');
1561            //apply text shadow
1562            self.updateTextShadowEffects(options.target);
1563        };
1564        /*
1565         * Remove shadow effect
1566         *
1567         * @param {type} subject
1568         */
1569        self.removeShadowEffect = function (effectId) {
1570            // delete from shadow effects
1571            delete self.currentPreset.shadowEffects[effectId];
1572            // update shadow effects on given element
1573            self.updateTextShadowEffects(self.currentElement);
1574        };
1575        //update effects
1576        self.updateTextShadowEffects = function (subject) {
1577            var allShadows = '';
1578            // go through all currently applied 
1579            for (var i = 0; i < self.currentPreset.effects.length; i++) {
1580                var effect = self.currentPreset.effects[i];
1581                if (effect.calculated && effect.enabled) {
1582                    if (allShadows) allShadows += ', ';
1583                    allShadows += effect.calculated;
1584                }
1585            }
1586            if (allShadows === '') {
1587                allShadows = 'none';
1588            }
1589            //get all different text shadows
1590            //subject[0].style.textShadow = allShadows + ' !important';
1591            subject.each(function () {
1592                $(this).style('textShadow', allShadows, 'important');
1593            });
1594            self.updateCssSettings('text-shadow', allShadows);
1595        };
1596        /*
1597         *
1598         * PRESETS MANAGEMENT
1599         *
1600         */
1601        /*
1602         *
1603         * Create a new preset - selection
1604         *
1605         */
1606        self.createPreset = function (presetName, selector) {
1607            var property,
1608                newPreset,
1609                newPresetId;
1610            if (selector === undefined || !selector) {
1611                selector = 'PICK AN ELEMENT NOW - or type CSS selector(advanced)';
1612            }
1613            //get first preset as a template
1614            newPreset = {};
1615            //set preset variables
1616            newPreset.name = presetName;
1617            newPreset.selector = selector;
1618            newPreset.fontid = '';
1619            newPreset.fontName = '';
1620            newPreset.effects = [];
1621            // !!! when styles are array they are not visible for PHP?
1622            newPreset.styles = {};
1623            //reset css styles
1624            for (property in newPreset.styles) {
1625                newPreset.styles[property] = '';
1626            }
1627            //push the preset at the end
1628            newPresetId = self.options.presets.push(newPreset) - 1;
1629            //add preset to the dropdown and select it
1630            self.$presetsDropdown.append('<option value="' + newPresetId + '">' + presetName + '</option>');
1631            //get preset id and load it
1632            self.loadPreset(newPresetId);
1633        };
1634        //add new preset
1635        self.renamePreset = function () {
1636            //current preset option
1637            var currPresetOption = self.$presetsDropdown.find('option[value=' + self.currentPresetNo + ']'),
1638                presetName = prompt('New preset name i.e. paragraph', currPresetOption.text()),
1639                property;
1640            if (!presetName) {
1641                return;
1642            }
1643            self.$presetsDropdown.find('option[value=' + self.currentPresetNo + ']').text(presetName);
1644            self.currentPreset.name = presetName;
1645        };
1646        //delete preset
1647        self.deletePreset = function (presetNo, silent) {
1648            if(silent === undefined) {
1649                if (!confirm("Are you sure you want to delete this preset?")) {
1650                    return false;
1651                }
1652            }
1653            if (self.options.presets.length === 1) {
1654                alert('At least one preset has to be present');
1655                return;
1656            }
1657            if(silent === undefined) {
1658                self.unhighlightCurrent();
1659            }
1660            self.options.presets.splice(presetNo, 1);
1661            self.$presetsDropdown.find('option[value=' + presetNo + ']').remove();
1662            // delete all presets
1663            self.$presetsDropdown.html('');
1664            // recreate presets menu to number values from 0
1665            for (var i = 0; i < self.options.presets.length; i += 1) {
1666                var preset = self.options.presets[i],
1667                    presetName = self.options.presets[i].name,
1668                    presetId = i;
1669                // unwire all outlined elements
1670                if (preset.selector.indexOf('ELEMENT NOW') === -1 && preset.selector.indexOf('PICK ELEMENT') === -1) {
1671                    $(preset.selector).data('wired', false);
1672                }
1673                self.$presetsDropdown.append('<option value="' + presetId + '">' + presetName + '</option>');
1674            }
1675            if(silent === undefined) {
1676                self.outlineAllPresets();
1677                alert('When resetting settings or deleting preset you have to save to see the change');
1678                self.loadPreset(0);
1679            } else if (self.options.presets && self.options.presets[1]) {
1680                self.loadPreset(1);
1681            } else {
1682                self.loadPreset(0);
1683            }
1684        };
1685        //delete current preset
1686        self.deleteCurrentPreset = function () {
1687            self.deletePreset(self.currentPresetNo);
1688        };
1689        /*
1690         * Reload currently loaded preset
1691         */
1692        self.reloadPreset = function () {
1693            self.loadPreset(self.currentPresetNo);
1694        };
1695        /*
1696         * load preset - load selection with all param values
1697         *
1698         * @param {type} presetNo
1699         * @returns {unresolved}
1700         */
1701        self.loadPreset = function (presetNo) {
1702            var preset;
1703            presetNo = parseInt(presetNo, 10);
1704            var preset = self.options.presets[presetNo];
1705
1706            self.unloadPreset();
1707            if (!preset) {
1708                return;
1709            }
1710            if (self.mode === 'postPage') return;
1711            // if this is preset 0 hide appearance panel
1712            if (presetNo === 0) {
1713                self.$apr.hide();
1714            } else {
1715                self.$apr.show();
1716            }
1717            // if only one preset exists hide appearance panel
1718            if (self.options.presets.length == 1) {
1719                self.settingFields.presets.hide();
1720                self.$selectorPicker2.hide();
1721            } else {
1722                self.settingFields.presets.show();
1723                self.$selectorPicker2.show();
1724            }
1725            // unload effects
1726            self.unloadCurrentElementEffects();
1727            self.currentPresetNo = presetNo;
1728            self.currentPreset = preset;
1729            // if current selector is invalid current element is false
1730            if (self.currentPreset.selector.indexOf('ELEMENT NOW') !== -1 || self.currentPreset.selector.indexOf('PICK ELEMENT') !== -1) {
1731                self.currentElement = false;
1732            } else {
1733                // otherwise get current element
1734                self.currentElement = $(self.currentPreset.selector);
1735            }
1736            // add space for shadow effects
1737            self.currentPreset.shadowEffects = [];
1738            //select preset from dropdown
1739            self.$presetsDropdown.val(presetNo);
1740            self.populateStyles(preset);
1741            self.highlightCurrent();
1742            //load effects
1743            self.loadEffectSettings();
1744            self.loadFontPreview();
1745        };
1746        self.populateStyles = function (preset) {
1747            var property,
1748                found;
1749            /*
1750            populate general fields
1751            */
1752            for (property in preset) {
1753                found = $('body').find('input[title=' + property + ']');
1754                if (found.length) {
1755                    found.val(preset[property]);
1756                    found.trigger('change');
1757                }
1758            }
1759            /*
1760            populate css fields
1761            */
1762            for (property in preset.styles) {
1763                found = $('body').find('input[title=' + property + ']');
1764                if (found.length) {
1765                    found.val(preset.styles[property]);
1766                    found.trigger('change');
1767                }
1768            }
1769        };
1770        self.unloadPreset = function () {
1771            var $field,
1772                $input,
1773                $dropdown,
1774                key;
1775            // go through all fields
1776            for (key in self.settingFields) {
1777                $dropdown = [];
1778                $field = self.settingFields[key];
1779                if (!$field || !($field instanceof $)) {
1780                    continue;
1781                }
1782                $input = $field.find('input');
1783                $input.val('');
1784                //$input.trigger('change');
1785                $dropdown = $field.find('select');
1786                if ($dropdown.length) {
1787                    $dropdown.val('');
1788                    $input.hide();
1789                }
1790            }
1791        };
1792        self.loadFontPreview = function () {
1793            var preset = self.currentPreset,
1794                imageUrl = '',
1795                originalBg = $('#fontfaceSettings').data('originalBackground');
1796            if (preset['fontid'] && preset['fontName']) {
1797                if (!originalBg) {
1798                    $('#fontfaceSettings').data('originalBackground', $('#fontfaceSettings').css('backgroundImage'));
1799                }
1800                imageUrl = self.options.FFW_baseUrl + '/public/fonts/' + preset['fontid'] + '/' + preset['fontName'] + '.png';
1801                $('#fontfaceSettings').css({'backgroundImage': 'url(' + imageUrl + ')'});
1802            } else if (originalBg) {
1803                $('#fontfaceSettings').css({'backgroundImage': $('#fontfaceSettings').data('originalBackground')});
1804            }
1805        }
1806        /**
1807         *
1808         *  FONTS BROWSER
1809         *
1810         */
1811        /*
1812        Create HTML markup
1813        */
1814        self.createFontPanel = function () {
1815            var i;
1816            var iframe = '<iframe style="width:98%; height:200px; border:none; margin: 10px 1%" frameBorder="0" src="http://fontsforweb.com/user/pluginactivate?apikey=' + self.options.apikey + '&blogurl=' + fontBlogUrl + '"></iframe>';
1817            
1818            //create html within font browser
1819            self.$el.show();
1820            self.$el.append($('<h1 class="draggableModalBar"><a class="closeModal" href="#">x</a></h1>'));
1821            self.$el.append('<div class="tablinks"><a class="tablink" href="fontslist">Fontsforweb.com</a><a class="tablink" href="uploaded">Upload</a><a class="tablink" href="pro">PRO settings</a></div>');
1822            self.$el.append('<div class="tab" id="fontslist"></div>');
1823            self.$el.append('<div class="tab" id="uploaded"><div class="loading"></div></div>');
1824            self.$el.append('<div class="tab" id="pro">' + iframe + '</div>');
1825            
1826            self.$el.find('.tab').hide();
1827            self.$el.find('.tab').eq(0).show();
1828            self.$el.find('.tablink').on('click', function (e) {
1829                var target,
1830                    tab;
1831                e.preventDefault();
1832                self.$el.find('.tab').hide();
1833                target = $(this).attr('href');
1834                tab = self.$el.find('#' + target).show();
1835                if (target === 'uploaded') {
1836                    self.loadPrivateFonts();
1837                }
1838                return false;
1839            });
1840            self.$el.css('left', '0')
1841                .draggable({
1842                    handle: self.$el.find('.draggableModalBar')
1843                });
1844            $('body').on('mousedown', '.draggableModal', function () {
1845                self.modalToTop(this);
1846            });
1847            self.$el.find('a.closeModal').on('click', function () {
1848                self.$el.hide();
1849                return false;
1850            });
1851        };
1852        /*
1853         * Show fonts browser
1854         */
1855        self.showFontsBrowser = function () {
1856            if (!this.fontInitialized) {
1857                self.createFontPanel();
1858                self.loadFontCategories();
1859                this.fontInitialized = true;
1860            } else {
1861                self.$el.toggle();
1862            }
1863            self.modalToTop(self.$el);
1864        };
1865        /*
1866         *  Init fonts browser carousel
1867         */
1868        self.initCarousel = function () {
1869            var carousel = self.$el.find('#FFW_browser_carousel').fcarousel({
1870                buttonNextHTML: '<a href="#" onclick="return false;"></a>',
1871                buttonPrevHTML: '<a href="#" onclick="return false;"></a>',
1872                animation: 1000,
1873                scroll: 2
1874            });
1875        };
1876        /*
1877         *  Load font categories
1878         */
1879        self.loadFontCategories = function () {
1880            //make ajax request to get categories
1881            self.xhrPost({
1882                    url: self.options.FFW_baseUrl + '/fontcategories/fontplugininit',
1883                    data: {
1884                        apikey: self.options.apikey,
1885                        blogurl: fontBlogUrl,
1886                        ver: self.version
1887                    },
1888                    format: 'html'
1889                },
1890                function (data) {
1891                    //if empty answer display error
1892                    if (!data || data === '') {
1893                        self.$el.html('<h1>An error has occurde</h1><p>Please try again later</p>');
1894                    }
1895                    //hide loader
1896                    self.$el.find('#loading').remove();
1897                    //show fonts list
1898                    self.$el.find('#fontslist').html(data);
1899                    //bind close to close button
1900                    self.$el.find('a.close_link').on('click', function () {
1901                        self.$el.toggle();
1902                    });
1903                    //init carousel
1904                    self.initCarousel();
1905                });
1906        };
1907        /*
1908         * bind onclick to links to reveal subcategories
1909         */
1910        self.bindShowCategoriesAction = function () {
1911            $('body').on('click', self.baseSelector + ' #categoriesList > ul li a.categoryChoose', function () {
1912                var categoryId = $(this).attr('name');
1913                //hide all subcategories of other parents
1914                self.$el.find('#subcategoriesList li').hide();
1915                self.$el.find('#subcategoriesList li.instructions').show();
1916                //show all subcategories of this parent
1917                self.$el.find('#subcategoriesList li#FFW_parentcategory_' + categoryId).show();
1918                self.$el.find('.fcarousel-next').click();
1919                return false;
1920            });
1921        };
1922        //bind onclick to reveal font of category
1923        self.bindShowFontsAction = function () {
1924            //bind onclick subcategories to load their fonts
1925            $('body').on('click', self.baseSelector + ' #subcategoriesList > ul li a.categoryChoose', function () {
1926                var categoryId = $(this).attr('name');
1927                self.xhrPost({
1928                        url: self.options.FFW_baseUrl + '/fontcategories/wpfontsforwebcategoryfonts/catid/' + categoryId,
1929                        data: {
1930                            apikey: self.options.apikey,
1931                            blogurl: fontBlogUrl
1932                        },
1933                        format: 'html'
1934                    },
1935                    function (data) {
1936                        //if empty answer display error
1937                        if (!data || data === '') {
1938                            self.$el.html('<h1>An error has occurde</h1><p>Please reload page and try again later</p>');
1939                        }
1940                        //apply content to div
1941                        self.$el.find('#fontList').html(data);
1942                        //move carousel right
1943                        self.$el.find('.fcarousel-next').click();
1944                    });
1945                return false;
1946            });
1947            //bind delete function
1948            $('body').on('click', self.baseSelector + ' #uploaded a.delete', function () {
1949                if (!confirm("Are you sure you want to delete this font?")) {
1950                    return false;
1951                }
1952                var fontId = $(this).attr('name');
1953                self.xhrPost({
1954                    url: self.options.FFW_baseUrl + '/api',
1955                    data: {
1956                        action: 'deletefont',
1957                        apikey: self.options.apikey,
1958                        blogurl: fontBlogUrl,
1959                        fontid: fontId
1960                    }
1961                }, function (data) {
1962                    if (data.success === 'true') {
1963                        self.loadPrivateFonts();
1964                    } else {
1965                        alert('Font deleting error.');
1966                        $('.fontUploadForm').show();
1967                        $('.fontUploading').hide();
1968                    }
1969                });
1970                return false;
1971            });
1972        };
1973        /*
1974         * when clicked on font in fonts browser
1975         */
1976        self.setFontOnclick = function () {
1977            var selector;
1978            //bind onclick font change action to font images
1979            $('body').on('click', self.baseSelector + ' #fontList a.font_pick, ' + self.baseSelector + ' .fontsList a.font_pick', function () {
1980                var element,
1981                    fontName = $(this).parent().attr('title'),
1982                    selector;
1983                //PAGE POST version
1984                if (self.mode === 'postPage') { //it's single post editing page
1985                    //set font to id from name attribute of a
1986                    self.setTinyMCEFont($(this).attr('name'), fontName);
1987                    return false;
1988                } else { //GENERAL VERSION
1989                    //get selector
1990                    selector = self.currentPreset.selector;
1991                    //exit when default selector
1992                    if (selector.indexOf('ELEMENT NOW') != -1 || selector.indexOf('PICK ELEMENT') != -1) {
1993                        return;
1994                    }
1995                    //set form fields
1996                    self.currentPreset.fontid = $(this).attr('name');
1997                    self.currentPreset.fontName = fontName;
1998                    //get target element from iframe or current page
1999                    if ($("#header1preview").length) {
2000                        element = $("#header1preview").contents().find();
2001                        self.applyFont(element);
2002                    } else {
2003                        self.applyFont();
2004                    }
2005                    self.loadFontPreview();
2006                    return false;
2007                }
2008            });
2009        };
2010        /**
2011        *   apply font to selection
2012        **/
2013        self.applyFont = function (frame) {
2014            //get selector
2015            var element,
2016                elements,
2017                fontName = self.currentPreset.fontName,
2018                selector = self.currentPreset.selector,
2019                head,
2020                linkElement;
2021
2022            if(frame === undefined) {
2023                element = $(document);
2024                head = document.getElementsByTagName('head')[0];
2025            } else {
2026                element = frame;
2027                head = element[0].getElementsByTagName('head')[0];
2028            }
2029
2030            linkElement = $(document.createElement('link'));
2031            //get and add stylesheet
2032            linkElement.attr({
2033                href: self.options.FFW_baseUrl + '/font/generatepreviewcss/?id=' + self.currentPreset.fontid,
2034                rel: 'stylesheet',
2035                type: 'text/css'
2036            });
2037            linkElement.appendTo(head);
2038            //set font family to a selector
2039            elements = element.find(selector);
2040            elements.each(function () {
2041                $(this).style("font-family", fontName, 'important');
2042            });
2043        }
2044        /* 
2045         * Load uploaded fonts
2046         *
2047         */
2048        self.loadPrivateFonts = function () {
2049            var apikey = self.options.apikey || false;
2050            //load private fonts
2051            self.xhrPost({
2052                url: self.options.FFW_baseUrl + '/font/getuserfonts',
2053                data: {
2054                    apikey: apikey,
2055                    blogurl: fontBlogUrl,
2056                    blogname: fontBlogName
2057                },
2058                format: 'html'
2059            }, function (data) {
2060                self.$el.find('#uploaded').html(data);
2061            });
2062        };
2063        /*
2064         * Initialize font upload form
2065         */
2066        self.initUploadForm = function () {
2067            $('body').on('submit', self.baseSelector + ' #fontUpload', function () {
2068                self.ajaxFontUpload.start();
2069            });
2070        };
2071        /* 
2072         * Upload font using ajax
2073         *
2074         */
2075        self.ajaxFontUpload = (function () {
2076            return {
2077                start: function () {
2078                    $('.fontUploadForm').hide();
2079                    $('.fontUploading').show();
2080                    $('#fontUploadIframe').load(function () {
2081                        self.loadPrivateFonts();
2082                        //get info about the upload
2083                        /*$.getJSON(self.options.FFW_baseUrl + '/font/wpaddsummary', function (data) {
2084                        if (data.success === 'true') {
2085                        self.loadPrivateFonts();
2086                        } else {
2087                        alert('Font upload error. Check if file is not blocked against embedding.');
2088                        $('.fontUploadForm').show();
2089                        $('.fontUploading').hide();
2090                        }
2091                        });*/
2092                    });
2093                }
2094            };
2095        }());
2096        /*
2097         *
2098         * ELEMENT SELECTION AND CSS RULE GENERATION
2099         *
2100         */
2101        /* 
2102         * Enter element selection mode
2103         *
2104         * @param {type} add
2105         * @returns {unresolved}
2106         */
2107        self.bindGetSelector = function (add) {
2108            //get selector
2109            var selector = self.currentPreset.selector;
2110            //add cancel button
2111            if (!self.$cancelSelecting) {
2112                self.$cancelSelecting = $('<a href="#" class="cancelSelecting">Stop selecting</a>');
2113                $('body').append(self.$cancelSelecting);
2114                self.$cancelSelecting.data('cancelSelecting', true);
2115                //hide other elements
2116                self.$presets.hide();
2117            } else {
2118                self.$cancelSelecting.show();
2119                //hide other elements
2120                self.$presets.hide();
2121            }
2122            $('a, button').on('click.selectionMode', function (e) {
2123                e.preventDefault();
2124                self.handleSelectionClick($(this), add);
2125                return false;
2126            });
2127            $('body').on('click.selectionMode', '*', function (e) {
2128                e.preventDefault();
2129                self.handleSelectionClick($(this), add);
2130                return false;
2131            });
2132            $('body').on('mouseover', '*', function () {
2133                if ($(this).data('cancelSelecting')) {
2134                    return;
2135                }
2136                self.hoverSelector = self.getSelector(this);
2137                self.hoverHighlightCurrent();
2138                $(this).css('outline', '3px dashed rgba(50,50,250,0.7)');
2139                (function () {
2140                    var element = $(this);
2141                    setTimeout(function () {
2142                        element.css('outline', 'none');
2143                    }, 100);
2144                }());
2145                return false;
2146            });
2147            $('body').on('mouseout', '*', function () {
2148                $(this).css('outline', 'none');
2149                return false;
2150            });
2151        };
2152        /*
2153        * Enter selection mode
2154        *
2155        */
2156        self.handleSelectionClick = function ($el, add) {
2157            //if this is cancel selecting button
2158            if ($el.hasClass('cancelSelecting')) {
2159                self.stopSelectionMode();
2160                self.reloadPreset();
2161                return;
2162            }
2163            //if the element's preset is already created just pick it up
2164            if ($el.data('wired')) {
2165                self.$cancelSelecting.css('outline', 'none');
2166                self.stopSelectionMode();
2167                self.loadPreset($el.data('presetNo'));
2168                return false;
2169            }
2170            var sel = self.getSelector($el[0]),
2171                selectorInput,
2172                comma;
2173            //return false;
2174            //if not add to current preset mode
2175            if (!add) {
2176                self.stopSelectionMode();
2177                var val = prompt('Name the selection i.e. title, header or paragraph. It\'ll be now listed in the upper left corner.');
2178                //if not ask for new selection name
2179                if (!val) {
2180                    self.bindGetSelector(add);
2181                    return false;
2182                } else {
2183                    //create a new preset of given selector
2184                    self.createPreset(val, sel);
2185                }
2186            }
2187            self.outlineAllPresets();
2188            selectorInput = self.getField('selector');
2189            if (add) {
2190                comma = selectorInput.val() ? ', ' : '';
2191                selectorInput.val(selectorInput.val() + comma + sel);
2192            } else {
2193                selectorInput.val(sel);
2194            }
2195            selectorInput.trigger('change');
2196            self.stopSelectionMode();
2197            self.highlightCurrent();
2198            //load preset again to apply changes to a new element
2199            if (add) {
2200                self.reloadPreset();
2201            }
2202        };
2203        /* 
2204         * Exit element selection mode
2205         *
2206         * @param {type} htmlcol
2207         * @returns {rgb}
2208         */
2209        self.stopSelectionMode = function () {
2210            $('body').off('mouseover', '*');
2211            $('body').off('click.selectionMode', '*');
2212            $('a').off('.selectionMode');
2213            $('button').off('.selectionMode');
2214            $(this).css('outline', 'none');
2215            $('body').off('mouseout', '*');
2216            self.$cancelSelecting.hide();
2217            //hide other elements
2218            self.$presets.show();
2219        };
2220        /* 
2221         * Generate CSS selector for passed element
2222         *
2223         * @param {type} element
2224         * @returns {selector}
2225         */
2226        self.getSelector = function (element) {
2227            var selector = $(element).parents()
2228                .map(function () {
2229                    return this.tagName;
2230                })
2231                .get().reverse().join(" "),
2232                id,
2233                classNames,
2234                selectorsArr,
2235                sel,
2236                parents = [],
2237                selectors = [];
2238            //go through each mapped object and get id class etc
2239            for (var i = 0;
2240                (i < $(element).parents().length && i < 4); i++) {
2241                var $parent = $(element).parents().eq(i),
2242                    parentSelector = $parent[0].nodeName;
2243                //classses only for first 3 parents(now it's reversed)
2244                if (i < 2) {
2245                    //get id
2246                    //if (id = $parent.attr('id')) {
2247                    //    parentSelector += '#' + id;
2248                    //}
2249                    //class names
2250                    classNames = $parent.attr("class");
2251                    if (classNames) {
2252                        parentSelector += "." + $.trim(classNames).replace(/\s/gi, ".");
2253                    }
2254                }
2255                selectors.push(parentSelector);
2256            }
2257            selector = selectors.reverse().join(' ');
2258            //get element tagname
2259            if (selector) {
2260                selector += " " + $(element)[0].nodeName;
2261            }
2262            id = $(element).attr("id");
2263            if (id) {
2264                selector += "#" + id;
2265            }
2266            classNames = $(element).attr("class");
2267            if (classNames) {
2268                selector += "." + $.trim(classNames).replace(/\s/gi, ".");
2269                // remove ui-draggable when re-selecting element with once removed draggability
2270                //ui-draggable
2271            }
2272            return selector;
2273        };
2274        /* 
2275         * Scroll to selected element
2276         *
2277         * @returns {unresolved}
2278         */
2279        self.scrollToSelection = function () {
2280            var selector = self.currentPreset.selector,
2281                container,
2282                found;
2283            container = $('body');
2284            //exit when default selector
2285            if (selector.indexOf('ELEMENT NOW') !== -1 || selector.indexOf('PICK ELEMENT') !== -1) {
2286                return;
2287            }
2288            //get by selector from the iframe
2289            found = container.find(selector);
2290            //scroll to the top when no el selected
2291            if (!found.length) {
2292                return;
2293            }
2294            //animate scroll
2295            $('html, body').animate({
2296                scrollTop: (parseInt(found.offset().top, 10) - 150)
2297            }, 'slow');
2298        };
2299        /*
2300         * Highlight current element on hover
2301         */
2302        self.hoverHighlightCurrent = function () {
2303            var selector = self.hoverSelector;
2304            if (self.tempHightlighted) {
2305                self.tempHightlighted.css('outline', 'none');
2306            }
2307            //exit when default selector
2308            if (selector.indexOf('ELEMENT NOW') !== -1 || selector.indexOf('PICK ELEMENT') !== -1) {
2309                return;
2310            }
2311            self.tempHightlighted = $(selector);
2312            self.tempHightlighted.css('outline', '3px dashed rgba(100,250,100,0.7)');
2313        };
2314        /*
2315         * highlight all elements(with edititng option)
2316         */
2317        self.outlineAllPresets = function () {
2318            //go through all presets
2319            for (var i = 0; i < self.options.presets.length; i++) {
2320                if (parseInt(i, 10) === parseInt(self.currentPresetNo, 10)) continue;
2321                var preset = self.options.presets[i];
2322                var selector = preset.selector;
2323                //exit when default selector
2324                if (selector.indexOf('ELEMENT NOW') != -1 || selector.indexOf('PICK ELEMENT') != -1) {
2325                    continue;
2326                }
2327                var litme = $(selector);
2328                litme.css('outline', '2px dashed rgba(150,150,250,0.3)');
2329                $('.deletePreset').remove();
2330                litme.css("cursor", 'pointer', 'important');
2331                if (litme.data('wired') === true) {
2332                    continue;
2333                }
2334                litme.data('wired', true);
2335                (function () {
2336                    var ind = i;
2337                    litme.data('presetNo', ind);
2338                    litme.click(function (e) {
2339                        // onbeoforeunload helps here
2340                        //e.preventDefault();
2341                        if (parseInt($(this).data('presetNo'), 10) !== parseInt(self.currentPresetNo, 10)) {
2342                            e.preventDefault();
2343                            self.loadPreset(ind);
2344                        }
2345                    });
2346                    litme.hover(
2347                        function () {
2348                            if (parseInt($(this).data('presetNo'), 10) !== parseInt(self.currentPresetNo, 10)) {
2349                                $(this).css('outline', '2px dashed rgba(150,150,250,0.9)');
2350                            }
2351                        },
2352                        function () {
2353                            if (parseInt($(this).data('presetNo'), 10) !== parseInt(self.currentPresetNo, 10)) {
2354                                $(this).css('outline', '2px dashed rgba(150,150,250,0.3)');
2355                            }
2356                        }
2357                    );
2358                }());
2359            }
2360        };
2361        /* 
2362         * highlight current
2363         * @returns {unresolved}
2364         */
2365        self.highlightCurrent = function () {
2366            var selector = self.currentPreset.selector,
2367                btn;
2368            self.outlineAllPresets();
2369            //exit when default selector
2370            if (selector.indexOf('ELEMENT NOW') != -1 || selector.indexOf('PICK ELEMENT') != -1) {
2371                return;
2372            }
2373            self.hightlighted = $(selector);
2374            self.hightlighted.css('outline', '3px dashed rgba(250,50,30,0.7)');
2375            self.hightlighted.style("cursor", 'move', 'important');
2376            if(!self.hightlighted.find('.deletePreset')[0]) {
2377                btn = $('<button class="deletePreset">X</button>')
2378                self.hightlighted.append(btn);
2379                btn.click(function (e) {
2380                    e.preventDefault();
2381                    self.deleteCurrentPreset();
2382                    return false;
2383                });
2384            }
2385            self.makeCurrentDraggable();
2386        };
2387        /*
2388         * Removes highlighting from current element
2389         *
2390         * @returns {unresolved}
2391         */
2392        self.unhighlightCurrent = function () {
2393            var selector = self.currentPreset.selector;
2394            self.outlineAllPresets();
2395            //exit when default selector
2396            if (selector.indexOf('ELEMENT NOW') != -1 || selector.indexOf('PICK ELEMENT') != -1) {
2397                return;
2398            }
2399            self.hightlighted = $(selector);
2400            self.hightlighted.css('outline', 'none');
2401            self.hightlighted.style("cursor", '');
2402            //self.removeCurrentDraggable();
2403            // remove draggable
2404            self.hightlighted.draggable("destroy");
2405        };
2406        /* 
2407         * make current element draggable
2408         */
2409        self.makeCurrentDraggable = function () {
2410            var isMozilla = $.browser && $.browser.mozilla;
2411            self.hightlighted.draggable({
2412                drag: function (event, ui) {
2413                    var top = ui.position.top,
2414                        left = ui.position.left,
2415                        $el = self.hightlighted;
2416                    $el.each(function () {
2417                        //set position to relative
2418                        $(this).style("position", 'relative', 'important');
2419                        //set slider y to dragged value (parseInt(top,10) + parseInt($(window).scrollTop(),10))
2420                        //for firefox only
2421                        if (isMozilla) {
2422                            self.updateCssSettings('top', (parseInt(top, 10) + parseInt($(window).scrollTop(), 10)) + 'px');
2423                        } else {
2424                            self.updateCssSettings('top', top + 'px');
2425                        }
2426                        //set slider x to dragged value
2427                        self.updateCssSettings('left', left + 'px');
2428                        //set position to relative
2429                        self.updateCssSettings('position', 'relative');
2430                    });
2431                    //waiting to set top and left to "!important" after jquery ui draggable
2432                    setTimeout(function () {
2433                        $el.each(function () {
2434                            //for firefox only
2435                            if (isMozilla) {
2436                                $(this).style("top", (parseInt(top, 10) + parseInt($(window).scrollTop(), 10)) + 'px', 'important');
2437                            } else {
2438                                $(this).style("top", top + 'px', 'important');
2439                            }
2440                            //set slider x to dragged value
2441                            $(this).style("left", left + 'px', 'important');
2442                        });
2443                    }, 10);
2444                }
2445            });
2446        };
2447        /**
2448         *
2449         * COLOR CONVERSION FUNCTIONS
2450         *
2451         */
2452        /* 
2453         * Convert from HEX/HTML to rgb object
2454         *
2455         * @param {type} htmlcol
2456         * @returns {rgb}
2457         */
2458        self.html2rgb = function (htmlcol) {
2459            var rgb = new Object();
2460            rgb.r = self.html2dec(htmlcol.substr(0, 2));
2461            rgb.g = self.html2dec(htmlcol.substr(2, 2));
2462            rgb.b = self.html2dec(htmlcol.substr(4, 2));
2463            return rgb;
2464        };
2465        /*
2466         * Parse RGB to HTML to
2467         * @param {type} rgbString
2468         * @returns {@exp;self@call;html2rgb}
2469         */
2470        self.parseRgb = function (rgbString) {
2471            return self.html2rgb(self.rgbString2hex(rgbString));
2472        };
2473        self.rgbString2hex = function (rgbString) {
2474            var parts = rgbString.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
2475            // parts now should be ["rgb(0, 70, 255", "0", "70", "255"]
2476            if (!parts) return '000000';
2477            delete(parts[0]);
2478            for (var i = 1; i <= 3; ++i) {
2479                parts[i] = parseInt(parts[i]).toString(16);
2480                if (parts[i].length == 1) parts[i] = '0' + parts[i];
2481            }
2482            var hexString = parts.join('');
2483            return hexString;
2484        };
2485        /*
2486         * Converts color from HEX/HTML-style (00-FF) to decimal color value (0-255)
2487         */
2488        self.html2dec = function (h) {
2489            return parseInt(h, 16);
2490        };
2491        /*
2492         *
2493         * MODAL WINDOWS AND OVERLAYS
2494         *
2495         */
2496        /*
2497         * Create new modal window
2498         */
2499        self.createNewModal = function (options) {
2500            var modal = $('<div class="draggableModal"><h1 class="draggableModalBar">' + options.title + '<a class="closeModal" href="#">x</a></h1></div>');
2501            modal.css({
2502                left: options.left ? options.left : 'auto',
2503                right: options.right ? options.right : 'auto',
2504                top: options.top,
2505                width: options.width,
2506                position: 'fixed',
2507                zIndex: 1000000
2508            });
2509            if (options.nobar) {
2510                modal.find('h1.draggableModalBar').hide();
2511            }
2512            if (!options.show) {
2513                modal.hide();
2514            }
2515            if (options.id) {
2516                modal.attr('id', options.id);
2517            }
2518            self.modals[options.name] = modal;
2519
2520            //make draggable
2521            modal.draggable({
2522                handle: modal.find('.draggableModalBar'),
2523                start: function () {
2524                    $(this).css('bottom', 'auto');
2525                }
2526            });
2527
2528            modal.find('.closeModal').click(function () {
2529                modal.hide();
2530                return;
2531            });
2532            modal.appendTo($('body'));
2533            //self.modalToTop(modal);
2534            return modal;
2535        };
2536        /* 
2537         * create a popup with content for overlay links
2538         *
2539         * @returns {unresolved}
2540         */
2541        self.bindLinkOverlay = function () {
2542            $('body').on('click', 'a.font_url, a.overlay_url, button.overlay_url', function () {
2543                var modal,
2544                    frameSrc,
2545                    iframe,
2546                    overlay,
2547                    href = $(this).attr('href');
2548                if ($(this).attr('data-upgrade') === 'true') {
2549                    self.setupApikeyCheckingInterval();
2550                }
2551                //create overlay
2552                overlay = $('<div id="memberOverlay"></div>');
2553                overlay.appendTo(document.body);
2554                overlay.css({
2555                    position: 'fixed',
2556                    top: 0,
2557                    left: 0,
2558                    width: '100%',
2559                    height: '100%',
2560                    'background-color': '#333',
2561                    filter: 'alpha(opacity=50)',
2562                    '-moz-opacity': 0.5,
2563                    '-khtml-opacity': 0.5,
2564                    opacity: 0.5,
2565                    'z-index': 200000001
2566                });
2567                modal = $('<div class="draggableModal ui-draggable"><h1 class="draggableModalBar">' + href + ' <a class="closeModal" href="#">x</a></h1></div>');
2568                modal.css({
2569                    position: 'fixed',
2570                    left: '-475px',
2571                    marginLeft: '50%',
2572                    width: '950px',
2573                    top: '40px',
2574                    zIndex: 200000001
2575                });
2576                modal.find('.closeModal').bind('mousedown', function () {
2577                    modal.remove();
2578                    overlay.remove();
2579                    return;
2580                });
2581                frameSrc = href;
2582                iframe = $('<iframe style="width:98%; height:490px; border:none; margin: 10px 1%" frameBorder="0" src="' + frameSrc + '"></iframe>');
2583                iframe.appendTo(modal);
2584                modal.appendTo($('body')).show();
2585                overlay.click(function () {
2586                    modal.remove();
2587                    $(this).remove();
2588                });
2589                return false;
2590            });
2591            return;
2592        };
2593        /*
2594         * adapt plugin looks to different modes - global or post page
2595         */
2596        self.adaptOptionsToMode = function () {
2597            if (self.mode === 'postPage') {
2598                self.settingFields.presets.hide();
2599                self.$presets.hide();
2600                self.$apr.show();
2601                self.settingFields.selector.hide();
2602                $('#fontSaveSettings').hide();
2603                $('#effectsButton').hide();
2604            }
2605        };
2606        /*
2607         * Show hide modal
2608         *
2609         * @param {type} modalName
2610         */
2611        self.toggleModal = function (modalName) {
2612            if (self.modals[modalName] === undefined) {
2613                return;
2614            }
2615            // if this is effects button
2616            if (modalName === 'effects') {
2617                // detect CSS text-shadow support in JavaScript
2618                if (document.createElement("detect").style.textShadow !== "") {
2619                    alert('Sorry, but your browser does not support advanced effects. Please upgrade to Chrome, Firefox or Internet Explorer 10');
2620                    return;
2621                }
2622            }
2623            self.modals[modalName].toggle();
2624            self.modalToTop(self.modals[modalName]);
2625        };
2626        /*
2627         * Put modal on top
2628         *
2629         * @param {type} selectedModal
2630         */
2631        self.modalToTop = function (selectedModal) {
2632            var highestZindex = 1000000;
2633            for (var index in self.modals) {
2634                var modal = self.modals[index],
2635                    modalZindex = modal.css('zIndex');
2636                if (parseInt(modalZindex, 10) > parseInt(highestZindex, 10)) {
2637                    highestZindex = modalZindex;
2638                }
2639            }
2640            $(selectedModal).css({
2641                'zIndex': parseInt(highestZindex, 10) + 1
2642            });
2643        };
2644        /*
2645         *
2646         *  TINY MCE EDITOR
2647         *
2648         */
2649        /*
2650         *    set font for post or page in tinymce
2651         */
2652        self.setTinyMCEFont = function (fontId, fontName) {
2653            //get selection
2654            var selection = tinyMCE.activeEditor.selection.getContent(),
2655                selectorValid,
2656                $selectorContents,
2657                fontClass,
2658                node,
2659                newSpan,
2660                newHTML,
2661                inserted,
2662                $tempDiv,
2663                multipleSelectors;
2664            //only apply if something is selected
2665            if (!selection) {
2666                return;
2667            }
2668            //get node
2669            fontClass = "fontplugin_fontid_" + fontId + "_" + fontName;
2670            node = tinyMCE.activeEditor.selection.getNode();
2671            //selectorValid - validate selector created from selected text. 
2672            //required because for example "." with nothing following breaks function execution
2673            selectorValid = true;
2674            try {
2675                $selectorContents = $(selection);
2676            } catch (e) {
2677                selectorValid = false;
2678            }
2679            if (selectorValid && $selectorContents.length > 1) {
2680                selectorValid = false;
2681                multipleSelectors = true;
2682            }
2683            if (selectorValid && ($.trim($(node).html()) === $.trim(selection) || $.trim($(node).html()) === $.trim($(selection).html()))) {
2684                //console.log('already isolated');
2685                tinyMCE.activeEditor.dom.setAttrib(node, 'class', fontClass);
2686            } else if (multipleSelectors) {
2687                $selectorContents.each(function () {
2688                    $(this).removePrefixedClasses('fontplugin_fontid_');
2689                    $(this).addClass(fontClass);
2690                    
2691                    $(this).attr('class').split(' ');
2692                });
2693                $tempDiv = $('<div>').append($selectorContents);
2694                newHTML = $tempDiv.html();
2695            
2696                inserted = tinyMCE.activeEditor.selection.setContent(newHTML);
2697            } else {
2698                newSpan = $("<span class=\"" + fontClass + "\">" + selection + '</span>');
2699                //get span html together with span itself
2700                newHTML = $('<div>').append(newSpan.clone()).html();
2701                //add font in use
2702                inserted = tinyMCE.activeEditor.selection.setContent(newHTML);
2703                tinyMCE.activeEditor.selection.select(tinyMCE.activeEditor.dom.select('span.' + fontClass)[0]);
2704            }
2705            //loads font face to iframe editor
2706            self.loadFontFace(fontId);
2707        };
2708        /*
2709         * Load font to tinymce iframe
2710         */
2711        self.loadFontFace = function (fontId) {
2712            var head = self.postIframe[0].getElementsByTagName('head')[0],
2713                linkElement = $(document.createElement('link'));
2714            linkElement.attr({
2715                href: self.options.FFW_baseUrl + '/font/generatepreviewcss/?id=' + fontId,
2716                rel: 'stylesheet',
2717                type: 'text/css'
2718            });
2719            linkElement.appendTo(head);
2720        };
2721        /*
2722         *
2723         * UTILITY FUNCTIONS
2724         *
2725         */
2726        /*
2727         * execute cross domain request, JSON response
2728         *
2729         * @param {type} params
2730         * @param {type} callback
2731         * @returns {@exp;$@call;post}
2732         */
2733        self.xhrPost = function (params, callback) {
2734            var data = params;
2735            if (params.format === undefined) {
2736                params.format = 'json';
2737            }
2738            data.action = 'cross_domain_request';
2739            
2740            return $.ajax({
2741                type: "POST",
2742                url: ajaxproxy,
2743                data: data,
2744                success: function (response) {
2745                    if (typeof callback === 'function') {
2746                        callback(response);
2747                    }
2748                },
2749                dataType: params.format,
2750                error: function (xhr, textStatus, errorThrown) {
2751                    alert("An error occurde. This might be a conflict with another plugin. Error message: " + textStatus);
2752                }
2753            });
2754        };
2755        /*
2756         * show loading spinner
2757         */
2758        self.showLoading = function () {
2759            if ($('.fontLoading').length) {
2760                //self.$apr.find('fieldset').hide();
2761                //self.$el.find('> div').hide();
2762                $('.fontLoading').show();
2763                $('.fontLoading').show();
2764            } else {
2765                $('body').append('<div class="fontLoading"></div>');
2766            }
2767        };
2768        /*
2769         * hide loading spinner
2770         */
2771        self.hideLoading = function () {
2772            $('.fontLoading').hide();
2773            $('.fontLoading').hide();
2774        };
2775        /*
2776         * detect if we're on post editing page or on any other page
2777         */
2778        self.detectMode = function () {
2779            var iframe = $('#content_ifr');
2780            if (iframe.length) {
2781                self.mode = 'postPage';
2782                self.postIframe = iframe.contents();
2783                //self.tinyMCE = $(self.postIframe).find('#tinymce');
2784            } else {
2785                self.mode = 'global';
2786            }
2787            // detect CSS text-shadow support in JavaScript
2788            if (document.createElement("detect").style.textShadow === "") {
2789                self.shadowSupport = true;
2790            } else {
2791                self.shadowSupport = false;
2792            }
2793        };
2794        // finally INIT
2795        self.init();
2796    };
2797    /*
2798     * set default options
2799     */
2800    $.fontPlugin.defaultOptions = {
2801        FFW_baseUrl: 'http://fontsforweb.com'
2802    };
2803    /*
2804     * make jquery plugin
2805     */
2806    $.fn.fontPlugin = function (options) {
2807        return this.each(function () {
2808            var fontPlugin = new $.fontPlugin(this, options);
2809        });
2810    };
2811    /*
2812     * HANDLE PLUGIN
2813     *
2814     * options
2815     * startX -vstarting position
2816     * startY -vstarting position
2817     * name - handle name
2818     * label - visible label
2819     *
2820     */
2821    var XYSlider = function (el, options) {
2822        var self = this;
2823        self.$el = $(el);
2824        self.$el.data('XYSlider', self);
2825        // immediate init funciton
2826        self.init = function () {
2827            // get handle element by name
2828            var handleHolder = self.$el.data(options.settingName),
2829                handle = (handleHolder && handleHolder[0]) ? handleHolder.find('.XYSlider') 
2829: false;
2830            // if doesn't exist create the handle
2831            if (!handleHolder || !handleHolder[0]) {
2832                handleHolder = $('<div class="XYSliderHolder"></div>');
2833                handle = $('<div class="XYSlider">' + options.label + '</div>');
2834                handleHolder.append(handle);
2835                self.$el.append(handleHolder);
2836            }
2837            self.$el.data(options.settingName, handleHolder);
2838            // save in object
2839            self.$handleHolder = handleHolder;
2840            self.$handle = handle;
2841            // make handle draggable
2842            handle.each(function () {
2843                // show handle
2844                $(this).parent().show();
2845                // make draggable
2846                $(this).draggable({
2847                    drag: function (event, ui) {
2848                        var top = ui.position.top,
2849                            left = ui.position.left,
2850                            // revert top: more is higher
2851                            y = -top,
2852                            x = options.invertX ? -left : left;
2853                        self.commitDrag(x, y, true);
2854                    }
2855                });
2856            });
2857        };
2858        /*
2859         * Change position of draggable
2860         *
2861         * x - x position
2862         * y - y position
2863         */
2864        self.setPosition = function (x, y) {
2865            var handle = self.$handle;
2866            
2867            if (x !== false) {
2868                self.x = x;
2869            } else if (self.x === undefined) {
2870                x = 0;
2871                self.x = 0;
2872            } else {
2873                x = self.x;
2874            }
2875            if (y !== false) {
2876                y = y;
2877                self.y = y;
2878            } else if (self.y === undefined) {
2879                y = 0;
2880                self.y = 0;
2881            } else {
2882                y = self.y;
2883            }
2884            if (x === false && y === false) {
2885                return;
2886            }
2887            // move x and y - revert top: more is higher
2888            handle.css({
2889                'top': -y + 'px',
2890                'left': (options.invertX ? -x : x) + 'px'
2891            });
2892            // commit drag
2893            self.commitDrag(x, y, true);
2894        };
2895        /*
2896         * Execute callbacks
2897         */
2898        self.commitDrag = function (x, y, stop) {
2899            var rad = Math.atan2(x, y),
2900                deg = rad * (180 / Math.PI),
2901                distance = 0;
2902            if (stop === undefined) {
2903                stop = false;
2904            }
2905            //get angle
2906            if (deg < 0) {
2907                deg = 360 + deg;
2908            }
2909            // round angle to full degrees
2910            deg = Math.round(deg);
2911            // calculate distance
2912            distance = Math.sqrt(x * x + y * y);
2913            // set current values
2914            self.setCurrentValues(x, y, deg, distance);
2915            // call actions
2916            options.actions.call(this, x, y, deg, distance);
2917        };
2918        self.setCurrentValues = function (x, y, deg, distance) {
2919            self.x = x;
2920            self.y = y;
2921            self.deg = deg;
2922            self.distance = distance;
2923            return self;
2924        };
2925        self.getCurrentValues = function () {
2926            return {
2927                'x': self.x,
2928                'y': self.y,
2929                'deg': self.deg,
2930                'distance': self.distance
2931            };
2932        };
2933        self.init();
2934    };
2935    // use prototype
2936    $.fn.XYSlider = function (options) {
2937        return this.each(function () {
2938            //new XYSlider(this, options);
2939            //var pluginName = 'XYSlider';
2940            new XYSlider(this, options);
2941        });
2942    };
2943}(jQuery));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.