PageSourceSearch

https://www.thesecondevolution.com/wp-content/plugins/font/js/jquery.fontPlugin.js?pver=7.5

js thesecondevolution.com collected 2026-10-02 13:32:59 UTC 129,814 bytes, 3,002 lines download raw bytes

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

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.