芝麻web文件管理V1.00
编辑当前文件:/home/soundframestudio/www/wp-content/themes/betheme/muffin-options/fields/visual/field_visual.js
(function($) { /* globals _, fieldVisualJS, quicktags, tinymce, wp */ "use strict"; var MfnFieldVisual = (function() { var __shortcodeManager = {}; var __editorSettings = {}; var newEditor = true; function init() { if ( typeof window.wpEditorL10n === "undefined" ) { newEditor = false; } bind(); if (newEditor) { mergeSettings(); } } /** * Bind events */ function bind() { // event fired after popup created, before show $(document).on('mfn:builder:edit', function( $this, modal ) { create( $(modal) ); }); // event fired after popup close, before destroy $(document).on('mfn:builder:close', function( $this, modal ) { destroy( $(modal) ); }); } /** * Merge Settings */ function mergeSettings() { __editorSettings = { tinymce: _.extend( window.wpEditorL10n.tinymce.settings, { menubar: false, statusbar: false, external_plugins: { 'mfnsc': fieldVisualJS.mfnsc, }, toolbar1: "formatselect,bold,italic,bullist,numlist,blockquote,alignleft,aligncenter,alignright,link,wp_more,spellchecker,dfw,wp_adv,mfnsc", toolbar2: "strikethrough,hr,forecolor,pastetext,removeformat,charmap,outdent,indent,undo,redo,wp_help", //tinymce events init_instance_callback: function(editor){ var keyFired = _.debounce(function(e){ __scLinter.methods.changeWatcher(); __scEditor.methods.moveWatcher(e); __scLinter.methods.removeLineNumbers(); }, 150); var removeTooltip = function(){ __scEditor.methods.tooltip.hide() } editor.on('click', keyFired); //tooltip have to be hidden when typing! editor.on('keydown', removeTooltip); }, } ), }; __shortcodeManager = { tinymce: _.extend( wp.oldEditor, { shortcodeManager: { getContent: () => tinymce.activeEditor.dom.doc.body.childNodes, getCursorLocation: () => tinymce.activeEditor.selection.getSel() } } ) } } /** * Create Tiny MCE instance * https://developer.wordpress.org/reference/functions/wp_editor/ */ function create( $modal ) { var $editor = $( 'textarea[data-visual]', $modal ); if( ! $editor.length ){ return false; } try { $editor.attr( 'id', 'mfn-editor' ); // add index when change used inside $.each $('#content-tmce.wp-switch-editor').click(); quicktags({ id: 'mfn-editor' }); if ( newEditor ) { wp.oldEditor.initialize('mfn-editor', __editorSettings); } else { tinymce.execCommand('mceAddEditor', true, 'mfn-editor'); } $('.switch-html', $modal).click(function() { $(this).closest('.wp-editor-wrap').removeClass('tmce-active').addClass('html-active'); }); $('.switch-tmce', $modal).click(function() { $(this).closest('.wp-editor-wrap').removeClass('html-active').addClass('tmce-active'); }); setTimeout(function(){ $('#mfn-editor_ifr').contents().find('body').attr('style','background-color:#F2F6FA'); }, 500); } catch (err) {} } /** * Destroy Tiny MCE instance * Prepare data to save in WP friendly format */ function destroy( $modal ) { var $editor = $('#mfn-editor'); var editorContent = ''; if( ! $editor.length ){ return false; } try { // try/catch: tinymce or quicktags may not exist in WP 5.0+ if ( ! tinymce.getContent ) { tinymce.execCommand( 'mceToggleEditor', false, 'mfn-editor' ); } /* * Do NOT change order of below lines * Get editor content, save it to variable, destroy editor, set textarea content */ editorContent = tinymce.get( 'mfn-editor' ).getContent(); $( '.wp-editor-wrap', $modal ).removeClass( 'html-active' ).addClass( 'tmce-active' ); if ( newEditor ) { wp.oldEditor.remove( 'mfn-editor' ); } else { tinymce.execCommand( 'mceRemoveEditor', false, 'mfn-editor' ); } $editor.val( editorContent ); } catch (err) {} $editor.removeAttr('id').trigger('change'); } let __scLinter = { shortcodes:{ content: ['alert', 'blockquote', 'dropcap', 'highlight', 'tooltip', 'tooltip_image', 'heading', 'google_font', 'alert', 'idea', 'popup', 'code'], noContent: ['button', 'icon_block', 'fancy_link', 'image', 'idea_block', 'progress_icons', 'hr', 'content_link', 'icon_bar', 'divider', 'icon', 'countdown_inline', 'counter_inline', 'sharebox'], inTextarea: [], wholeLines: [], regex: /\[(.*?)?\](?:([^\[]+)?\[\/\])?/, }, methods:{ removeSlash: (name) => _.without(name, '/').join('').toString(), checkIfHasContent: (name) => _.contains(__scLinter.shortcodes.content, __scLinter.methods.removeSlash(name)), parseScFromLines: (line, lineNr) => { //parser, check by letter let shortcode = {line: lineNr, bracket1: undefined, bracket2: undefined, content: '', attributes: []}, bracketOpen = false, spacePressed = false, attributesString = ''; _.each(line, function(letter, pos) { switch(true){ case ('[' === letter && !bracketOpen): bracketOpen = true; shortcode.bracket1 = pos; break; case ('[' === letter && bracketOpen): shortcode.bracket1 = pos; shortcode.content = ''; break; case (' ' === letter && bracketOpen && !spacePressed): spacePressed = true; break; case (spacePressed && letter === ']' && !_.contains( _.flatten([__scLinter.shortcodes.content, __scLinter.shortcodes.noContent]), shortcode.content)): spacePressed = false; bracketOpen = false; shortcode = {...shortcode, bracket1: undefined, bracket2: undefined, content: ''}; attributesString = ''; break; case (' ' === letter && !bracketOpen): break; case ('/' === letter && !spacePressed): case(bracketOpen && !_.contains([ ']', '[', ' '], letter) && !spacePressed): shortcode.content += letter; break; case (']' === letter && _.contains( _.flatten([__scLinter.shortcodes.content, __scLinter.shortcodes.noContent]), shortcode.content)): case ('/' === shortcode.content[0] && _.contains(__scLinter.shortcodes.content, __scLinter.methods.removeSlash(shortcode.content))): case (']' === letter && spacePressed && _.contains(__scLinter.shortcodes.noContent, shortcode.content)): shortcode.attributes = __scLinter.methods.getAttributes(attributesString); shortcode.bracket2 = pos+1; __scLinter.shortcodes.inTextarea.push(shortcode); bracketOpen = false; spacePressed = false; attributesString = ''; shortcode = {...shortcode, bracket1: undefined, bracket2: undefined, content: ''}; break; case (spacePressed && bracketOpen): attributesString += letter; break; default: bracketOpen = false; shortcode = {...shortcode, bracket1: undefined, bracket2: undefined, content: ''}; break; } }); }, getAttributes: (attributesString) => { //for parser (function above), just get the attributes from shortcodes in CM const attributes = []; let attribute = {isOpened: false, name:'', value: ''}, quoteCount = 0; _.each(attributesString, (letter) => { switch(true){ case (!_.contains(['=', ' '], letter)&& !attribute.isOpened): attribute.name += letter; break; case ('=' === letter): attribute.isOpened = true; break; case (attribute.isOpened): if('"' == letter) quoteCount++; if(quoteCount === 2) { attributes.push({name: attribute.name, value: attribute.value }); attribute.isOpened = false; attribute.value = ''; attribute.name = ''; quoteCount = 0; }else if('"' != letter){ attribute.value += letter; } break; } }); return attributes; }, ifShortcodePushToArray: (lineContent, lineNr) => { if(__scLinter.shortcodes.regex.test(lineContent)){ __scLinter.methods.parseScFromLines(lineContent, lineNr); __scLinter.shortcodes.wholeLines.push({...lineContent, line: lineNr}); } }, iterateAndSetLines: () => { __scLinter.shortcodes.inTextarea = []; __scLinter.shortcodes.wholeLines = []; var lines = wp.oldEditor.shortcodeManager.getContent(); var lineNr = 0; lines.forEach(line => { $(line).attr('data-line', lineNr); __scLinter.methods.ifShortcodePushToArray($(line).text(), lineNr); lineNr++; }) }, removeLineNumbers: () => { var lines = wp.oldEditor.shortcodeManager.getContent(); $(lines).removeAttr('data-line'); }, changeWatcher: () => { __scLinter.methods.iterateAndSetLines() }, } }; let __scEditor = { allShortcodesDOM : $('.mfn-isc-builder'), shortcodeParentDOM: $('.mfn-sc-editor'), shortcodeModal: $('.modal-add-shortcode'), shortcode: { focusedBrackets1: {line:0, bracket1: 0, bracket2:0, content: '', attributes:[]}, focusedBrackets2: {line:0, bracket1: 0, bracket2:0, content: '', attributes:[]} }, iframeContent: () => $('#mfn-editor_ifr').contents(), methods:{ tooltip:{ isDisplayed: false, instance: undefined, turnOn: function(){ __scEditor.methods.tooltip.instance = new tinymce.ui.Menu({ items: [{ text: "Edit", onclick: function() { let clickedSc = __scEditor.shortcode.focusedBrackets1.content, scName = clickedSc[0] !== "/" ? clickedSc.charAt(0)+ clickedSc.slice(1) : clickedSc.charAt(1) + clickedSc.slice(2); let shortcodeDOM = $(__scEditor.allShortcodesDOM).find(`.mfn-isc-builder-${scName}`); //Append fields + display modal __scEditor.shortcodeParentDOM.find('.modalbox-content').empty().append( shortcodeDOM.clone(true) ); __scEditor.shortcodeModal.addClass('show'); __scEditor.methods.modal.prepareToEdit(__scEditor.shortcodeParentDOM.find(`.mfn-isc-builder-${scName}`)); } }, { text: "Delete", onclick: function() { __scEditor.methods.replaceShortcode(' '); } }, ], context: "contextmenu", onhide: function() { __scEditor.methods.tooltip.hide(); } }); //Render! console.log($('.mce-tinymce')); __scEditor.methods.tooltip.instance.renderTo($('.mce-tinymce')); __scEditor.methods.tooltip.isDisplayed = true; }, hide: function(){ if(__scEditor.methods.tooltip.isDisplayed){ __scEditor.methods.tooltip.isDisplayed = false; __scEditor.methods.tooltip.instance.remove(); } }, toggle: (e) => { let shortcodeFocused = __scEditor.shortcode.focusedBrackets1; if(!shortcodeFocused){ //remove the event of scroll when not focused $( __scEditor.iframeContent() ).off('scroll'); return; }else if(!__scEditor.methods.tooltip.isDisplayed){ //in case, when sc is not closed or does not have closing tag //do nothing, prevent doing anything! switch(true){ case __scLinter.methods.checkIfHasContent(shortcodeFocused.content) && !__scEditor.shortcode.focusedBrackets2.content: case _.contains(['sharebox'], shortcodeFocused.content): return; } __scEditor.methods.tooltip.turnOn(e.clientX, e.clientY); __scEditor.methods.tooltip.instance.moveTo(e.clientX, e.clientY); //remove tooltip when scrolling $( __scEditor.iframeContent() ).one('scroll', function(){ shortcodeFocused = undefined; __scEditor.methods.tooltip.hide(); }); } }, acceptButtonWatcher: () => { const acceptButton = __scEditor.shortcodeParentDOM.find('.btn-modal-close-sc'); const acceptButtonFooter = $(__scEditor.shortcodeParentDOM).find('.modalbox-footer .btn-modal-close-sc'); acceptButtonFooter.html('Update'); $(acceptButton).one('click', function(e){ try{ //get name, attrs of actual shortcode const shortcodeName = __scEditor.shortcodeParentDOM.find('[data-shortcode]').attr('data-shortcode'); const shortcodeAttributes = __scEditor.shortcodeParentDOM.find('input[data-name], select[data-name], textarea[data-name]'); //for security reasons, prevent making [undefined] if(!shortcodeName) return; __scEditor.methods.modal.createShortcode(shortcodeName, shortcodeAttributes); //fix for iris error __scEditor.shortcodeModal.trigger('click'); setTimeout(function(){ //make it look like creating shortcode by removing html and triggering modal:close __scEditor.shortcodeModal.removeClass('show'); }, 50 ); //important! it prevents further event bubbling. return false; }catch(err){ // } }); } }, modal: { prepareToEdit: (modal) => { let modalInputs = $(modal).find(`select, input, textarea`), shortcodeAttr = _.isEmpty(__scEditor.shortcode.focusedBrackets1.attributes) ? __scEditor.shortcode.focusedBrackets2.attributes : __scEditor.shortcode.focusedBrackets1.attributes; //for each attribute, you have to set the existing value _.each(shortcodeAttr, (attr) => { let modalAttr = $(modalInputs).closest(`[data-name="${attr.name}"]`)[0]; //not existing attrs, must be avoided if(!modalAttr) return; switch(true){ case 'checkbox' === modalAttr.type: const liParent = $(modalAttr).closest('.segmented-options'), newActiveLi = liParent.find(`[value="${attr.value}"]`); //remove default li and attach the one from shortcode liParent.find('.active').removeClass(); newActiveLi.prop('checked', 'checked'); newActiveLi.closest('li').addClass('active'); break; case _.contains(['type', 'icon'], $(modalAttr).attr('data-name')): const parent = $(modalAttr).closest('.browse-icon'); parent.find('i').removeClass().addClass(attr.value); $(modalAttr).val(attr.value).trigger('change'); break; case _.contains(['font_color', 'color', 'background'], $(modalAttr).attr('data-name')): $(modalAttr).closest('.color-picker-group').find('input.has-colorpicker').val(attr.value); //alpha, not visible input $(modalAttr).closest('.color-picker-group').find('input[data-name]').val(attr.value); // just in case fill the visible input too $(modalAttr).closest('.color-picker-group').find('.color-picker-open span').css('background-color', attr.value); //for not-alpha colors break; case _.contains(['image', 'link_image', 'src'], $(modalAttr).attr('data-name')): const parentLocation = $(modalAttr).closest('.browse-image'); parentLocation.removeClass('empty'); parentLocation.find('.selected-image').html(`
`); parentLocation.find('.mfn-form-input').val(attr.value); break; default: if(attr.value){ $(modalAttr).val(attr.value); $(modalAttr).attr('value', attr.value); }else{ // } break; } }); //if shortcode has content if( $(modalInputs).closest('textarea').length ){ let {first, second} = __scEditor.methods.getFromToPos(); let getContent = function(){ let buildedContent = ''; const whichLine = _.find(__scLinter.shortcodes.wholeLines, function(lineObject){ return lineObject.line === first.line; }) for(first.bracket2; first.bracket2 < second.bracket1; first.bracket2++){ buildedContent += whichLine[first.bracket2]; } return buildedContent; } $(modalInputs).closest('textarea').val( getContent() ); } $(document).trigger('mfn:builder:edit', $(modal).closest('.mfn-modal')); __scEditor.methods.tooltip.acceptButtonWatcher(); }, createShortcode: (shortcodeName, shortcodeAttributes) => { //create ready HTML shortcode structure let scPrepareCode, scParam, textareaContent; scPrepareCode = `[${shortcodeName}`; $(shortcodeAttributes).each(function(){ scParam = $(this)[0]; if( (!_.contains(['textarea', 'checkbox'], $(scParam).prop('type')) && $(scParam).val()) && $(scParam).val() !== 0 && $(scParam).val() !== '0'){ //name has the lbracket and rbracket, remove them scPrepareCode += ` ${ $(scParam).attr('data-name') }="${ $(scParam).val() }"`; }else if($(scParam).parents('li').hasClass('active') && $(scParam).prop('type') == 'checkbox'){ scPrepareCode += ` ${ $(scParam).attr('data-name') }="${ $(scParam).val() }"`; }else if($(scParam).prop('type') == 'textarea'){ //Even if the textarea field is empty, assign value for it to close the tag textareaContent = $(scParam).val() ? $(scParam).val() : '\t' ; } }); scPrepareCode += ']'; if(textareaContent){ scPrepareCode += `${textareaContent}[/${shortcodeName}]`; } //update after saving! __scEditor.methods.replaceShortcode(scPrepareCode); }, }, focus: { //highlight focused(cursor above) shortcode brackets1: _ => { __scEditor.methods.getTriggeredShortcode(); __scEditor.shortcode.focusedBrackets2 = {line:0, bracket1: 0, bracket2:0, content: '', attributes:[]}; if(!__scEditor.shortcode.focusedBrackets1) return __scEditor.methods.tooltip.hide(); __scEditor.methods.focus.brackets2(__scEditor.shortcode.focusedBrackets1); }, brackets2: (shortcode) => { //if sc has the content, then find his ending (second bracket) let similarScFound = 0; if(_.contains(__scLinter.shortcodes.content, shortcode.content)){ let similarSc = _.filter(__scLinter.shortcodes.inTextarea, ({content}) => `/${shortcode.content}` === content || shortcode.content === content), focusedScPos = _.indexOf(similarSc, shortcode) + 1, nextShortcodes = _.last(similarSc, similarSc.length - focusedScPos); _.find(nextShortcodes, function(nextShortcode){ if(shortcode.content === nextShortcode.content) similarScFound++; if('/' === nextShortcode.content[0] && similarScFound === 0) return __scEditor.shortcode.focusedBrackets2 = nextShortcode; if('/' === nextShortcode.content[0] ) similarScFound--; }); }else if(shortcode.content[0] === '/'){ let scName = __scLinter.methods.removeSlash(shortcode.content), similarSc = _.filter(__scLinter.shortcodes.inTextarea, ({content}) => content === `/${scName}` || content === scName), focusedScPos = _.indexOf(similarSc, shortcode), nextShortcodes = _.first(similarSc, focusedScPos); _.find(nextShortcodes.reverse(), function(nextShortcode){ if('/' === nextShortcode.content[0]) similarScFound++; if(scName === nextShortcode.content && similarScFound === 0) return __scEditor.shortcode.focusedBrackets2 = nextShortcode; if(scName === nextShortcode.content) similarScFound--; }); } } }, getFromToPos: _ => { /** * set proper position of shortcodes (to know, where it started and ended) */ var first = __scEditor.shortcode.focusedBrackets1, second = __scEditor.shortcode.focusedBrackets2; if(!second.content){ second = first; }else if(second.line < first.line || (second.line === first.line && second.bracket1 < first.bracket1) || (second.line === first.line && second.bracket2 < first.bracket2) ){ second = __scEditor.shortcode.focusedBrackets1; first = __scEditor.shortcode.focusedBrackets2; } return {first, second}; }, getTriggeredShortcode: () => { //get info about focused shortcode let getCursorPos = wp.oldEditor.shortcodeManager.getCursorLocation(); const cursorPos = { x: parseInt( getCursorPos.anchorOffset) , line: parseInt( $(getCursorPos.focusNode.parentNode).attr('data-line') ) }; let shortcode = _.findIndex(__scLinter.shortcodes.inTextarea, function(index){ if(index.bracket1 <= cursorPos.x && index.bracket2 >= cursorPos.x && index.line === cursorPos.line){ return index; } }); __scEditor.shortcode.focusedBrackets1 = __scLinter.shortcodes.inTextarea[shortcode]; return __scLinter.shortcodes.inTextarea[shortcode]; }, replaceShortcode(readyShortcode){ let {first, second} = __scEditor.methods.getFromToPos(); //recog, which line we are going to edit. const whichLine = _.find(__scLinter.shortcodes.wholeLines, function(lineObject){ return lineObject.line === first.line; }) let oldLineContent = Object.values(whichLine); oldLineContent.pop(); oldLineContent = oldLineContent.join(''); //DOM of TinyMCE required for working let lineDOM = $(wp.oldEditor.shortcodeManager.getContent()[whichLine.line]); let lineContent = lineDOM.text(); let lineLength = lineContent.length; //Utils, for debugging const replaceDebug = { shortcodeCreated: readyShortcode, oldLine: lineContent, oldLength: lineLength, newLength: readyShortcode.length, } //Required let buildedContent = ''; let isClosed = first === second ? false : true; let stringLength = replaceDebug.oldLength > replaceDebug.newLength ? lineLength : readyShortcode.length - 1; let x = 0; let flags = { new: { slashNoticed: false, firstOpenBracketPosition: undefined, endingClosingBracketNoticed: false, positionWhereSCEnded: undefined, builded: '', }, old: { slashNoticed: false, firstOpenBracketPosition: undefined, endingClosingBracketNoticed: false, positionWhereSCEnded: undefined, builded: '', isShortcodeNameInserted: false, }, skipShortcode: false, } if(isClosed) { //Recog, if SC is closed type [/] or not /* Getting info for params like, where it ends etc. */ for(x; x <= stringLength; x++){ //new if (readyShortcode[x] === '/') flags.new.slashNoticed = true; if (!flags.new.endingClosingBracketNoticed) flags.new.builded += readyShortcode[x]; if (readyShortcode[x] === ']' && flags.new.slashNoticed) { flags.new.endingClosingBracketNoticed = true; flags.new.slashNoticed = false; } //old if (lineContent[x] === '/') flags.old.slashNoticed = true; if (!flags.old.endingClosingBracketNoticed) flags.old.builded += lineContent[x]; if (lineContent[x] === ']' && flags.old.slashNoticed) { flags.old.endingClosingBracketNoticed = true; flags.old.slashNoticed = false; } } flags.old.firstOpenBracketPosition = first.bracket1; flags.old.positionWhereSCEnded = second.bracket2-1; }else{ for(x; x <= stringLength; x++){ //new if (!flags.new.endingClosingBracketNoticed) flags.new.builded += readyShortcode[x]; if (readyShortcode[x] === ']' && !flags.new.endingClosingBracketNoticed) { flags.new.endingClosingBracketNoticed = true; } //old if (!flags.old.endingClosingBracketNoticed) flags.old.builded += lineContent[x]; if (lineContent[x] === ']' && !flags.old.endingClosingBracketNoticed && !flags.skipShortcode) { flags.old.endingClosingBracketNoticed = true; } } flags.old.firstOpenBracketPosition = second.bracket1; flags.old.positionWhereSCEnded = second.bracket2-1; } /* Prepare proper string */ let stringToArray = lineContent.split(''); let cleanedStringObject = {before:'', after:''}; stringToArray.filter( (item, index) => { if( flags.old.firstOpenBracketPosition > index ){ cleanedStringObject.before += item; }else if(index > flags.old.positionWhereSCEnded) { cleanedStringObject.after += item; } }) //DELETE case if(readyShortcode === ' ') { buildedContent = cleanedStringObject.before+''+cleanedStringObject.after; } else { buildedContent = cleanedStringObject.before+''+flags.new.builded+''+cleanedStringObject.after; } //replace whole html code of actual line (paragraph in tinymce)*/ $(lineDOM).html(buildedContent) }, moveWatcher: (e) => { __scEditor.methods.focus.brackets1(); __scEditor.methods.tooltip.toggle(e); } } }; /** * Return * Method to start the closure */ return { init: init }; })(); /** * $(document).ready * Specify a function to execute when the DOM is fully loaded. */ $(function() { MfnFieldVisual.init(); }); })(jQuery);