| /* globals jQuery */ | 
| /** | 
|  * ext-storage.js | 
|  * | 
|  * This extension allows automatic saving of the SVG canvas contents upon | 
|  *  page unload (which can later be automatically retrieved upon future | 
|  *  editor loads). | 
|  * | 
|  *  The functionality was originally part of the SVG Editor, but moved to a | 
|  *  separate extension to make the setting behavior optional, and adapted | 
|  *  to inform the user of its setting of local data. | 
|  * Dependencies: | 
|  * | 
|  * 1. jQuery BBQ (for deparam) | 
|  * @license MIT | 
|  * | 
|  * @copyright 2010 Brett Zamir | 
|  * @todo Revisit on whether to use $.pref over directly setting curConfig in all | 
|  *   extensions for a more public API (not only for extPath and imagePath, | 
|  *   but other currently used config in the extensions) | 
|  * @todo We might provide control of storage settings through the UI besides the | 
|  *   initial (or URL-forced) dialog. * | 
| */ | 
| export default { | 
|   name: 'storage', | 
|   init () { | 
|     const svgEditor = this; | 
|     const $ = jQuery; | 
|     const svgCanvas = svgEditor.canvas; | 
|   | 
|     // We could empty any already-set data for users when they decline storage, | 
|     //  but it would be a risk for users who wanted to store but accidentally | 
|     // said "no"; instead, we'll let those who already set it, delete it themselves; | 
|     // to change, set the "emptyStorageOnDecline" config setting to true | 
|     // in svgedit-config-iife.js/svgedit-config-es.js. | 
|     const { | 
|       emptyStorageOnDecline, | 
|       // When the code in svg-editor.js prevents local storage on load per | 
|       //  user request, we also prevent storing on unload here so as to | 
|       //  avoid third-party sites making XSRF requests or providing links | 
|       // which would cause the user's local storage not to load and then | 
|       // upon page unload (such as the user closing the window), the storage | 
|       //  would thereby be set with an empty value, erasing any of the | 
|       // user's prior work. To change this behavior so that no use of storage | 
|       // or adding of new storage takes place regardless of settings, set | 
|       // the "noStorageOnLoad" config setting to true in svgedit-config-iife.js. | 
|       noStorageOnLoad, | 
|       forceStorage | 
|     } = svgEditor.curConfig; | 
|     const {storage, updateCanvas} = svgEditor; | 
|   | 
|     function replaceStoragePrompt (val) { | 
|       val = val ? 'storagePrompt=' + val : ''; | 
|       const loc = top.location; // Allow this to work with the embedded editor as well | 
|       if (loc.href.includes('storagePrompt=')) { | 
|         loc.href = loc.href.replace(/([&?])storagePrompt=[^&]*(&?)/, function (n0, n1, amp) { | 
|           return (val ? n1 : '') + val + (!val && amp ? n1 : (amp || '')); | 
|         }); | 
|       } else { | 
|         loc.href += (loc.href.includes('?') ? '&' : '?') + val; | 
|       } | 
|     } | 
|     function setSVGContentStorage (val) { | 
|       if (storage) { | 
|         const name = 'svgedit-' + svgEditor.curConfig.canvasName; | 
|         if (!val) { | 
|           storage.removeItem(name); | 
|         } else { | 
|           storage.setItem(name, val); | 
|         } | 
|       } | 
|     } | 
|   | 
|     function expireCookie (cookie) { | 
|       document.cookie = encodeURIComponent(cookie) + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT'; | 
|     } | 
|   | 
|     function removeStoragePrefCookie () { | 
|       expireCookie('store'); | 
|     } | 
|   | 
|     function emptyStorage () { | 
|       setSVGContentStorage(''); | 
|       for (let name in svgEditor.curPrefs) { | 
|         if (svgEditor.curPrefs.hasOwnProperty(name)) { | 
|           name = 'svg-edit-' + name; | 
|           if (storage) { | 
|             storage.removeItem(name); | 
|           } | 
|           expireCookie(name); | 
|         } | 
|       } | 
|     } | 
|   | 
|     // emptyStorage(); | 
|   | 
|     /** | 
|     * Listen for unloading: If and only if opted in by the user, set the content | 
|     *   document and preferences into storage: | 
|     * 1. Prevent save warnings (since we're automatically saving unsaved | 
|     *       content into storage) | 
|     * 2. Use localStorage to set SVG contents (potentially too large to allow in cookies) | 
|     * 3. Use localStorage (where available) or cookies to set preferences. | 
|     * @returns {undefined} | 
|     */ | 
|     function setupBeforeUnloadListener () { | 
|       window.addEventListener('beforeunload', function (e) { | 
|         // Don't save anything unless the user opted in to storage | 
|         if (!document.cookie.match(/(?:^|;\s*)store=(?:prefsAndContent|prefsOnly)/)) { | 
|           return; | 
|         } | 
|         if (document.cookie.match(/(?:^|;\s*)store=prefsAndContent/)) { | 
|           setSVGContentStorage(svgCanvas.getSvgString()); | 
|         } | 
|   | 
|         svgEditor.setConfig({no_save_warning: true}); // No need for explicit saving at all once storage is on | 
|         // svgEditor.showSaveWarning = false; | 
|   | 
|         const {curPrefs} = svgEditor; | 
|   | 
|         for (let key in curPrefs) { | 
|           if (curPrefs.hasOwnProperty(key)) { // It's our own config, so we don't need to iterate up the prototype chain | 
|             let val = curPrefs[key]; | 
|             const store = (val !== undefined); | 
|             key = 'svg-edit-' + key; | 
|             if (!store) { | 
|               continue; | 
|             } | 
|             if (storage) { | 
|               storage.setItem(key, val); | 
|             } else if (window.widget) { | 
|               window.widget.setPreferenceForKey(val, key); | 
|             } else { | 
|               val = encodeURIComponent(val); | 
|               document.cookie = encodeURIComponent(key) + '=' + val + '; expires=Fri, 31 Dec 9999 23:59:59 GMT'; | 
|             } | 
|           } | 
|         } | 
|       }); | 
|     } | 
|   | 
|     let loaded = false; | 
|     return { | 
|       name: 'storage', | 
|       async langReady ({importLocale}) { | 
|         const {storagePrompt} = $.deparam.querystring(true); | 
|   | 
|         const confirmSetStorage = await importLocale(); | 
|         const { | 
|           message, storagePrefsAndContent, storagePrefsOnly, | 
|           storagePrefs, storageNoPrefsOrContent, storageNoPrefs, | 
|           rememberLabel, rememberTooltip | 
|         } = confirmSetStorage; | 
|   | 
|         // No need to run this one-time dialog again just because the user | 
|         //   changes the language | 
|         if (loaded) { | 
|           return; | 
|         } | 
|         loaded = true; | 
|   | 
|         // Note that the following can load even if "noStorageOnLoad" is | 
|         //   set to false; to avoid any chance of storage, avoid this | 
|         //   extension! (and to avoid using any prior storage, set the | 
|         //   config option "noStorageOnLoad" to true). | 
|         if (!forceStorage && ( | 
|           // If the URL has been explicitly set to always prompt the | 
|           //  user (e.g., so one can be pointed to a URL where one | 
|           // can alter one's settings, say to prevent future storage)... | 
|           storagePrompt === true || | 
|           ( | 
|             // ...or...if the URL at least doesn't explicitly prevent a | 
|             //  storage prompt (as we use for users who | 
|             // don't want to set cookies at all but who don't want | 
|             // continual prompts about it)... | 
|             storagePrompt !== false && | 
|             // ...and this user hasn't previously indicated a desire for storage | 
|             !document.cookie.match(/(?:^|;\s*)store=(?:prefsAndContent|prefsOnly)/) | 
|           ) | 
|           // ...then show the storage prompt. | 
|         )) { | 
|           const options = []; | 
|           if (storage) { | 
|             options.unshift( | 
|               {value: 'prefsAndContent', text: storagePrefsAndContent}, | 
|               {value: 'prefsOnly', text: storagePrefsOnly}, | 
|               {value: 'noPrefsOrContent', text: storageNoPrefsOrContent} | 
|             ); | 
|           } else { | 
|             options.unshift( | 
|               {value: 'prefsOnly', text: storagePrefs}, | 
|               {value: 'noPrefsOrContent', text: storageNoPrefs} | 
|             ); | 
|           } | 
|   | 
|           // Hack to temporarily provide a wide and high enough dialog | 
|           const oldContainerWidth = $('#dialog_container')[0].style.width, | 
|             oldContainerMarginLeft = $('#dialog_container')[0].style.marginLeft, | 
|             oldContentHeight = $('#dialog_content')[0].style.height, | 
|             oldContainerHeight = $('#dialog_container')[0].style.height; | 
|           $('#dialog_content')[0].style.height = '120px'; | 
|           $('#dialog_container')[0].style.height = '170px'; | 
|           $('#dialog_container')[0].style.width = '800px'; | 
|           $('#dialog_container')[0].style.marginLeft = '-400px'; | 
|   | 
|           // Open select-with-checkbox dialog | 
|           // From svg-editor.js | 
|           $.select( | 
|             message, | 
|             options, | 
|             function (pref, checked) { | 
|               if (pref && pref !== 'noPrefsOrContent') { | 
|                 // Regardless of whether the user opted | 
|                 // to remember the choice (and move to a URL which won't | 
|                 // ask them again), we have to assume the user | 
|                 // doesn't even want to remember their not wanting | 
|                 // storage, so we don't set the cookie or continue on with | 
|                 //  setting storage on beforeunload | 
|                 document.cookie = 'store=' + encodeURIComponent(pref) + '; expires=Fri, 31 Dec 9999 23:59:59 GMT'; // 'prefsAndContent' | 'prefsOnly' | 
|                 // If the URL was configured to always insist on a prompt, if | 
|                 //    the user does indicate a wish to store their info, we | 
|                 //    don't want ask them again upon page refresh so move | 
|                 //    them instead to a URL which does not always prompt | 
|                 if (storagePrompt === true && checked) { | 
|                   replaceStoragePrompt(); | 
|                   return; | 
|                 } | 
|               } else { // The user does not wish storage (or cancelled, which we treat equivalently) | 
|                 removeStoragePrefCookie(); | 
|                 if (pref && // If the user explicitly expresses wish for no storage | 
|                   emptyStorageOnDecline | 
|                 ) { | 
|                   emptyStorage(); | 
|                 } | 
|                 if (pref && checked) { | 
|                   // Open a URL which won't set storage and won't prompt user about storage | 
|                   replaceStoragePrompt('false'); | 
|                   return; | 
|                 } | 
|               } | 
|   | 
|               // Reset width/height of dialog (e.g., for use by Export) | 
|               $('#dialog_container')[0].style.width = oldContainerWidth; | 
|               $('#dialog_container')[0].style.marginLeft = oldContainerMarginLeft; | 
|               $('#dialog_content')[0].style.height = oldContentHeight; | 
|               $('#dialog_container')[0].style.height = oldContainerHeight; | 
|   | 
|               // It should be enough to (conditionally) add to storage on | 
|               //   beforeunload, but if we wished to update immediately, | 
|               //   we might wish to try setting: | 
|               //       svgEditor.setConfig({noStorageOnLoad: true}); | 
|               //   and then call: | 
|               //       svgEditor.loadContentAndPrefs(); | 
|   | 
|               // We don't check for noStorageOnLoad here because | 
|               //   the prompt gives the user the option to store data | 
|               setupBeforeUnloadListener(); | 
|   | 
|               svgEditor.storagePromptState = 'closed'; | 
|               updateCanvas(true); | 
|             }, | 
|             null, | 
|             null, | 
|             { | 
|               label: rememberLabel, | 
|               checked: true, | 
|               tooltip: rememberTooltip | 
|             } | 
|           ); | 
|           svgEditor.storagePromptState = 'waiting'; | 
|         } else if (!noStorageOnLoad || forceStorage) { | 
|           setupBeforeUnloadListener(); | 
|         } | 
|       } | 
|     }; | 
|   } | 
| }; |