learnosity-1.0.x-dev/js/learnosity.init.js

js/learnosity.init.js
/**
 *  Learnosity API initialization.
 *
 *  This only provides basic Learnosity integration. If you want to add your own
 *  behavior you can add your own handler which fires during service
 *  initialization.
 *
 *  Available services:
 *  - authorapi
 *  - items
 *  - reports
 *
 *  Drupal.learnosityHandlers.addHandler('authorapi', {
 *    onAppReady: function (app) {
 *
 *    }
 *  });
 **/
(function ($, window, Drupal, drupalSettings) {

  /**
   * Ajax command to trigger polling learnosity.
   *
   * This is used in conjunction with Drupal event subscribers. If you need
   * to rely on time-sensitive data from learnosity you can return a
   * PollLearnosityCommand to ensure the data is up-to-date.
   *
   * @param {Drupal.Ajax} [ajax]
   *   The ajax object.
   * @param {object} response
   *   Object holding the server response.
   * @param {string} response.value
   *   The value of the element.
   * @param {number} [status]
   *   The HTTP status code.
   */
  Drupal.AjaxCommands.prototype.pollLearnosity = function (ajax, response, status) {
    // Intentionally left blank.
  };

  /**
   * Ajax command to trigger an error.
   *
   * This is used in conjunction with Drupal event subscribers. Use this command
   * if you need want to trigger an error with a Drupal event subscriber return
   * a LearnosityErrorCommand.
   *
   * @param {Drupal.Ajax} [ajax]
   *   The ajax object.
   * @param {object} response
   *   Object holding the server response.
   * @param {string} response.value
   *   The value of the element.
   * @param {number} [status]
   *   The HTTP status code.
   */
  Drupal.AjaxCommands.prototype.learnosityError = function (ajax, response, status) {
    // Intentionally left blank.
  };

  Drupal.behaviors.Learnosity = {
    attach: function (context) {
      // Run this script only once. Drupal behaviors will be reloaded after
      // every ajax request. In this case we don't want to do that.
      once('data-learnosity', '[data-learnosity]', context).forEach(function() {
        let intCount = 0;
        let intLimit = 30;
        let interval = setInterval(function () {
          // If we're over the maximum interval then kill the process.
          if (intCount > intLimit) {
            clearInterval(interval);
          }

          let signedRequest = JSON.parse(drupalSettings.learnosity.signedRequest);
          let service = drupalSettings.learnosity.service;
          // Assigned to its own internal object for security purposes.
          // This object cannot be inspected from the console.
          let LearnosityApp = new Drupal.learnosityApp(signedRequest);

          // If the library is ready then initialize the service and clear the
          // interval.
          if (LearnosityApp.libraryReady(service)) {
            LearnosityApp.initialize(service)
            clearInterval(interval)
          }
          intCount++;
        }, 1000);
      });
    }
  };

  Drupal.learnosityHandlers = {

    handlers: {},

    addHandler: function(service, handler) {
      if (!this.handlers.hasOwnProperty(service)) {
        this.handlers[service] = [];
      }
      this.handlers[service].push(handler);
    },

    getHandler: function(service) {
      return this.handlers[service];
    },

  }

  Drupal.learnosityApp = function (initObj) {

    this.app = {};

    this.initObj = initObj;

  }

  Drupal.learnosityApp.prototype.libraryReady = function (service) {
    let handlers = Drupal.learnosityHandlers.getHandler(service);

    for (var handler in handlers) {
      if (typeof handlers[handler].libReady === 'function') {
        return handlers[handler].libReady();
      }
    }
  }

  Drupal.learnosityApp.prototype.initialize = function (service) {
    this.app = this.init(service);
  }

  Drupal.learnosityApp.prototype.init = function(service) {
    let _this = this;

    let handlers = Drupal.learnosityHandlers.getHandler(service);

    var callbacks = {
      readyListener: function () {
        // Bind learnosity event subscribers.
        _this.bindAjaxEventSubscribers(_this.app, handlers);

        // There might be more than one handler using the same service.
        // In that case loop through each one and execute its hooks.
        for (var handler in handlers) {
          handlers[handler].onAppReady(_this.app);
        }

      },
      errorListener: function (err) {
        for (var handler in handlers) {
          // Allow handlers to respond to errors.
          if (typeof handlers[handler].onError === 'function') {
            handlers[handler].onError(err, _this.app);
          }
        }
      },
      customUnload: function () {
        for (var handler in handlers) {
          if (handlers[handler].hasOwnProperty('beforeunload')) {
            return handlers[handler].beforeunload;
          }
        }

        return false;
      }
    };

    for (var handler in handlers) {
      // If the handler has the init method then call that.
      // Note: This should only happen for the core handlers.
      // TODO: Add validation.
      if (typeof handlers[handler].init === 'function') {
        return handlers[handler].init(this.initObj, callbacks);
      }
    }
  }

  Drupal.learnosityApp.prototype.bindAjaxEventSubscribers = function (app, handlers) {
    var _this = this;

    // Pass the subscribed events.
    // See LearnosityApiEventHandler::getSubscribedEvents().
    let events = drupalSettings.learnosity.events;

    let context = drupalSettings.learnosity.context ?? {};

    // If 'app:ready' has a subscriber then call bindEventSubscriber.
    // This isn't technically a learnosity event so we trigger it ourselves.
    let index = events.indexOf('app:ready');
    if (index !== -1) {
      let eventSubscriber = new Drupal.learnosityEventSubscriber(app, 'app:ready', context, handlers);
      eventSubscriber.bind();

      // Because this isn't actually supported by learnosity we also make sure
      // it's not passed to app.on().
      events.splice(index, 1);
    }

    // Only execute the event if the app supports the "on" method.
    // Note: This is true for the items and authorapi services, reports
    // is not currently supported.
    if (typeof app.on === 'function') {
      $.each(events, function (index, eventName) {
        app.on(eventName, function () {
          let eventSubscriber = new Drupal.learnosityEventSubscriber(app, eventName, context, handlers);
          eventSubscriber.bindWithPolling();
        });
      });
    }
  }

  /**
   * Learnosity event subscriber.
   *
   * Subscribe to an individual event in Drupal.
   */
    Drupal.learnosityEventSubscriber = function (app, eventName, context, handlers) {
      var _this = this;

      this.eventName = eventName;

      this.handlers = handlers;

      this.context = context;

      // Perform the ajax request in an interval. This allows us to perform
      // polling if necessary. Most of the time it will only execute once.
      this.intervalTime = 3000;

      this.pollCount = 0;

      this.pollLimit = 8;

      // Allow handlers to alter the context of the event and add their
      // own values before its sent to the event subscriber.
      for (var handler in _this.handlers) {
        if (typeof handlers[handler].alterEventSubscriberContext === 'function') {
          context = handlers[handler].alterEventSubscriberContext(app, eventName, context);
        }
      }

    }

    Drupal.learnosityEventSubscriber.prototype.createAjaxUrl = function (eventName, context) {
      // Pass the current context data as a query param.
      var query = Object.keys(context).map(key => key + '=' + context[key]).join('&');

      // Reformat the eventName to be URL friendly.
      eventName = eventName.replaceAll(":", '-');

      let url = '/learnosity-api/event/' + eventName;

      // Append any query params. This passes contextual information.
      if (typeof query !== 'undefined') {
        url += '?' + query;
      }

      return url;
    }

    Drupal.learnosityEventSubscriber.prototype.createAjaxObj = function (url) {
      var _this = this;

      var handlers = _this.handlers;

      // By default, we should only poll learnosity once. However, sometimes
      // we'll want to poll learnosity again if we don't get a result.
      let stopPolling = true;

      var ajaxObj = Drupal.ajax({
        url: url,
        element: false,
        progress: {}
      });

      // On success execute any AJAX commands.
      ajaxObj.success = function (response, status) {
        Object.keys(response || {}).forEach(i => {
          if (response[i].command && this.commands[response[i].command]) {
            // If an error was returned then invoke the error handlers and clear
            // the interval.
            if (response[i].command == 'learnosityError') {
              let err = response[i].message;
              console.error(err);
              // Also allow handlers to respond to this error.
              for (var handler in handlers) {
                // Allow handlers to respond to errors.
                // @todo err should be same format for both contexts.
                if (typeof handlers[handler].onError === 'function') {
                  handlers[handler].onError(err, _this.app);
                }
              }
              _this.stopPolling();
            }
            // If no errors detected then continue.
            else {
              // If a pollLearnosity command is found then do not clear the
              // interval.
              if (!_this.atPollLimit() && response[i].command == 'pollLearnosity') {
                stopPolling = false;
              }

              this.commands[response[i].command](this, response[i], status);
            }

            if (stopPolling) {
              _this.stopPolling();
            }
        }
      });
    }

    return ajaxObj;
  }

  Drupal.learnosityEventSubscriber.prototype.atPollLimit = function () {
    return (this.pollCount > this.pollLimit);
  }

  Drupal.learnosityEventSubscriber.prototype.stopPolling = function () {
    clearInterval(this.interval);
  }

  Drupal.learnosityEventSubscriber.prototype.bindWithPolling = function () {

    let _this = this;

    let url = this.createAjaxUrl(this.eventName, this.context);

    this.interval = setInterval(function () {
      let ajaxObj = _this.createAjaxObj(url);
      ajaxObj.execute();
      _this.pollCount++;

      // Don't let the interval execute more than the specified number
      // of times. This is to prevent constant requests to Learnosity.
      // For example, if the service is down or there is an interruption.
      if (_this.atPollLimit()) {
        _this.stopPolling();
      }

    }, _this.intervalTime);
  }

  Drupal.learnosityEventSubscriber.prototype.bind = function () {
    let _this = this;

    let url = this.createAjaxUrl(this.eventName, this.context);
    let ajaxObj = _this.createAjaxObj(url);
    ajaxObj.execute();
  }

  Drupal.learnosityHandlers.addHandler('reports', {

    libReady: function () {
      return (typeof LearnosityReports != 'undefined');
    },

    init: function (initObj, callbacks) {
      return LearnosityReports.init(initObj, callbacks);
    },

    onAppReady: function (app) {

    }

  });

  Drupal.learnosityHandlers.addHandler('items', {

    libReady: function () {
      return (typeof LearnosityItems != 'undefined');
    },

    init: function (initObj, callbacks) {
      return LearnosityItems.init(initObj, callbacks);
    },

    onAppReady: function (app) {

    }
  });

})(jQuery, window, Drupal, drupalSettings);

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc