Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>In environments that only provide a one-way message passing system, you can always implement "message-with-callback" yourself, by associating an unique ID with every message, and storing the callbacks in a dictionary that maps these IDs to the callbacks.</p> <p>At the end of this answer, I've copy-pasted the Safari-specific storage module from the source code of my cross-browser <a href="https://robwu.nl/lyricshere/" rel="nofollow">Lyrics Here extension</a>, which provides the following API:</p> <ul> <li><code>config.getItem(key, callback)</code></li> <li><code>config.setItem(key, value, callback)</code></li> <li><code>config.removeItem(key, callback)</code></li> <li><code>config.clear(callback)</code></li> </ul> <p>The callback of <code>getItem</code> contains the value associated with the key (if found). The other callbacks receive a boolean that tells whether the operation succeeded or not.</p> <p>The source code is annotated and contains snippets to handle a few edge-cases. Feel free to shoot a few questions at me if there's anything unclear.</p> <h3>config-safari.js (<a href="http://en.wikipedia.org/wiki/Asynchronous_module_definition" rel="nofollow">AMD</a>)</h3> <pre><code>// Adapter for maintaining preferences (Safari 5+) // All methods are ASYNCHRONOUS define('config-safari', function() { var config = {}; var callbacks = {}; ['getItem', 'setItem', 'removeItem', 'clear'].forEach(function(methodName) { config[methodName] = function() { var args = [].slice.call(arguments); var callback = args.pop(); var messageID = Math.random(); callbacks[messageID] = callback; var message = { type: methodName, messageID: messageID, args: args }; safari.self.tab.dispatchMessage('config-request', message); }; }); config.init = function() { if (typeof safari === 'undefined') { // Safari bug: safari is undefined when current context is an iframe // and src="javascript:''" // This error is only expected on YouTube. // config.getItem is triggered in main, so we just redefine // it. Don't overwrite setItem, etc., so that errors are thrown // when these methods are used. config.getItem = function(key, callback){ callback(); }; return; } safari.self.addEventListener('message', function(event) { if (event.name === 'config-reply') { var messageID = event.message.messageID; var callback = callbacks[messageID]; // Check if callback exists. It may not exist when the script is // activated in multiple frames, because every frame receives the message if (callback) { delete callbacks[messageID]; callback(event.message.result); } } }, true); }; return config; }); </code></pre> <p>Fragment of global.html:</p> <pre><code>&lt;script&gt; (function(exports) { var config = {}; config.getItem = function(key, callback) { var result = safari.extension.settings.getItem(key); if (typeof result === 'string') { try { result = JSON.parse(result); } catch (e) { // Extremely unlikely to happen, but don't neglect the possibility console.log('config.getItem error: ' + e); result = undefined; } } callback(result); }; // callback's argument: true on success, false otherwise config.setItem = function(key, value, callback) { var success = false; try { value = JSON.stringify(value); // Safari (5.1.5) does not enforce the database quota, // let's enforce it manually (ok, set the quota per key, since // the performance issue only occur when a specific key has an outrageous high value) // 1 MB should be sufficient. if (value.length &gt; 1e6) { throw new Error('QUOTA_EXCEEDED_ERR: length=' + value.length); } safari.extension.settings.setItem(key, value); success = true; } catch (e) { console.log('config.setItem error: ' + e); } callback(success); }; // callback's argument: true on success, false otherwise config.removeItem = function(key, callback) { safari.extension.settings.removeItem(key); callback(true); }; // callback's argument: true on success, false otherwise config.clear = function(callback) { safari.extension.settings.clear(); callback(true); }; // config's message handler function handleConfigRequest(event) { var args = event.message.args; // Last argument: Always a callback args.push(function(result) { // Note: All of the config methods send a callback only once // Behavior for calling the callback twice is undefined. // Send a reply to trigger the callback at the sender's end event.target.page.dispatchMessage('config-reply', { messageID: event.message.messageID, result: result }); }); config[event.message.type].apply(config, args); } // Export exports.handleConfigRequest = handleConfigRequest; })(window); &lt;/script&gt; &lt;script&gt; safari.application.addEventListener('message', function(event) { switch (event.name) { case 'config-request': handleConfigRequest(event); break; /* ... other things removed ... */ } }, true); &lt;/script&gt; </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload