UNPKG

apostrophe

Version:

The Apostrophe Content Management System.

194 lines (164 loc) • 5.72 kB
apos.synth = moog({}); _.extend(apos, { modules: {}, handlers: {}, // EVENT HANDLING // // apos.emit(eventName, /* arg1, arg2, arg3... */) // // Emit an Apostrophe event. All handlers that have been set // with apos.on for the same eventName will be invoked. Any additional // arguments are received by the handler functions as arguments. // // CURRENT EVENTS // // 'enhance' is triggered to request progressive enhancement // of form elements newly loaded into the DOM. It is most often // listened for in admin modals. // // 'ready' is triggered when the main content area of the page // has been refreshed. emit: function(eventName /* ,arg1, arg2, arg3... */) { var handlers = apos.handlers[eventName]; if (!handlers) { return; } var args = Array.prototype.slice.call(arguments, 1); var i; for (i = 0; (i < handlers.length); i++) { handlers[i].apply(window, args); } }, // Install an Apostrophe event handler. The handler will be called // when apos.emit is invoked with the same eventName. The handler // will receive any additional arguments passed to apos.emit. on: function(eventName, fn) { apos.handlers[eventName] = (apos.handlers[eventName] || []).concat([ fn ]); }, // Remove an Apostrophe event handler. If fn is not supplied, all // handlers for the given eventName are removed. off: function(eventName, fn) { if (!fn) { delete apos.handlers[eventName]; return; } apos.handlers[eventName] = _.filter(apos.handlers[eventName], function(_fn) { return fn !== _fn; }); }, // When apos.change('foo') is invoked, the following things happen: // // 1. apos.emit('change', 'foo') is invoked. // // 2. The main content area (the data-apos-refreshable div) is // refreshed via an AJAX request, without refreshing the entire page. // // This occurs without regard to the `what` parameter because there are // too many ways that the main content area can be influenced by any // change made via the admin bar. It's simplest to refresh the // entire content zone and still much faster than a page refresh. // // You can also pass a doc object as `what`, in which // case the `emit` call looks like: // // apos.emit('change', what.type, what) change: function(what) { if (typeof (what) === 'object') { apos.emit('change', what.type, what); } else { apos.emit('change', what); } apos.ui.globalBusy(true); $.get(window.location.href, { apos_refresh: apos.utils.generateId() }, function(data) { apos.ui.globalBusy(false); // Make sure we run scripts in the returned HTML $('[data-apos-refreshable]').html($.parseHTML(data, document, true)); }).fail(function() { apos.ui.globalBusy(false); }); }, isDefined: apos.synth.isDefined, define: apos.synth.define, redefine: apos.synth.redefine, create: apos.synth.create, mirror: apos.synth.mirror, // The page is ready for the addition of event handlers and // progressive enhancement of controls. // // Either the entire page is new, or the main content div, // [data-apos-refreshable], has been refreshed. // // $el will be either $('body') or $('[data-apos-refreshable]'), // as appropriate. // // This is not invoked until after DOM Ready and "next tick." pageReady: function($el) { apos.emit("ready"); apos.emit("enhance", $el); }, // Wrapper that invokes pageReady after both DOM Ready and // "next tick" so that code in pushed javascript files has had // ample opportunity to run first. See pageReady pageReadyWhenCalm: function($el) { $(function() { setImmediate(function() { apos.pageReady($el); }); }); }, // Angular-compatible. Send the csrftoken cookie as the X-CSRFToken header. // This works because if we're running via a script tag or iframe, we won't // be able to read the cookie. // // https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection // // We use Angular's cookie name although CSRF is a more common spelling. -Tom csrf: function() { $.ajaxPrefilter(function(options, originalOptions, jqXHR) { var csrfToken; if ((options.type !== 'OPTIONS') && (!options.crossDomain)) { if (!apos.csrfCookieName) { csrfToken = 'csrf-fallback'; } else { csrfToken = $.cookie(apos.csrfCookieName); } jqXHR.setRequestHeader('X-XSRF-TOKEN', csrfToken); } }); }, prefixAjax: function() { // If Apostrophe has a global URL prefix, patch // jQuery's AJAX capabilities to prepend that prefix // to any non-absolute URL if (apos.prefix) { $.ajaxPrefilter(function(options, originalOptions, jqXHR) { if (options.url) { if (!options.url.match(/^[a-zA-Z]+:/)) { options.url = apos.prefix + options.url; } } }); } }, // Create a singleton representing a server-side module and // register it in `apos.modules`. If it has an alias register // it directly on the `apos` object too. createModule: function(type, options, alias) { if (_.has(apos.modules, type)) { // Don't double-create singletons on apos.change(), leads to doubled click events etc. return; } var module = apos.create(type, options); apos.modules[type] = module; if (alias) { apos[alias] = module; } } }); // So that it can be instantiated with options passed from the server apos.define('apostrophe-assets', { construct: function(self, options) { self.options = options; apos.assets = self; } });