<commit_before>var currentlyFocusedSympalEditor = null;

$(function()
{
  shortcut.add('Ctrl+P', function() {
    $('.sympal_inline_edit_bar_publish a').click();
  });

  // Content publishing fancybox
  $('#sympal_assets #sympal_ask_confirmation input.no').live('click', function() {
    $('#sympal_assets').load($('.toggle_sympal_assets').attr('rel'));
    return false;
  });

  $('#fancy_content #sympal_ask_confirmation input.no').live('click', function() {
    $.fn.fancybox.close();
    return false;
  });

  $('#fancy_content #sympal_ask_confirmation input.yes').live('click', function() {
    $.fn.fancybox.close();
    setTimeout(function() {
      location.href = location.href;
    }, 750);
  });

  $('.sympal_inline_edit_bar_publish a').fancybox({
    'zoomSpeedIn': 300,
    'zoomSpeedOut': 300,
    'overlayShow': true,
    'hideOnContentClick': false,
  });

  // Toggle edit mode
  var editMode = $.cookie('sympal_inline_edit_mode');

  if (editMode == 'true')
  {
    sympalEnableEditMode();
  }

  // When a input element with class="toggle_edit_mode" is clicked toggle edit mode
  $('input.toggle_edit_mode').click(function() {
    sympalToggleEditMode(this);
  });

  // When a span element with class="toggle_edit_mode" is double clicked toggle edit mode
  $('span.toggle_edit_mode').dblclick(function() {
    sympalToggleEditMode(this);
  });

  // Show the change slot type dropdown when class="sympal_change_slot_type" is clicked
  $('.sympal_change_slot_type').click(function() {
    $(this).siblings('.sympal_change_slot_type_dropdown').slideToggle();
  });

  // Update the editor form for this slot when the dropdown is changed
  $('.sympal_change_slot_type_dropdown select').change(function() {
    var select = $(this);
    var form = $(this.form);
    var type = select.val();
    var slotId = form.parents('span').find('.content_slot_id').attr('value');
    var contentId = form.parents('span').find('.content_id').attr('value');

    var url = $('#sympal_base_url').attr('value') + 'change_content_slot_type/' + contentId + '/' + slotId + '/' + type;

    form.parents('.sympal_content_slot').find('.editor .sympal_content_slot_editor_form').load(url, function() {
      select.parents('.sympal_change_slot_type_dropdown').slideToggle()
    });
  });

  // Save content slots
  $('.sympal_save_content_slots').click(function() {
    sympalSaveContentSlots();
  });

  // Preview content slots
  $('.sympal_preview_content_slots').click(function() {
    sympalSaveContentSlots(true);
  });

  // Disable edit mode
  $('.sympal_disable_edit_mode').click(function() {
    sympalDisableEditMode();
  });

  // Keep track of the currently focused editor
  $('.sympal_content_slot .editor input, .sympal_content_slot .editor textarea').focus(function() {
    currentlyFocusedSympalEditor = $(this);
  });

  // Close all dropdowns with class="sympal_close_menu" is clicked
  $('.sympal_close_menu').live('click', function() {
    sympalCloseAllDropdowns();
  });

  $('.sympal_content_slot_error').live('mouseover', function () {
    sympalHighlightContentSlot($(this).attr('rel'));
  });

  $('#sympal_slot_errors #close').live('click', function() {
    $('#sympal_slot_errors_icon').show();
    $('#sympal_slot_errors').slideToggle();
  });

  $('#sympal_slot_errors_icon').live('click', function() {
    $('#sympal_slot_errors').slideToggle();
  });

  // Setup the sympal dropdown menu panels (assets and links)
  sympalSetupDropdownMenus();
});

function sympalToggleEditMode(clicked)
{
  var editMode = $.cookie('sympal_inline_edit_mode');

  if (clicked)
  {
    // Focus on the form element that was clicked to enable inline editing
    $($(clicked).parent().get(0)).find('.editor input, .editor textarea, .editor select:first').focus();
  }

  // If the state is being enabled then lets change the inline edit bar buttons and link
  if (editMode == 'true')
  {
    sympalDisableEditMode();
  } else {
    sympalEnableEditMode();
  }
}

function sympalSaveContentSlots(preview)
{
  $('.sympal_inline_edit_loading').show();

  preview = typeof(preview) != 'undefined' ? preview : false;

  sympalTinyMCETriggerSave();

  var queryString;
  $('.sympal_content_slot form').each(function() {
    var form = $(this);
    var slotId = form.parents('span').find('.content_slot_id').attr('value');
    queryString = queryString + '&' + form.formSerialize() + '&slot_ids[]=' + slotId;
  });

  var url = $('#sympal_save_slots_url').attr('value');
  url = preview ? url + '?preview=1' : url;
  $.post(url, queryString, function(response) {
    // If response doesn't contain !Errors! then we know no errors occurred
    // so we can disable edit mode
    if (response.indexOf('!Errors!') == -1)
    {
      $('#sympal_slot_errors').html('');

      sympalDisableEditMode();
    }

    eval(response);

    $('.sympal_inline_edit_loading').hide();
  });
}

function sympalHighlightContentSlot(id)
{
  $('#sympal_content_slot_' + id + ' input, #sympal_content_slot_' + id + ' textarea').focus();
}

function sympalDisableEditMode()
{
  $('.toggle_edit_mode').show();
  $('.sympal_content_slot .value').show();
  $('.sympal_inline_edit_bar_edit_buttons').hide();
  $('.sympal_content_slot .editor').hide();

  $('#sympal_slot_errors').slideUp();
  $('#sympal_slot_errors_icon').hide();

  $.cookie('sympal_inline_edit_mode', 'false');
  sympalCloseAllDropdowns();
}

function sympalEnableEditMode()
{
  $('.toggle_edit_mode').hide();
  $('.sympal_content_slot .value').hide()
  $('.sympal_inline_edit_bar_edit_buttons').show();
  $('.sympal_content_slot .editor').show();

  if ($('#sympal_slot_errors').html())
  {
    $('#sympal_slot_errors_icon').show();
    $('#sympal_slot_errors').slideDown();
  }

  $.cookie('sympal_inline_edit_mode', 'true');
}

function sympalTinyMCETriggerSave()
{
  tinyMCE.triggerSave();
}

function sympalHandleTinyMCEEvent(e)
{
  if (e.type == 'click')
  {
    currentlyFocusedSympalEditor = $('#' + tinyMCE.activeEditor.id);
  }
}

function sympalInsertIntoCurrentEditor(text)
{
  if (currentlyFocusedSympalEditor === null)
  {
    currentlyFocusedSympalEditor = $('.sympal_content_slot .editor textarea:first');
  }

  if (currentlyFocusedSympalEditor !== null)
  {
    // Check if is tinymce
    if (currentlyFocusedSympalEditor.parents('span').hasClass('sympal_tiny_mce_content_slot_editor_form'))
    {
      tinyMCE.execInstanceCommand(currentlyFocusedSympalEditor.attr('id'), 'mceInsertContent', false, text);
    } else {
      currentlyFocusedSympalEditor.insertAtCaret(text);
    }
  }
}

$.fn.insertAtCaret = function (myValue) {
  return this.each(function() {
    //IE support
    if (document.selection) {
      this.focus();
      sel = document.selection.createRange();
      sel.text = myValue;
      this.focus();
    }
    //MOZILLA/NETSCAPE support
    else if (this.selectionStart || this.selectionStart == '0')
    {
      var startPos = this.selectionStart;
      var endPos = this.selectionEnd;
      var scrollTop = this.scrollTop;
      this.value = this.value.substring(0, startPos) + myValue + this.value.substring(endPos, this.value.length);
      this.focus();
      this.selectionStart = startPos + myValue.length;
      this.selectionEnd = startPos + myValue.length;
      this.scrollTop = scrollTop;
    } else {
      this.value += myValue;
      this.focus();
    }
  });
};

function sympalSetupDropdownMenu(id, control)
{
  $(id).hide();

  $(control).click(function() {
    if ($(this).attr('rel'))
    {
      if (!$(id).html())
      {
        $(id).load($(this).attr('rel'), {
          success: function() {
            $(id).slideToggle('fast');
            sympalCloseAllDropdowns();
          }
        });
      } else {
        $(id).slideToggle('fast');
        sympalCloseAllDropdowns();
      }
    } else {
      $(id).slideToggle('fast');
      sympalCloseAllDropdowns();
    }
  });
}

function sympalCloseAllDropdowns()
{
  $('#sympal_editor').hide();
  $('#sympal_assets').hide();
  $('#sympal_links').hide();
  $('#sympal_objects').hide();
}

function sympalSetupDropdownMenus()
{
  sympalSetupDropdownMenu('#sympal_assets', '.toggle_sympal_assets');
  sympalSetupDropdownMenu('#sympal_links', '.toggle_sympal_links');
  sympalSetupDropdownMenu('#sympal_objects', '.toggle_sympal_objects');
}<commit_msg>[1.4][sfSympalPlugin][1.0] Changing editor save to not disable edit mode when save is complete<commit_after>var currentlyFocusedSympalEditor = null;

$(function()
{
  shortcut.add('Ctrl+P', function() {
    $('.sympal_inline_edit_bar_publish a').click();
  });

  // Content publishing fancybox
  $('#sympal_assets #sympal_ask_confirmation input.no').live('click', function() {
    $('#sympal_assets').load($('.toggle_sympal_assets').attr('rel'));
    return false;
  });

  $('#fancy_content #sympal_ask_confirmation input.no').live('click', function() {
    $.fn.fancybox.close();
    return false;
  });

  $('#fancy_content #sympal_ask_confirmation input.yes').live('click', function() {
    $.fn.fancybox.close();
    setTimeout(function() {
      location.href = location.href;
    }, 750);
  });

  $('.sympal_inline_edit_bar_publish a').fancybox({
    'zoomSpeedIn': 300,
    'zoomSpeedOut': 300,
    'overlayShow': true,
    'hideOnContentClick': false,
  });

  // Toggle edit mode
  var editMode = $.cookie('sympal_inline_edit_mode');

  if (editMode == 'true')
  {
    sympalEnableEditMode();
  }

  // When a input element with class="toggle_edit_mode" is clicked toggle edit mode
  $('input.toggle_edit_mode').click(function() {
    sympalToggleEditMode(this);
  });

  // When a span element with class="toggle_edit_mode" is double clicked toggle edit mode
  $('span.toggle_edit_mode').dblclick(function() {
    sympalToggleEditMode(this);
  });

  // Show the change slot type dropdown when class="sympal_change_slot_type" is clicked
  $('.sympal_change_slot_type').click(function() {
    $(this).siblings('.sympal_change_slot_type_dropdown').slideToggle();
  });

  // Update the editor form for this slot when the dropdown is changed
  $('.sympal_change_slot_type_dropdown select').change(function() {
    var select = $(this);
    var form = $(this.form);
    var type = select.val();
    var slotId = form.parents('span').find('.content_slot_id').attr('value');
    var contentId = form.parents('span').find('.content_id').attr('value');

    var url = $('#sympal_base_url').attr('value') + 'change_content_slot_type/' + contentId + '/' + slotId + '/' + type;

    form.parents('.sympal_content_slot').find('.editor .sympal_content_slot_editor_form').load(url, function() {
      select.parents('.sympal_change_slot_type_dropdown').slideToggle()
    });
  });

  // Save content slots
  $('.sympal_save_content_slots').click(function() {
    sympalSaveContentSlots();
  });

  // Preview content slots
  $('.sympal_preview_content_slots').click(function() {
    sympalSaveContentSlots(true);
  });

  // Disable edit mode
  $('.sympal_disable_edit_mode').click(function() {
    sympalDisableEditMode();
  });

  // Keep track of the currently focused editor
  $('.sympal_content_slot .editor input, .sympal_content_slot .editor textarea').focus(function() {
    currentlyFocusedSympalEditor = $(this);
  });

  // Close all dropdowns with class="sympal_close_menu" is clicked
  $('.sympal_close_menu').live('click', function() {
    sympalCloseAllDropdowns();
  });

  $('.sympal_content_slot_error').live('mouseover', function () {
    sympalHighlightContentSlot($(this).attr('rel'));
  });

  $('#sympal_slot_errors #close').live('click', function() {
    $('#sympal_slot_errors_icon').show();
    $('#sympal_slot_errors').slideToggle();
  });

  $('#sympal_slot_errors_icon').live('click', function() {
    $('#sympal_slot_errors').slideToggle();
  });

  // Setup the sympal dropdown menu panels (assets and links)
  sympalSetupDropdownMenus();
});

function sympalToggleEditMode(clicked)
{
  var editMode = $.cookie('sympal_inline_edit_mode');

  if (clicked)
  {
    // Focus on the form element that was clicked to enable inline editing
    $($(clicked).parent().get(0)).find('.editor input, .editor textarea, .editor select:first').focus();
  }

  // If the state is being enabled then lets change the inline edit bar buttons and link
  if (editMode == 'true')
  {
    sympalDisableEditMode();
  } else {
    sympalEnableEditMode();
  }
}

function sympalSaveContentSlots(preview, disableEditMode)
{
  $('.sympal_inline_edit_loading').show();

  preview = typeof(preview) != 'undefined' ? preview : false;
  disableEditMode = typeof(disableEditMode) != 'undefined' ? disableEditMode : false;
  if (preview)
  {
    disableEditMode = true;
  }

  sympalTinyMCETriggerSave();

  var queryString;
  $('.sympal_content_slot form').each(function() {
    var form = $(this);
    var slotId = form.parents('span').find('.content_slot_id').attr('value');
    queryString = queryString + '&' + form.formSerialize() + '&slot_ids[]=' + slotId;
  });

  var url = $('#sympal_save_slots_url').attr('value');
  url = preview ? url + '?preview=1' : url;
  $.post(url, queryString, function(response) {
    // If response doesn't contain !Errors! then we know no errors occurred
    // so we can disable edit mode
    if (response.indexOf('!Errors!') == -1)
    {
      $('#sympal_slot_errors').html('');

      if (disableEditMode)
      {
        sympalDisableEditMode();
      }
    }

    eval(response);

    $('.sympal_inline_edit_loading').hide();
  });
}

function sympalHighlightContentSlot(id)
{
  $('#sympal_content_slot_' + id + ' input, #sympal_content_slot_' + id + ' textarea').focus();
}

function sympalDisableEditMode()
{
  $('.toggle_edit_mode').show();
  $('.sympal_content_slot .value').show();
  $('.sympal_inline_edit_bar_edit_buttons').hide();
  $('.sympal_content_slot .editor').hide();

  $('#sympal_slot_errors').slideUp();
  $('#sympal_slot_errors_icon').hide();

  $.cookie('sympal_inline_edit_mode', 'false');
  sympalCloseAllDropdowns();
}

function sympalEnableEditMode()
{
  $('.toggle_edit_mode').hide();
  $('.sympal_content_slot .value').hide()
  $('.sympal_inline_edit_bar_edit_buttons').show();
  $('.sympal_content_slot .editor').show();

  if ($('#sympal_slot_errors').html())
  {
    $('#sympal_slot_errors_icon').show();
    $('#sympal_slot_errors').slideDown();
  }

  $.cookie('sympal_inline_edit_mode', 'true');
}

function sympalTinyMCETriggerSave()
{
  tinyMCE.triggerSave();
}

function sympalHandleTinyMCEEvent(e)
{
  if (e.type == 'click')
  {
    currentlyFocusedSympalEditor = $('#' + tinyMCE.activeEditor.id);
  }
}

function sympalInsertIntoCurrentEditor(text)
{
  if (currentlyFocusedSympalEditor === null)
  {
    currentlyFocusedSympalEditor = $('.sympal_content_slot .editor textarea:first');
  }

  if (currentlyFocusedSympalEditor !== null)
  {
    // Check if is tinymce
    if (currentlyFocusedSympalEditor.parents('span').hasClass('sympal_tiny_mce_content_slot_editor_form'))
    {
      tinyMCE.execInstanceCommand(currentlyFocusedSympalEditor.attr('id'), 'mceInsertContent', false, text);
    } else {
      currentlyFocusedSympalEditor.insertAtCaret(text);
    }
  }
}

$.fn.insertAtCaret = function (myValue) {
  return this.each(function() {
    //IE support
    if (document.selection) {
      this.focus();
      sel = document.selection.createRange();
      sel.text = myValue;
      this.focus();
    }
    //MOZILLA/NETSCAPE support
    else if (this.selectionStart || this.selectionStart == '0')
    {
      var startPos = this.selectionStart;
      var endPos = this.selectionEnd;
      var scrollTop = this.scrollTop;
      this.value = this.value.substring(0, startPos) + myValue + this.value.substring(endPos, this.value.length);
      this.focus();
      this.selectionStart = startPos + myValue.length;
      this.selectionEnd = startPos + myValue.length;
      this.scrollTop = scrollTop;
    } else {
      this.value += myValue;
      this.focus();
    }
  });
};

function sympalSetupDropdownMenu(id, control)
{
  $(id).hide();

  $(control).click(function() {
    if ($(this).attr('rel'))
    {
      if (!$(id).html())
      {
        $(id).load($(this).attr('rel'), {
          success: function() {
            $(id).slideToggle('fast');
            sympalCloseAllDropdowns();
          }
        });
      } else {
        $(id).slideToggle('fast');
        sympalCloseAllDropdowns();
      }
    } else {
      $(id).slideToggle('fast');
      sympalCloseAllDropdowns();
    }
  });
}

function sympalCloseAllDropdowns()
{
  $('#sympal_editor').hide();
  $('#sympal_assets').hide();
  $('#sympal_links').hide();
  $('#sympal_objects').hide();
}

function sympalSetupDropdownMenus()
{
  sympalSetupDropdownMenu('#sympal_assets', '.toggle_sympal_assets');
  sympalSetupDropdownMenu('#sympal_links', '.toggle_sympal_links');
  sympalSetupDropdownMenu('#sympal_objects', '.toggle_sympal_objects');
}<|endoftext|>


<commit_before>import loadScript from 'load-script';
import Promise from 'promise';
import ThreeDSecureStrategy from './strategy';
import { Frame } from '../../../frame';

const debug = require('debug')('recurly:risk:three-d-secure:adyen');

export default class AdyenStrategy extends ThreeDSecureStrategy {
  static libUrl = 'https://checkoutshopper-live.adyen.com/checkoutshopper/sdk/2.2.0/adyen.js';
  static strategyName = 'adyen';

  constructor (...args) {
    super(...args);

    if (!this.shouldLoadAdyenLibrary) return;
    debug('loading Adyen library');
    this.loadAdyenLibrary()
      .catch(cause => this.threeDSecure.error('3ds-vendor-load-error', { vendor: 'Adyen', cause }))
      .then(() => {
        this.adyenCheckout = new window.AdyenCheckout();
        debug('Adyen checkout instance created', this.adyenCheckout);
        this.markReady();
      });
  }

  get shouldLoadAdyenLibrary () {
    return !this.shouldFallback;
  }

  get shouldFingerprint () {
    return !!this.adyenFingerprintToken;
  }

  get shouldChallenge () {
    return !!this.adyenChallengeToken;
  }

  get shouldFallback () {
    return !!this.adyenRedirectParams;
  }

  get adyenFingerprintToken () {
    const { authentication } = this.actionToken.three_d_secure.params;
    return authentication && authentication['threeds2.fingerprintToken'];
  }

  get adyenChallengeToken () {
    const { authentication } = this.actionToken.three_d_secure.params;
    return authentication && authentication['threeds2.challengeToken'];
  }

  get adyenRedirectParams () {
    return this.actionToken.three_d_secure.params.redirect;
  }

  /**
   * Provides the target DOM element for which we will apply
   * fingerprint detection, challenge flows, and results
   *
   * @param {HTMLElement} element
   */
  attach (element) {
    super.attach(element);

    const { shouldFallback, shouldFingerprint, shouldChallenge } = this;

    if (shouldFingerprint) {
      this.whenReady(() => this.fingerprint());
    } else if (shouldChallenge) {
      this.whenReady(() => this.challenge());
    } else if (shouldFallback) {
      this.fallback();
    } else {
      const cause = 'We could not determine an authentication method';
      this.threeDSecure.error('3ds-auth-determination-error', { cause });
    }
  }

  /**
   * Removes DOM elements
   */
  remove () {
    const { frame } = this;
    if (frame) frame.destroy();
    super.remove();
  }

  /**
   * Initiates device fingerprinting through AdyenCheckout
   */
  fingerprint () {
    const { adyenCheckout, adyenFingerprintToken, container } = this;

    debug('Initializing fingerprinting with Adyen token', adyenFingerprintToken);

    const fingerprintService = adyenCheckout.create('threeDS2DeviceFingerprint', {
      fingerprintToken: adyenFingerprintToken,
      onComplete: results => this.emit('done', results),
      onError: cause => this.threeDSecure.error('3ds-auth-error', { cause }),
    });

    fingerprintService.mount(container);
  }

  /**
   * Initiates a challenge through AdyenCheckout
   */
  challenge () {
    const { adyenCheckout, adyenChallengeToken, container } = this;

    debug('Initializing challenge with Adyen token', adyenChallengeToken);

    const challengeService = adyenCheckout.create('threeDS2Challenge', {
      challengeToken: adyenChallengeToken,
      onComplete: results => this.emit('done', results),
      onError: cause => this.threeDSecure.error('3ds-auth-error', { cause }),
      size: '05'
    });

    challengeService.mount(container);
  }

  /**
   * Constructs a 3D Secure 1.0 iframe
   *
   * TODO: Move this into a separate strategy, and have this strategy
   *       instruct ThreeDSecureConcern to employ it instead
   */
  fallback () {
    debug('Initiating 3D Secure 1.0 frame');
    const { adyenRedirectParams, container, threeDSecure } = this;
    const { recurly } = threeDSecure.risk;
    const payload = {
      redirect_url: adyenRedirectParams.url,
      ...adyenRedirectParams.data
    };
    this.frame = recurly.Frame({
      type: Frame.TYPES.IFRAME,
      path: '/three_d_secure/start',
      payload,
      container,
      defaultEventName: 'adyen-3ds-challenge'
    })
    .on('error', cause => threeDSecure.error('3ds-auth-error', { cause }))
    .on('done', results => this.emit('done', results));
  }

  /**
   * Loads Adyen library dependency
   */
  loadAdyenLibrary () {
    return new Promise((resolve, reject) => {
      if (window.AdyenCheckout) return resolve();
      loadScript(AdyenStrategy.libUrl, error => {
        if (error) reject(error);
        else resolve();
      });
    });
  }
}
<commit_msg>fix linter errors<commit_after>import loadScript from 'load-script';
import Promise from 'promise';
import ThreeDSecureStrategy from './strategy';
import { Frame } from '../../../frame';

const debug = require('debug')('recurly:risk:three-d-secure:adyen');

export default class AdyenStrategy extends ThreeDSecureStrategy {
  static libUrl = 'https://checkoutshopper-live.adyen.com/checkoutshopper/sdk/2.2.0/adyen.js';
  static strategyName = 'adyen';

  constructor (...args) {
    super(...args);

    if (!this.shouldLoadAdyenLibrary) return;
    debug('loading Adyen library');
    this.loadAdyenLibrary()
      .catch(cause => this.threeDSecure.error('3ds-vendor-load-error', { vendor: 'Adyen', cause }))
      .then(() => {
        this.adyenCheckout = new window.AdyenCheckout();
        debug('Adyen checkout instance created', this.adyenCheckout);
        this.markReady();
      });
  }

  get shouldLoadAdyenLibrary () {
    return !this.shouldFallback;
  }

  get shouldFingerprint () {
    return !!this.adyenFingerprintToken;
  }

  get shouldChallenge () {
    return !!this.adyenChallengeToken;
  }

  get shouldFallback () {
    return !!this.adyenRedirectParams;
  }

  get adyenFingerprintToken () {
    const { authentication } = this.actionToken.three_d_secure.params;
    return authentication && authentication['threeds2.fingerprintToken'];
  }

  get adyenChallengeToken () {
    const { authentication } = this.actionToken.three_d_secure.params;
    return authentication && authentication['threeds2.challengeToken'];
  }

  get adyenRedirectParams () {
    return this.actionToken.three_d_secure.params.redirect;
  }

  /**
   * Provides the target DOM element for which we will apply
   * fingerprint detection, challenge flows, and results
   *
   * @param {HTMLElement} element
   */
  attach (element) {
    super.attach(element);

    const { shouldFallback, shouldFingerprint, shouldChallenge } = this;

    if (shouldFingerprint) {
      this.whenReady(() => this.fingerprint());
    } else if (shouldChallenge) {
      this.whenReady(() => this.challenge());
    } else if (shouldFallback) {
      this.fallback();
    } else {
      const cause = 'We could not determine an authentication method';
      this.threeDSecure.error('3ds-auth-determination-error', { cause });
    }
  }

  /**
   * Removes DOM elements
   */
  remove () {
    const { frame } = this;
    if (frame) frame.destroy();
    super.remove();
  }

  /**
   * Initiates device fingerprinting through AdyenCheckout
   */
  fingerprint () {
    const { adyenCheckout, adyenFingerprintToken, container } = this;

    debug('Initializing fingerprinting with Adyen token', adyenFingerprintToken);

    const fingerprintService = adyenCheckout.create('threeDS2DeviceFingerprint', {
      fingerprintToken: adyenFingerprintToken,
      onComplete: results => this.emit('done', results),
      onError: cause => this.threeDSecure.error('3ds-auth-error', { cause }),
    });

    fingerprintService.mount(container);
  }

  /**
   * Initiates a challenge through AdyenCheckout
   */
  challenge () {
    const { adyenCheckout, adyenChallengeToken, container } = this;

    debug('Initializing challenge with Adyen token', adyenChallengeToken);

    const challengeService = adyenCheckout.create('threeDS2Challenge', {
      challengeToken: adyenChallengeToken,
      onComplete: results => this.emit('done', results),
      onError: cause => this.threeDSecure.error('3ds-auth-error', { cause }),
      size: '05'
    });

    challengeService.mount(container);
  }

  /**
   * Constructs a 3D Secure 1.0 iframe
   *
   * TODO: Move this into a separate strategy, and have this strategy
   *       instruct ThreeDSecureConcern to employ it instead
   */
  fallback () {
    debug('Initiating 3D Secure 1.0 frame');
    const { adyenRedirectParams, container, threeDSecure } = this;
    const { recurly } = threeDSecure.risk;
    const payload = {
      redirect_url: adyenRedirectParams.url,
      ...adyenRedirectParams.data
    };
    this.frame = recurly.Frame({
      type: Frame.TYPES.IFRAME,
      path: '/three_d_secure/start',
      payload,
      container,
      defaultEventName: 'adyen-3ds-challenge'
    })
      .on('error', cause => threeDSecure.error('3ds-auth-error', { cause }))
      .on('done', results => this.emit('done', results));
  }

  /**
   * Loads Adyen library dependency
   */
  loadAdyenLibrary () {
    return new Promise((resolve, reject) => {
      if (window.AdyenCheckout) return resolve();
      loadScript(AdyenStrategy.libUrl, error => {
        if (error) reject(error);
        else resolve();
      });
    });
  }
}
<|endoftext|>


<commit_before>const {app, Menu, BrowserWindow} = require('electron')
const path = require('path')
const url = require('url')

let mainWindow

function createWindow () {
    mainWindow = new BrowserWindow({
        width: 800,
        height: 600,
        tabbingIdentifier: 'flowdock',
        webPreferences: {
            partition: 'persist:xxx'
        }
    })

    mainWindow.loadURL('https://www.flowdock.com/app/mynet')
    mainWindow.on('closed', function () {
        mainWindow = null
    })

    const template = [
        {
            label: 'Edit',
            submenu: [
                { label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" },
                { label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" },
                { type: "separator" },
                { label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" },
                { label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" },
                { label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" },
                { label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:" }
            ]
        },
        {
            label: 'View',
            submenu: [
                {role: 'reload'},
                {role: 'forcereload'},
                {role: 'toggledevtools'},
                {type: 'separator'},
                {role: 'resetzoom'},
                {role: 'zoomin'},
                {role: 'zoomout'},
                {type: 'separator'},
                {role: 'togglefullscreen'}
            ]
        },
        {
            role: 'window',
            submenu: [
                {role: 'minimize'},
                {role: 'close'}
            ]
        }
    ]

    if (process.platform === 'darwin') {
        template.unshift({
            label: app.getName(),
            submenu: [
                {role: 'about'},
                {type: 'separator'},
                {role: 'services', submenu: []},
                {type: 'separator'},
                {role: 'hide'},
                {role: 'hideothers'},
                {role: 'unhide'},
                {type: 'separator'},
                {role: 'quit'}
            ]
        })

        template[3].submenu = [
            {role: 'close'},
            {role: 'minimize'},
            {role: 'zoom'},
            {type: 'separator'},
            {role: 'front'}
        ]
    }

    const menu = Menu.buildFromTemplate(template)
    Menu.setApplicationMenu(menu)
}

app.on('ready', createWindow)
app.on('window-all-closed', function () {
    if (process.platform !== 'darwin') {
        app.quit()
    }
})

app.on('activate', function () {
  if (mainWindow === null) {
    createWindow()
  }
})
<commit_msg>change loadURL<commit_after>const {app, Menu, BrowserWindow} = require('electron')
const path = require('path')
const url = require('url')

let mainWindow

function createWindow () {
    mainWindow = new BrowserWindow({
        width: 800,
        height: 600,
        tabbingIdentifier: 'flowdock',
        webPreferences: {
            partition: 'persist:xxx'
        }
    })

    mainWindow.loadURL('https://www.flowdock.com/app')
    mainWindow.on('closed', function () {
        mainWindow = null
    })

    const template = [
        {
            label: 'Edit',
            submenu: [
                { label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" },
                { label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" },
                { type: "separator" },
                { label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" },
                { label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" },
                { label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" },
                { label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:" }
            ]
        },
        {
            label: 'View',
            submenu: [
                {role: 'reload'},
                {role: 'forcereload'},
                {role: 'toggledevtools'},
                {type: 'separator'},
                {role: 'resetzoom'},
                {role: 'zoomin'},
                {role: 'zoomout'},
                {type: 'separator'},
                {role: 'togglefullscreen'}
            ]
        },
        {
            role: 'window',
            submenu: [
                {role: 'minimize'},
                {role: 'close'}
            ]
        }
    ]

    if (process.platform === 'darwin') {
        template.unshift({
            label: app.getName(),
            submenu: [
                {role: 'about'},
                {type: 'separator'},
                {role: 'services', submenu: []},
                {type: 'separator'},
                {role: 'hide'},
                {role: 'hideothers'},
                {role: 'unhide'},
                {type: 'separator'},
                {role: 'quit'}
            ]
        })

        template[3].submenu = [
            {role: 'close'},
            {role: 'minimize'},
            {role: 'zoom'},
            {type: 'separator'},
            {role: 'front'}
        ]
    }

    const menu = Menu.buildFromTemplate(template)
    Menu.setApplicationMenu(menu)
}

app.on('ready', createWindow)
app.on('window-all-closed', function () {
    if (process.platform !== 'darwin') {
        app.quit()
    }
})

app.on('activate', function () {
  if (mainWindow === null) {
    createWindow()
  }
})
<|endoftext|>


<commit_before>$(document).on('turbolinks:load', function() {

	$('.menu_control, menu.complete .close').click(function(e) {
		e.preventDefault();
		if( $('menu.complete').css('display') == 'none' ) {
			$('menu.complete').velocity("slideDown").velocity("scroll");
			$('menu.complete .close').velocity({
				display: 'block',
				top: '-2em',
				"z-index": 1,
				opacity: 1
			}, {
				delay: 150
			});
		}
		else {
			$('menu.complete .close').velocity({
				"z-index": -1,
				top: '2em',
				opacity: 0
			});
			$('menu.complete').velocity("slideUp", { delay: 150 }).velocity("scroll");
		}
	});

});<commit_msg>Remove scrollto on desktop<commit_after>$(document).on('turbolinks:load', function() {

	$('.menu_control, menu.complete .close').click(function(e) {
		e.preventDefault();
		if( $('menu.complete').css('display') == 'none' ) {
			$('menu.complete').velocity("slideDown");
			if(($(window).width() < 740)) {
				$('menu.complete').velocity("scroll", { offset: -30});
			}
			$('menu.complete .close').velocity({
				display: 'block',
				top: '-2em',
				"z-index": 1,
				opacity: 1
			}, {
				delay: 150
			});
		}
		else {
			$('menu.complete .close').velocity({
				"z-index": -1,
				top: '2em',
				opacity: 0
			});
			$('menu.complete').velocity("slideUp", { delay: 150 }).velocity("scroll");
		}
	});

});<|endoftext|>


<commit_before>(function () {

    /**
     * Define public loader
     * @returns {boolean}
     * @private
     */
    function _loadPublic() {

        var script = document.getElementById('require-init'),
            dataset = script.dataset || {},
            site = dataset.resource,
            uuid = dataset.uuid,
            version = parseInt(dataset.version || 0, 10) || 1,
            user = dataset.user,
            mode = dataset.mode,
            activated = dataset.activated,
            environment = dataset.environment;

        /**
         * Define Setup
         * @returns Application
         * @private
         */
        function _setup() {

            require([

                'jquery.ui',
                'bootstrap',
                '_',

                'lz-string',
                'jquery.ujs',
                'jquery.resizestop',
                'jquery.zoomooz',

                'extends/function',
                'extends/json',
                'extends/event',
                'extends/string',
                'extends/array',

                'config/listeners',
                'config/permissions',

                'public/' + site + '/javascript/listeners',
                'public/' + site + '/javascript/permissions'

            ], function init() {

                require([
                    'config/application',
                    'public/' + site + '/javascript/config'
                ], function loadApplication(Application, config) {

                    $.extend(true, config, {
                        user: user,
                        uuid: uuid,
                        version: version,
                        activate: activated === 'true',
                        environment: environment,
                        appName: site,
                        mode: mode
                    });

                    /**
                     * Define application
                     * @type {Application}
                     */
                    return new Application({config: config});
                });
            });
        }

        require(['../scripts/core/config/main'], _setup);
    }

    _loadPublic();

})();<commit_msg>fix loading version<commit_after>(function () {

    /**
     * Define public loader
     * @returns {boolean}
     * @private
     */
    function _loadPublic() {

        var script = document.getElementById('require-init'),
            dataset = script.dataset || {},
            site = dataset.resource,
            uuid = dataset.uuid,
            version = parseInt(dataset.current || 0, 10) || 1,
            user = dataset.user,
            mode = dataset.mode,
            activated = dataset.activated,
            environment = dataset.environment;

        /**
         * Define Setup
         * @returns Application
         * @private
         */
        function _setup() {

            require([

                'jquery.ui',
                'bootstrap',
                '_',

                'lz-string',
                'jquery.ujs',
                'jquery.resizestop',
                'jquery.zoomooz',

                'extends/function',
                'extends/json',
                'extends/event',
                'extends/string',
                'extends/array',

                'config/listeners',
                'config/permissions',

                'public/' + site + '/javascript/listeners',
                'public/' + site + '/javascript/permissions'

            ], function init() {

                require([
                    'config/application',
                    'public/' + site + '/javascript/config'
                ], function _initApplication(Application, config) {

                    $.extend(true, config, {
                        user: user,
                        uuid: uuid,
                        version: version,
                        activate: activated === 'true',
                        environment: environment,
                        appName: site,
                        mode: mode
                    });

                    /**
                     * Define application
                     * @type {Application}
                     */
                    return new Application({config: config});
                });
            });
        }

        require(['../scripts/core/config/main'], _setup);
    }

    _loadPublic();

})();<|endoftext|>


<commit_before>/**
 * Module dependencies
 */

var _ = require('@sailshq/lodash');

/**
 * Transformation
 *
 * Allows for a Waterline Collection to have different
 * attributes than what actually exist in an adater's representation.
 *
 * @param {Object} attributes
 * @param {Object} tables
 */

var Transformation = module.exports = function(attributes) {

  // Hold an internal mapping of keys to transform
  this._transformations = {};

  // Initialize
  this.initialize(attributes);

  return this;
};

/**
 * Initial mapping of transformations.
 *
 * @param {Object} attributes
 * @param {Object} tables
 */

Transformation.prototype.initialize = function(attributes) {
  var self = this;

  _.each(attributes, function(attrValue, attrName) {
    // Make sure the attribute has a columnName set
    if (!_.has(attrValue, 'columnName')) {
      return;
    }

    // Ensure the columnName is a string
    if (!_.isString(attrValue.columnName)) {
      throw new Error('Column Name must be a string on ' + attrName);
    }

    // Set the column name transformation
    self._transformations[attrName] = attrValue.columnName;
  });
};

/**
 * Transforms a set of attributes into a representation used
 * in an adapter.
 *
 * @param {Object} attributes to transform
 * @return {Object}
 */

Transformation.prototype.serialize = function(values, behavior) {
  var self = this;

  behavior = behavior || 'default';

  function recursiveParse(obj) {

    // Return if no object
    if (!obj) {
      return;
    }

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    // TODO: remove this:
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    // Handle array of types for findOrCreateEach
    if (_.isString(obj)) {
      if (_.has(self._transformations, obj)) {
        values = self._transformations[obj];
        return;
      }

      return;
    }
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    _.each(obj, function(propertyValue, propertyName) {
      // Schema must be serialized in first level only
      if (behavior === 'schema') {
        if (_.has(self._transformations, propertyName)) {
          obj[self._transformations[propertyName]] = propertyValue;
          delete obj[propertyName];
        }
        return;
      }

      // Recursively parse `OR` or `AND` criteria objects to transform keys
      if (_.isArray(propertyValue) && (propertyName === 'or' || propertyName === 'and')) {
        return recursiveParse(propertyValue);
      }

      // If Nested Object call function again passing the property as obj
      if ((toString.call(propertyValue) !== '[object Date]') && (_.isPlainObject(propertyValue))) {

        // check if object key is in the transformations
        if (_.has(self._transformations, propertyName)) {
          obj[self._transformations[propertyName]] = propertyValue;

          // Only delete if the names are different
          if (self._transformations[propertyName] !== propertyName) {
            delete obj[propertyName];
          }

          return recursiveParse(obj[self._transformations[propertyName]]);
        }

        return recursiveParse(propertyValue);
      }

      // If the property === SELECT check for any transformation keys
      if (propertyName === 'select' && _.isArray(propertyValue)) {
        // var arr = _.clone(obj[property]);
        _.each(propertyValue, function(prop) {
          if(_.has(self._transformations, prop)) {
            var idx = _.indexOf(propertyValue, prop);
            if(idx > -1) {
              obj[propertyName][idx] = self._transformations[prop];
            }
          }
        });
      }

      // If the property === SORT check for any transformation keys
      if (propertyName === 'sort' && _.isArray(propertyValue)) {
        obj.sort = _.map(obj.sort, function(sortClause) {
          var sort = {};
          var attrName = _.first(_.keys(sortClause));
          var sortDirection = sortClause[attrName];
          var columnName = self._transformations[attrName];
          sort[columnName] = sortDirection;
          return sort;
        });
      }

      // Check if property is a transformation key
      if (_.has(self._transformations, propertyName)) {
        obj[self._transformations[propertyName]] = propertyValue;
        if (self._transformations[propertyName] !== propertyName) {
          delete obj[propertyName];
        }
      }
    });
  }

  // Recursivly parse attributes to handle nested criteria
  recursiveParse(values);

  return values;
};

/**
 * .unserialize()
 *
 * Destructively transforms a physical-layer record received
 * from an adapter into a logical representation appropriate
 * for userland (i.e. swapping out column names for attribute
 * names)
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * @param {Dictionary} pRecord
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * @returns {Dictionary}
 *          This is an unnecessary return -- this method just
 *          returns the same reference to the original pRecord,
 *          which has been destructively mutated anyway.
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */

Transformation.prototype.unserialize = function(pRecord) {

  // Loop through the keys of the record and change them.
  _.each(this._transformations, function(columnName, attrName) {

    if (!_.has(pRecord, columnName)) {
      return;
    }

    pRecord[attrName] = pRecord[columnName];
    if (columnName !== attrName) {
      delete pRecord[columnName];
    }

  });

  return pRecord;
};
<commit_msg>Refactor variable name for clarity, and add TODO about probable bug involving attributes named 'sort', 'select', etc.<commit_after>/**
 * Module dependencies
 */

var _ = require('@sailshq/lodash');

/**
 * Transformation
 *
 * Allows for a Waterline Collection to have different
 * attributes than what actually exist in an adater's representation.
 *
 * @param {Object} attributes
 * @param {Object} tables
 */

var Transformation = module.exports = function(attributes) {

  // Hold an internal mapping of keys to transform
  this._transformations = {};

  // Initialize
  this.initialize(attributes);

  return this;
};

/**
 * Initial mapping of transformations.
 *
 * @param {Object} attributes
 * @param {Object} tables
 */

Transformation.prototype.initialize = function(attributes) {
  var self = this;

  _.each(attributes, function(attrDef, attrName) {
    // Make sure the attribute has a columnName set
    if (!_.has(attrDef, 'columnName')) {
      return;
    }

    // Ensure the columnName is a string
    if (!_.isString(attrDef.columnName)) {
      throw new Error('Consistency violation: `columnName` must be a string.  But for this attribute (`'+attrName+'`) it is not!');
    }

    // Set the column name transformation
    self._transformations[attrName] = attrDef.columnName;
  });
};

/**
 * Transforms a set of attributes into a representation used
 * in an adapter.
 *
 * @param {Object} attributes to transform
 * @return {Object}
 */

Transformation.prototype.serialize = function(values, behavior) {
  var self = this;

  behavior = behavior || 'default';

  function recursiveParse(obj) {

    // Return if no object
    if (!obj) {
      return;
    }

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    // TODO: remove this:
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    // Handle array of types for findOrCreateEach
    if (_.isString(obj)) {
      if (_.has(self._transformations, obj)) {
        values = self._transformations[obj];
        return;
      }

      return;
    }
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    _.each(obj, function(propertyValue, propertyName) {
      // Schema must be serialized in first level only
      if (behavior === 'schema') {
        if (_.has(self._transformations, propertyName)) {
          obj[self._transformations[propertyName]] = propertyValue;
          delete obj[propertyName];
        }
        return;
      }

      // Recursively parse `OR` or `AND` criteria objects to transform keys
      if (_.isArray(propertyValue) && (propertyName === 'or' || propertyName === 'and')) {
        return recursiveParse(propertyValue);
      }

      // If nested dictionary, then take the recursive step, calling the function again
      // and passing the nested dictionary as `obj`
      if (!_.isDate(propertyValue) && _.isPlainObject(propertyValue)) {

        // check if object key is in the transformations
        if (_.has(self._transformations, propertyName)) {
          obj[self._transformations[propertyName]] = propertyValue;

          // Only delete if the names are different
          if (self._transformations[propertyName] !== propertyName) {
            delete obj[propertyName];
          }

          return recursiveParse(obj[self._transformations[propertyName]]);
        }

        return recursiveParse(propertyValue);
      }

      // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      // TODO: Assuming this is still in use, then split it up into separate
      // utilities (consider what would happen if you named an attribute "select"
      // or "sort").  We shouldn't use the same logic to transform attrs to column
      // names in criteria as we do `newRecord` or `valuesToSet`, etc.
      // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      // If the property === SELECT check for any transformation keys
      if (propertyName === 'select' && _.isArray(propertyValue)) {
        // var arr = _.clone(obj[property]);
        _.each(propertyValue, function(prop) {
          if(_.has(self._transformations, prop)) {
            var idx = _.indexOf(propertyValue, prop);
            if(idx > -1) {
              obj[propertyName][idx] = self._transformations[prop];
            }
          }
        });
      }

      // If the property === SORT check for any transformation keys
      if (propertyName === 'sort' && _.isArray(propertyValue)) {
        obj.sort = _.map(obj.sort, function(sortClause) {
          var sort = {};
          var attrName = _.first(_.keys(sortClause));
          var sortDirection = sortClause[attrName];
          var columnName = self._transformations[attrName];
          sort[columnName] = sortDirection;
          return sort;
        });
      }
      // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

      // Check if property is a transformation key
      if (_.has(self._transformations, propertyName)) {
        obj[self._transformations[propertyName]] = propertyValue;
        if (self._transformations[propertyName] !== propertyName) {
          delete obj[propertyName];
        }
      }
    });
  }

  // Recursivly parse attributes to handle nested criteria
  recursiveParse(values);

  return values;
};

/**
 * .unserialize()
 *
 * Destructively transforms a physical-layer record received
 * from an adapter into a logical representation appropriate
 * for userland (i.e. swapping out column names for attribute
 * names)
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * @param {Dictionary} pRecord
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * @returns {Dictionary}
 *          This is an unnecessary return -- this method just
 *          returns the same reference to the original pRecord,
 *          which has been destructively mutated anyway.
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */

Transformation.prototype.unserialize = function(pRecord) {

  // Loop through the keys of the record and change them.
  _.each(this._transformations, function(columnName, attrName) {

    if (!_.has(pRecord, columnName)) {
      return;
    }

    pRecord[attrName] = pRecord[columnName];
    if (columnName !== attrName) {
      delete pRecord[columnName];
    }

  });

  return pRecord;
};
<|endoftext|>


<commit_before>// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

$(document).ready(function(){
  var editor = ace.edit("editor");
  var textarea = $('#gemfile_content');
  textarea.hide();

  editor.setTheme("ace/theme/github");
  editor.getSession().setMode("ace/mode/ruby");
  editor.getSession().setValue(textarea.val());
  editor.getSession().on('change', function(){
    textarea.val(editor.getSession().getValue());
  });
});
<commit_msg>Use Textmate theme<commit_after>// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

$(document).ready(function(){
  var editor = ace.edit("editor");
  var textarea = $('#gemfile_content');
  textarea.hide();

  editor.setTheme("ace/theme/textmate");
  editor.getSession().setMode("ace/mode/ruby");
  editor.getSession().setValue(textarea.val());
  editor.getSession().on('change', function(){
    textarea.val(editor.getSession().getValue());
  });
});
<|endoftext|>


<commit_before>// ELMO.UrlBuilder
(function (ns, klass) {
  // constructor
  ns.UrlBuilder = klass = function (params) {
    this.locale = params.locale;
    this.mode = params.mode;
    this.mission_name = params.mission_name;
  };

  klass.prototype.build = function () {
    // we need some funky magic to turn the arguments object into an array
    let args = Array.prototype.slice.call(arguments, 0);
    let options = {};

    // if the last arg is an options hash, extract it
    if (typeof (args[args.length - 1]) === 'object') {
      options = args[args.length - 1];
      args = args.slice(0, args.length - 1);
    }

    // default to the current locale and mission name
    options.locale = options.locale || this.locale;
    options.mode = options.mode || this.mode;
    options.mission_name = options.mission_name || this.mission_name;

    suffix = this.strip_scope(args.join('/'));

    let result;
    switch (options.mode) {
      case 'basic':
        result = `/${options.locale}/${suffix}`;
        break;
      case 'mission':
        result = `/${options.locale}/m/${options.mission_name}/${suffix}`;
        break;
      case 'admin':
        result = `/${options.locale}/admin/${suffix}`;
        break;
      default:
        throw 'invalid mode';
    }

    // return, fixing any double or trailing slashes
    return result.replace(/[\/]{2,}/g, '/').replace(/\/$/, '');
  };

  // Strips the locale and mission_name from the given path.
  klass.prototype.strip_scope = function (path) {
    let mode_mission;
    if (this.mode == 'mission') mode_mission = `m/${this.mission_name}`;
    else if (this.mode == 'admin') mode_mission = 'admin';
    else mode_mission = '';

    // replace the "/en/", "/en/m/mission", "/en/m/mission/", "/en", and "/" variants with "/"
    if (path == '' || path.match(new RegExp(`^\/([a-z]{2}(\/${mode_mission})?(\/)?)?$`))) return '/';
    // else fix the "/en/foo" or "/en/m/mission/foo" variants
    return path.replace(new RegExp(`^\/[a-z]{2}(\/${mode_mission})?\/(.+)`), (m, $1, $2) => { return `/${$2}`; });
  };
}(ELMO));
<commit_msg>9397: Manually fix remaining url_builder lints<commit_after>/* eslint-disable no-param-reassign */

// ELMO.UrlBuilder
(function (ns, klass) {
  // eslint-disable-next-line no-multi-assign
  ns.UrlBuilder = klass = function (params) {
    this.locale = params.locale;
    this.mode = params.mode;
    this.mission_name = params.mission_name;
  };

  klass.prototype.build = function (...args) {
    let options = {};

    // If the last arg is an options hash, extract it.
    if (typeof args[args.length - 1] === 'object') {
      options = args[args.length - 1];
      args = args.slice(0, args.length - 1);
    }

    // Default to the current locale and mission name.
    options.locale = options.locale || this.locale;
    options.mode = options.mode || this.mode;
    options.mission_name = options.mission_name || this.mission_name;

    const suffix = this.strip_scope(args.join('/'));

    let result;
    switch (options.mode) {
      case 'basic':
        result = `/${options.locale}/${suffix}`;
        break;
      case 'mission':
        result = `/${options.locale}/m/${options.mission_name}/${suffix}`;
        break;
      case 'admin':
        result = `/${options.locale}/admin/${suffix}`;
        break;
      default:
        throw new Error(`Invalid mode: ${options.mode}`);
    }

    // Return, fixing any double or trailing slashes.
    return result.replace(/[/]{2,}/g, '/').replace(/\/$/, '');
  };

  // Strip the locale and mission_name from the given path.
  klass.prototype.strip_scope = function (path) {
    let modeMission;
    if (this.mode === 'mission') modeMission = `m/${this.mission_name}`;
    else if (this.mode === 'admin') modeMission = 'admin';
    else modeMission = '';

    // Replace the "/en/", "/en/m/mission", "/en/m/mission/", "/en", and "/" variants with "/"...
    if (!path || path.match(new RegExp(`^/([a-z]{2}(/${modeMission})?(/)?)?$`))) {
      return '/';
    }
    // ...else fix the "/en/foo" or "/en/m/mission/foo" variants.
    return path.replace(new RegExp(`^/[a-z]{2}(/${modeMission})?/(.+)`), (m, $1, $2) => `/${$2}`);
  };
}(ELMO));
<|endoftext|>


<commit_before>var roundSimpleRating = function(rating) {
  if (Ember.isNone(rating)) {
    return null;
  }

  if (rating > 2.4 && rating < 3.6) {
    return 3;
  } else if (rating < 3) {
    return 1;
  } else {
    return 5;
  }
};

var simpleRatingIcons = {
  1: 'fa fa-frown-o',
  3: 'fa fa-meh-o',
  5: 'fa fa-smile-o'
};

var advancedRatingIcons = ['fa fa-star-o', 'fa fa-star-half-o', 'fa fa-star'];

var nearestHalf = function(number) {
  if (Ember.isNone(number)) {
    return null;
  }
  return Math.floor(number * 2) / 2;
};

var starFor = function(rating, i) {
  if (nearestHalf(rating) - i > -0.01) {
    return advancedRatingIcons[2];
  } else if (nearestHalf(rating) - i > -0.51) {
    return advancedRatingIcons[1];
  } else {
    return advancedRatingIcons[0];
  }
};

var renderStars = function(rating) {
  var stars = "";
  for (var i = 1; i <= 5; i++) {
    stars += "";
  }
  return stars;
};

$.fn.AwesomeRating = function(options) {
  return this.each(function() {
    var _results;
    var widget = this,
      rating = options["rating"];
    $(widget).empty();
    $(widget).unbind();
    if (options["editable"]) {
      $(widget).addClass("editable");
    }
    if (options["type"] === "simple") {
      // Simple ratings.
      rating = roundSimpleRating(rating);
      // Loop through 1,3,5.
      for (var i = 1; i <= 5; i += 2) {
        var icon = $("");
        icon.find("i").addClass(simpleRatingIcons[i]);
        if (rating === i) {
          icon.find("i").addClass("active");
        }
        if (options["editable"]) {
          icon.css('cursor', 'pointer');
          icon.click(function() {
            var newRating = roundSimpleRating($(this).attr("data-rating"));
            return options["update"](newRating);
          });
        }
        _results.push($(this).append(icon));
      }
      return _results;
    } else {
      // Advanced ratings.
      rating = nearestHalf(rating);
      $(widget).html(renderStars(rating));
      if (options["editable"]) {
        $(widget).css('cursor', 'pointer');
        return $(widget).bind({
          mousemove: function(e) {
            var newRating = nearestHalf(5 * (e.pageX - $(widget).offset().left) / $(widget).width() + 0.5);
            return $(widget).find(".icon-container").each(function() {
              var index = $(this).attr("data-rating"),
                starClass = starFor(newRating, index);
              return $(this).find("i").removeClass('fa-star').removeClass('fa-star-half-o').removeClass('fa-star-o').addClass(starClass);
            });
          },
          mouseleave: function() {
            return $(widget).html(renderStars(rating));
          },
          click: function(e) {
            var newRating = nearestHalf(5 * (e.pageX - $(widget).offset().left) / $(widget).width() + 0.5);
            return options["update"](newRating);
          }
        });
      }
    }
  });
};
<commit_msg>fix simple ratings not showing<commit_after>var roundSimpleRating = function(rating) {
  if (Ember.isNone(rating)) {
    return null;
  }

  if (rating > 2.4 && rating < 3.6) {
    return 3;
  } else if (rating < 3) {
    return 1;
  } else {
    return 5;
  }
};

var simpleRatingIcons = {
  1: 'fa fa-frown-o',
  3: 'fa fa-meh-o',
  5: 'fa fa-smile-o'
};

var advancedRatingIcons = ['fa fa-star-o', 'fa fa-star-half-o', 'fa fa-star'];

var nearestHalf = function(number) {
  if (Ember.isNone(number)) {
    return null;
  }
  return Math.floor(number * 2) / 2;
};

var starFor = function(rating, i) {
  if (nearestHalf(rating) - i > -0.01) {
    return advancedRatingIcons[2];
  } else if (nearestHalf(rating) - i > -0.51) {
    return advancedRatingIcons[1];
  } else {
    return advancedRatingIcons[0];
  }
};

var renderStars = function(rating) {
  var stars = "";
  for (var i = 1; i <= 5; i++) {
    stars += "";
  }
  return stars;
};

$.fn.AwesomeRating = function(options) {
  return this.each(function() {
    var _results = [];
    var widget = this,
      rating = options["rating"];
    $(widget).empty();
    $(widget).unbind();
    if (options["editable"]) {
      $(widget).addClass("editable");
    }
    if (options["type"] === "simple") {
      // Simple ratings.
      rating = roundSimpleRating(rating);
      // Loop through 1,3,5.
      for (var i = 1; i <= 5; i += 2) {
        var icon = $("");
        icon.find("i").addClass(simpleRatingIcons[i]);
        if (rating === i) {
          icon.find("i").addClass("active");
        }
        if (options["editable"]) {
          icon.css('cursor', 'pointer');
          icon.click(function() {
            var newRating = roundSimpleRating($(this).attr("data-rating"));
            return options["update"](newRating);
          });
        }
        _results.push($(this).append(icon));
      }
      return _results;
    } else {
      // Advanced ratings.
      rating = nearestHalf(rating);
      $(widget).html(renderStars(rating));
      if (options["editable"]) {
        $(widget).css('cursor', 'pointer');
        return $(widget).bind({
          mousemove: function(e) {
            var newRating = nearestHalf(5 * (e.pageX - $(widget).offset().left) / $(widget).width() + 0.5);
            return $(widget).find(".icon-container").each(function() {
              var index = $(this).attr("data-rating"),
                starClass = starFor(newRating, index);
              return $(this).find("i").removeClass('fa-star').removeClass('fa-star-half-o').removeClass('fa-star-o').addClass(starClass);
            });
          },
          mouseleave: function() {
            return $(widget).html(renderStars(rating));
          },
          click: function(e) {
            var newRating = nearestHalf(5 * (e.pageX - $(widget).offset().left) / $(widget).width() + 0.5);
            return options["update"](newRating);
          }
        });
      }
    }
  });
};
<|endoftext|>


<commit_before>// Remote model with pagination

(function($) {
  function RemoteModel(path, initialFilters, columns) {
    // private
    var loadingSize = 200;
    var preemptiveLoadingSize = 100;
    var pageSize = 0;
    var pageNum = 0;
    var totalRows = 0;
    var rowsWithoutFilter = -1; // Ekohe Add
    var data = {length:0};
    var oldData = {length:0};
    var sortcol = null;
    var sortdir = 1;
    var h_request = null;
    var req = null; // ajax request
    var req_page;
    var params = [];
    var pagingOptionsChanged = false;
    var grid;
    var loadingIndicator = null;
    var mainIndicator = null;
    var initedFilter = false;
    var filters = [];
    var lastRequestVersionNumber = 0;
    var currentRequestVersionNumber = 0;
    var self = this;

    if(initialFilters) {
      for(var i = 0; i < initialFilters.length; i++) {
        filters.push([initialFilters[i]['column'], initialFilters[i]['value'], initialFilters[i]['operator']]);
      }
    }

    // Connection manager
    var connectionManager = new ConnectionManager();

    // events
    var beforeRemoteRequest = new Slick.Event();
    var onDataLoading = new Slick.Event();
    var onPagingInfoChanged = new Slick.Event();
    var onDataLoaded = new Slick.Event();

    function setGrid(newGrid) {
      grid = newGrid;

      // Connect the grid and the loader
      grid.onViewportChanged.subscribe(function(e, args) {
        var vp = grid.getViewport();
        // when the grid rendered, onViewportChanged will be triggerd, if eagerLoading is false and no data loaded yet, we don't load the initial data
        if(grid.options.eagerLoading === false && grid.getData().length === 0) return false;
        // Event triggered before the ajax request
        beforeRemoteRequest.notify();
        ensureData(vp.top, vp.bottom);
      });

      grid.onSort.subscribe(function(e, args){
        setSort(args.sortCol.sortColumn, args.sortAsc ? 1 : -1);
      });
    }

    function rowsChanged() {
      var vp = grid.getViewport();
      ensureData(vp.top, vp.bottom);
    }

    function dataIsLoaded(args) {
      for (var i = args.from; i < args.to; i++) {
        grid.invalidateRow(i);
      }

      grid.updateRowCount();

      grid.render();

      onDataLoaded.notify();
    }

    function isDataLoaded(from,to) {
      for (var i=from; i<=to; i++) {
        if (data[i] === undefined || data[i] === null)
          return false;
      }

      return true;
    }

    function clear() {
      for (var key in data) {
        delete data[key];
      }
      data.length = 0;
    }

    function generateUrl(from, to) {
      // pagination - specific to the grid viewport
      var paginationOptions = getPaginationOptions(from, to);
      var offset = paginationOptions[0];
      var count = paginationOptions[1];
      var normalLoadingMode = true;
      if (count === 0) {
        // Nothing to load, try to see if there is a need to load data preemptively
        paginationOptions = getPaginationOptions(from-preemptiveLoadingSize, to+preemptiveLoadingSize);
        offset = paginationOptions[0];
        count = paginationOptions[1];

        if (count === 0) {
          // the current data on the view is loaded and no preemptive loading to do
          return null;
        }

        // Preemptive loading mode
        normalLoadingMode = false;
      }
      if (initedFilter || filters.length > 0) {
        path = path.replace(/filters.*?&/g,'').replace(/&filters.*/g,'');
      } else {
        initedFilter = true;
      }
      var url = path + "&offset=" + offset + "&count=" + count;

      // filters, ordering, extra parameters - not specific to the viewport
      url += conditionalURI();
      return [url, normalLoadingMode];
    }

    function conditionalURI() {
      // Sorting
      if (sortcol === null) {
        sortcol = "";
      }
      var url = "&sort_col="+encodeURIComponent(sortcol);
      if (sortdir>0) {
        url += "&sort_dir=ASC";
      } else {
        url += "&sort_dir=DESC";
      }

      // Filters
      $.each(filters, function(index, value) {
        url += "&filters[][column]="+encodeURIComponent(value[0])+"&filters[][value]="+encodeURIComponent(value[1])+"&filters[][operator]="+encodeURIComponent(value[2]);
      });

      // Parameters
      $.each(params, function(index, value) {
        url += "&"+encodeURIComponent(value[0])+"="+encodeURIComponent(value[1]);
      });

      return url;
    }

    // Returns offset, count
    function getPaginationOptions(from, to) {
      // 2 cases, whether we are in load-as-scroll mode or the per page mode
      if (pageSize === 0) {
        // Load as we scroll
        if (from < 0)
          from = 0;

        fromPage = Math.floor(from / loadingSize);
        toPage = Math.floor(to / loadingSize);

        // Increment fromPage if the page at fromPage is already loaded until we find an area that hasn't been loaded yet
        while (data[fromPage * loadingSize] !== undefined && fromPage < toPage)
          fromPage++;

        // Decrement toPage if the page at fromPage is already loaded until we find an area that hasn't been loaded yet
        while (data[toPage * loadingSize] !== undefined && fromPage < toPage)
          toPage--;

        if (fromPage > toPage) {
          return [0,0];
        }

        var alreadyLoaded = true;

        for (var i=fromPage; i<=toPage; i++) {
          if (typeof(data[i*loadingSize]) == 'undefined') {
            alreadyLoaded = false;
          }
        }

        if (alreadyLoaded) {
          return [0,0];
        }

        for (var i=fromPage; i<=toPage; i++)
          data[i*loadingSize] = null; // null indicates a 'requested but not available yet'

        return [(fromPage * loadingSize), (((toPage - fromPage) * loadingSize) + loadingSize)];
      } else {
        // Per page
        if (pagingOptionsChanged === false) {
          return [0,0];
        }
        pagingOptionsChanged = false;
        fromPage = pageNum;
        toPage = pageNum;

        // for (var i=fromPage; i<=toPage; i++)
        //   data[i*loadingSize] = null; // null indicates a 'requested but not available yet'
        return [(pageNum * pageSize), pageSize];
      }
    }

    function ensureData(from, to) {
      var urlData = generateUrl(from, to);

      // Nothing to load, just return.
      if(urlData === null) { return; }
      var url = urlData[0];
      var normalLoading = urlData[1];

      // Ekohe Edit: Stop passing indicator to connection (Create progress bar as indicator in connection instead)
      // Store loading size to provide stats. If pageSize is not zero then we are coming from a pager request.
      // loadingIndicator.loadingSize = (pageSize === 0 ? loadingSize : pageSize);
      connectionManager.createConnection(grid, url, loadingIndicator, onSuccess, onError, currentRequestVersionNumber);
    }

    function onError(request, textStatus, errorThrown) {
      //
    }

    function onSuccess(resp, textStatus, request) {
      var from;
      var to;

      // Ekohe Add: Save row length without filter
      rowsWithoutFilter = parseInt(resp.totalNoFilter, 10);

      // Load as we scroll
      if (pageSize === 0) {
        from = resp.offset;
        to = resp.offset + resp.count;
        data.length = parseInt(resp.total, 10);
      } else {
        from = 0;
        to = parseInt(resp.count, 10);
        data.length = to;
      }

      totalRows = parseInt(resp.total, 10);
      if (resp.rows) {
        if (resp.rows.length < loadingSize) {
          data[from+loadingSize] = null;
        }
        for (var i = 0; i < resp.rows.length; i++) {
          var j = parseInt(from, 10) + parseInt(i, 10);
          var obj = {};
          $.each(columns, function(index, value) {
            var item = resp.rows[i][index];
            // match the column and the response data (compare column name and response data key)
            if(item && typeof(item) == 'object' && !(item instanceof Array)) {
              $.extend(true, obj, item);
            } else {
              obj[value.id] = item;
            }
          });
          data[j] = obj;
          data[j].slick_index = j;
        }
      }
      req = null;

      // keep oldData as a clone of data, never get deleted
      this.loader.oldData = deep_clone(data);

      // Loading data
      dataIsLoaded({from:from, to:to});

      // Updating pager
      onPagingInfoChanged.notify(getPagingInfo());
    }

    function getColumns() {
      return columns;
    }

    function getKeys(h) {
      var keys = [];
      for (var key in h)
        keys.push(key);
      return keys;
    }

    function reloadData(from,to) {
      if(!from && !to){
        var position = decideCurrentPosition();
        from = position[0];
        to = position[1];
      }
      var i;
      if (from && to) {
        for (i=from; i<=to; i++)
          delete data[i];
      } else {
        for (i=0; i<=data.length; i++)
          delete data[i];
      }

      ensureData(from,to);
    }

    function decideCurrentPosition(){
      var currentRow, from;
      if(grid.operatedIds && grid.operatedIds.length > 0) {
        var gridRow = grid.getRowByRecordId(grid.operatedIds[grid.operatedIds.length-1]);
        if(!gridRow) return [null, null];
        currentRow = gridRow.index - 1;
        from = parseInt(currentRow / 200, 10) * 200;
        return [from, currentRow];
      } else {
        return [null, null];
      }
    }

    function setSort(column,dir) {
      if (sortcol != column || sortdir != dir) {
        currentRequestVersionNumber++;
        sortcol = column;
        sortdir = dir;
      }
      refresh();
    }

    function setSortWithoutRefresh(column, dir) {
      if (sortcol != column || sortdir != dir) {
        currentRequestVersionNumber++;
        sortcol = column;
        sortdir = dir;
      }
    }

    function getSortColumn() {
      return sortcol;
    }

    function getSortDirection() {
      return sortdir;
    }

    function setFilter(filterFn) {
      if (filters != filterFn) {
        currentRequestVersionNumber++;
        filters = filterFn;
      }
      refresh();
    }

    function setFilterWithoutRefresh(filterFn) {
      if (filters != filterFn) {
        currentRequestVersionNumber++;
        filters = filterFn;
      }
    }

    function addFilterWithoutRefresh(column, string, operator) {
      if(typeof(operator)==='undefined') operator = 'equals';

      // If the string is an empty string and operator is 'equals', then removing the filter if existing
      if (string === '' && operator == 'equals') {
        var newFilters = [];
        $.each(filters, function(index,filter) {
          if (filter[0]!=column)
            newFilters.push(filter);
        });
        if (filters != newFilters) {
          currentRequestVersionNumber++;
          filters = newFilters;
        }
      } else {
        var updated = 0;
        // Try to update existing filter
        $.map(filters, function(filter) {
          if (filter[0]==column) {
            filter[1] = string;
            filter[2] = operator;
            updated = 1;
            return;
          }
        });
        // Add new filter
        if (updated === 0) {
          filters.push([column, string, operator]);
          currentRequestVersionNumber++;
        }
      }
    }

    function addFilter(column, string, operator) {
      addFilterWithoutRefresh(column, string, operator);
      refresh();
    }

    function addFiltersWithoutRefresh(filters) {
      $.each(filters, function(index, filter) {
        addFilterWithoutRefresh(filter[0], filter[1], filter[2]);
      });
    }

    function addFilters(filters) {
      addFiltersWithoutRefresh(filters);
      refresh();
    }

    function getFilters() {
      return filters;
    }

    function setParam(column, string, _refresh) {
      if (_refresh === undefined) _refresh = true;
      // If the string is an empty string, then removing the param if existing
      if (string === '') {
        var newParams = [];
        $.each(params, function(index,param) {
          if (param[0]!=column)
            newParams.push(param);
        });
        if (params != newParams) {
          currentRequestVersionNumber++;
          params = newParams;
        }
        if (_refresh) refresh(); // Only clear if it was found
        return;
      }

      var updated = 0;
      // Try to update existing param
      $.map(params, function(param) {
        if (param[0]==column) {
          param[1] = string;
          updated = 1;
          return;
        }
      });

      // Add new param
      if (updated === 0)
        params.push([column, string]);
        currentRequestVersionNumber++;

      if (_refresh) refresh();
    }

    function getParams() {
      return params;
    }

    function getParam(key) {
      var value = null;
      $.each(params, function(i) {
        if (params[i][0]==key) {
          value = params[i][1];
        }
      });
      return value;
    }

    function setLoadingIndicator(indicator) {
      loadingIndicator = indicator;
    }

    function setMainIndicator(indicator) {
      mainIndicator = indicator;
    }

    function refresh() {
      pagingOptionsChanged = true;
      clear();
      rowsChanged();
    }

    function setPagingOptions(args) {
      pagingOptionsChanged = false;

      if ((args.pageSize !== undefined) && (args.pageSize!=pageSize)) {
        currentRequestVersionNumber++;
        pageSize = args.pageSize;
        pagingOptionsChanged = true;
      }

      var newPageNum = Math.min(args.pageNum, Math.ceil(totalRows / pageSize));

      if ((args.pageNum !== undefined) && (pageNum!=newPageNum)) {
        currentRequestVersionNumber++;
        pageNum = newPageNum;
        pagingOptionsChanged = true;
      }

      // If we click "All" on pager, this is where we are
      if ((args.pageNum === undefined) && (args.pageSize === 0)) {
        currentRequestVersionNumber++;
        pageNum = 0;
        pageSize = 0;
        pagingOptionsChanged = false;
      }

      // Dirty fix for cases where the numbers don't add up
      if (totalRows / pageSize < pageNum) {
        currentRequestVersionNumber++;
        pageNum = 0;
        pagingOptionsChanged = true;
      }

      refresh();
      onPagingInfoChanged.notify(getPagingInfo());
    }

    function getPagingInfo() {
      // Ekohe Edit: Add rowsWithoutFilter
      // return {pageSize:pageSize, pageNum:pageNum, totalRows:totalRows};
      return {
        pageSize: pageSize,
        pageNum: pageNum,
        totalRows: totalRows,
        rowsWithoutFilter: rowsWithoutFilter
      };
    }

    return {
      // properties
      "data": data,
      "oldData": oldData,
      "connectionManager": connectionManager,
      "currentRequestVersionNumber": currentRequestVersionNumber,
      "lastRequestVersionNumber": lastRequestVersionNumber,

      // methods
      "clear": clear,
      "isDataLoaded": isDataLoaded,
      "ensureData": ensureData,
      "reloadData": reloadData,
      "setSort": setSort,
      "setSortWithoutRefresh": setSortWithoutRefresh,
      "getSortColumn": getSortColumn,
      "getSortDirection": getSortDirection,
      "getFilters": getFilters,
      "setFilterWithoutRefresh": setFilterWithoutRefresh,
      "setFilter": setFilter,
      "addFilterWithoutRefresh": addFilterWithoutRefresh,
      "addFilter": addFilter,
      "addFiltersWithoutRefresh": addFiltersWithoutRefresh,
      "addFilters": addFilters,
      "getParams": getParams,
      "getParam": getParam,
      "setParam": setParam,
      "setGrid": setGrid,
      "conditionalURI": conditionalURI,
      'getColumns': getColumns,

      // events
      "beforeRemoteRequest": beforeRemoteRequest,
      "onDataLoading": onDataLoading,
      "onPagingInfoChanged":  onPagingInfoChanged,
      "onDataLoaded": onDataLoaded,

      // pager
      "getPagingInfo": getPagingInfo,
      "setPagingOptions": setPagingOptions,

      "setLoadingIndicator": setLoadingIndicator,
      "setMainIndicator": setMainIndicator
    };
  }

  // WulinMaster.Data.RemoteModel
  $.extend(true, window, { WulinMaster: { Data: { RemoteModel: RemoteModel }}});
})(jQuery);
<commit_msg>FEATURE #59 - Row detail: sorting/filtering work again<commit_after>// Remote model with pagination

(function($) {
  function RemoteModel(path, initialFilters, columns) {
    // private
    var loadingSize = 200;
    var preemptiveLoadingSize = 100;
    var pageSize = 0;
    var pageNum = 0;
    var totalRows = 0;
    var rowsWithoutFilter = -1; // Ekohe Add
    var data = {length:0};
    var oldData = {length:0};
    var sortcol = null;
    var sortdir = 1;
    var h_request = null;
    var req = null; // ajax request
    var req_page;
    var params = [];
    var pagingOptionsChanged = false;
    var grid;
    var loadingIndicator = null;
    var mainIndicator = null;
    var initedFilter = false;
    var filters = [];
    var lastRequestVersionNumber = 0;
    var currentRequestVersionNumber = 0;
    var self = this;

    if(initialFilters) {
      for(var i = 0; i < initialFilters.length; i++) {
        filters.push([initialFilters[i]['column'], initialFilters[i]['value'], initialFilters[i]['operator']]);
      }
    }

    // Connection manager
    var connectionManager = new ConnectionManager();

    // events
    var beforeRemoteRequest = new Slick.Event();
    var onDataLoading = new Slick.Event();
    var onPagingInfoChanged = new Slick.Event();
    var onDataLoaded = new Slick.Event();

    function setGrid(newGrid) {
      grid = newGrid;

      // Connect the grid and the loader
      grid.onViewportChanged.subscribe(function(e, args) {
        var vp = grid.getViewport();
        // when the grid rendered, onViewportChanged will be triggerd, if eagerLoading is false and no data loaded yet, we don't load the initial data
        if(grid.options.eagerLoading === false && grid.getData().length === 0) return false;
        // Event triggered before the ajax request
        beforeRemoteRequest.notify();
        ensureData(vp.top, vp.bottom);
      });

      grid.onSort.subscribe(function(e, args){
        setSort(args.sortCol.sortColumn, args.sortAsc ? 1 : -1);
      });
    }

    function rowsChanged() {
      var vp = grid.getViewport();
      ensureData(vp.top, vp.bottom);
    }

    function dataIsLoaded(args) {
      for (var i = args.from; i < args.to; i++) {
        grid.invalidateRow(i);
      }

      // Reset data since data is not updated automatically when row detail view added
      grid.setData(args.data);

      grid.updateRowCount();

      grid.render();

      onDataLoaded.notify();
    }

    function isDataLoaded(from,to) {
      for (var i=from; i<=to; i++) {
        if (data[i] === undefined || data[i] === null)
          return false;
      }

      return true;
    }

    function clear() {
      for (var key in data) {
        delete data[key];
      }
      data.length = 0;
    }

    function generateUrl(from, to) {
      // pagination - specific to the grid viewport
      var paginationOptions = getPaginationOptions(from, to);
      var offset = paginationOptions[0];
      var count = paginationOptions[1];
      var normalLoadingMode = true;
      if (count === 0) {
        // Nothing to load, try to see if there is a need to load data preemptively
        paginationOptions = getPaginationOptions(from-preemptiveLoadingSize, to+preemptiveLoadingSize);
        offset = paginationOptions[0];
        count = paginationOptions[1];

        if (count === 0) {
          // the current data on the view is loaded and no preemptive loading to do
          return null;
        }

        // Preemptive loading mode
        normalLoadingMode = false;
      }
      if (initedFilter || filters.length > 0) {
        path = path.replace(/filters.*?&/g,'').replace(/&filters.*/g,'');
      } else {
        initedFilter = true;
      }
      var url = path + "&offset=" + offset + "&count=" + count;

      // filters, ordering, extra parameters - not specific to the viewport
      url += conditionalURI();
      return [url, normalLoadingMode];
    }

    function conditionalURI() {
      // Sorting
      if (sortcol === null) {
        sortcol = "";
      }
      var url = "&sort_col="+encodeURIComponent(sortcol);
      if (sortdir>0) {
        url += "&sort_dir=ASC";
      } else {
        url += "&sort_dir=DESC";
      }

      // Filters
      $.each(filters, function(index, value) {
        url += "&filters[][column]="+encodeURIComponent(value[0])+"&filters[][value]="+encodeURIComponent(value[1])+"&filters[][operator]="+encodeURIComponent(value[2]);
      });

      // Parameters
      $.each(params, function(index, value) {
        url += "&"+encodeURIComponent(value[0])+"="+encodeURIComponent(value[1]);
      });

      return url;
    }

    // Returns offset, count
    function getPaginationOptions(from, to) {
      // 2 cases, whether we are in load-as-scroll mode or the per page mode
      if (pageSize === 0) {
        // Load as we scroll
        if (from < 0)
          from = 0;

        fromPage = Math.floor(from / loadingSize);
        toPage = Math.floor(to / loadingSize);

        // Increment fromPage if the page at fromPage is already loaded until we find an area that hasn't been loaded yet
        while (data[fromPage * loadingSize] !== undefined && fromPage < toPage)
          fromPage++;

        // Decrement toPage if the page at fromPage is already loaded until we find an area that hasn't been loaded yet
        while (data[toPage * loadingSize] !== undefined && fromPage < toPage)
          toPage--;

        if (fromPage > toPage) {
          return [0,0];
        }

        var alreadyLoaded = true;

        for (var i=fromPage; i<=toPage; i++) {
          if (typeof(data[i*loadingSize]) == 'undefined') {
            alreadyLoaded = false;
          }
        }

        if (alreadyLoaded) {
          return [0,0];
        }

        for (var i=fromPage; i<=toPage; i++)
          data[i*loadingSize] = null; // null indicates a 'requested but not available yet'

        return [(fromPage * loadingSize), (((toPage - fromPage) * loadingSize) + loadingSize)];
      } else {
        // Per page
        if (pagingOptionsChanged === false) {
          return [0,0];
        }
        pagingOptionsChanged = false;
        fromPage = pageNum;
        toPage = pageNum;

        // for (var i=fromPage; i<=toPage; i++)
        //   data[i*loadingSize] = null; // null indicates a 'requested but not available yet'
        return [(pageNum * pageSize), pageSize];
      }
    }

    function ensureData(from, to) {
      var urlData = generateUrl(from, to);

      // Nothing to load, just return.
      if(urlData === null) { return; }
      var url = urlData[0];
      var normalLoading = urlData[1];

      // Ekohe Edit: Stop passing indicator to connection (Create progress bar as indicator in connection instead)
      // Store loading size to provide stats. If pageSize is not zero then we are coming from a pager request.
      // loadingIndicator.loadingSize = (pageSize === 0 ? loadingSize : pageSize);
      connectionManager.createConnection(grid, url, loadingIndicator, onSuccess, onError, currentRequestVersionNumber);
    }

    function onError(request, textStatus, errorThrown) {
      //
    }

    function onSuccess(resp, textStatus, request) {
      var from;
      var to;

      // Ekohe Add: Save row length without filter
      rowsWithoutFilter = parseInt(resp.totalNoFilter, 10);

      // Load as we scroll
      if (pageSize === 0) {
        from = resp.offset;
        to = resp.offset + resp.count;
        data.length = parseInt(resp.total, 10);
      } else {
        from = 0;
        to = parseInt(resp.count, 10);
        data.length = to;
      }

      totalRows = parseInt(resp.total, 10);
      if (resp.rows) {
        if (resp.rows.length < loadingSize) {
          data[from+loadingSize] = null;
        }
        for (var i = 0; i < resp.rows.length; i++) {
          var j = parseInt(from, 10) + parseInt(i, 10);
          var obj = {};
          $.each(columns, function(index, value) {
            var item = resp.rows[i][index];
            // match the column and the response data (compare column name and response data key)
            if(item && typeof(item) == 'object' && !(item instanceof Array)) {
              $.extend(true, obj, item);
            } else {
              obj[value.id] = item;
            }
          });
          data[j] = obj;
          data[j].slick_index = j;
        }
      }
      req = null;

      // keep oldData as a clone of data, never get deleted
      this.loader.oldData = deep_clone(data);

      // Loading data
      dataIsLoaded({from:from, to:to, data:data});

      // Updating pager
      onPagingInfoChanged.notify(getPagingInfo());
    }

    function getColumns() {
      return columns;
    }

    function getKeys(h) {
      var keys = [];
      for (var key in h)
        keys.push(key);
      return keys;
    }

    function reloadData(from,to) {
      if(!from && !to){
        var position = decideCurrentPosition();
        from = position[0];
        to = position[1];
      }
      var i;
      if (from && to) {
        for (i=from; i<=to; i++)
          delete data[i];
      } else {
        for (i=0; i<=data.length; i++)
          delete data[i];
      }

      ensureData(from,to);
    }

    function decideCurrentPosition(){
      var currentRow, from;
      if(grid.operatedIds && grid.operatedIds.length > 0) {
        var gridRow = grid.getRowByRecordId(grid.operatedIds[grid.operatedIds.length-1]);
        if(!gridRow) return [null, null];
        currentRow = gridRow.index - 1;
        from = parseInt(currentRow / 200, 10) * 200;
        return [from, currentRow];
      } else {
        return [null, null];
      }
    }

    function setSort(column,dir) {
      if (sortcol != column || sortdir != dir) {
        currentRequestVersionNumber++;
        sortcol = column;
        sortdir = dir;
      }
      refresh();
    }

    function setSortWithoutRefresh(column, dir) {
      if (sortcol != column || sortdir != dir) {
        currentRequestVersionNumber++;
        sortcol = column;
        sortdir = dir;
      }
    }

    function getSortColumn() {
      return sortcol;
    }

    function getSortDirection() {
      return sortdir;
    }

    function setFilter(filterFn) {
      if (filters != filterFn) {
        currentRequestVersionNumber++;
        filters = filterFn;
      }
      refresh();
    }

    function setFilterWithoutRefresh(filterFn) {
      if (filters != filterFn) {
        currentRequestVersionNumber++;
        filters = filterFn;
      }
    }

    function addFilterWithoutRefresh(column, string, operator) {
      if(typeof(operator)==='undefined') operator = 'equals';

      // If the string is an empty string and operator is 'equals', then removing the filter if existing
      if (string === '' && operator == 'equals') {
        var newFilters = [];
        $.each(filters, function(index,filter) {
          if (filter[0]!=column)
            newFilters.push(filter);
        });
        if (filters != newFilters) {
          currentRequestVersionNumber++;
          filters = newFilters;
        }
      } else {
        var updated = 0;
        // Try to update existing filter
        $.map(filters, function(filter) {
          if (filter[0]==column) {
            filter[1] = string;
            filter[2] = operator;
            updated = 1;
            return;
          }
        });
        // Add new filter
        if (updated === 0) {
          filters.push([column, string, operator]);
          currentRequestVersionNumber++;
        }
      }
    }

    function addFilter(column, string, operator) {
      addFilterWithoutRefresh(column, string, operator);
      refresh();
    }

    function addFiltersWithoutRefresh(filters) {
      $.each(filters, function(index, filter) {
        addFilterWithoutRefresh(filter[0], filter[1], filter[2]);
      });
    }

    function addFilters(filters) {
      addFiltersWithoutRefresh(filters);
      refresh();
    }

    function getFilters() {
      return filters;
    }

    function setParam(column, string, _refresh) {
      if (_refresh === undefined) _refresh = true;
      // If the string is an empty string, then removing the param if existing
      if (string === '') {
        var newParams = [];
        $.each(params, function(index,param) {
          if (param[0]!=column)
            newParams.push(param);
        });
        if (params != newParams) {
          currentRequestVersionNumber++;
          params = newParams;
        }
        if (_refresh) refresh(); // Only clear if it was found
        return;
      }

      var updated = 0;
      // Try to update existing param
      $.map(params, function(param) {
        if (param[0]==column) {
          param[1] = string;
          updated = 1;
          return;
        }
      });

      // Add new param
      if (updated === 0)
        params.push([column, string]);
        currentRequestVersionNumber++;

      if (_refresh) refresh();
    }

    function getParams() {
      return params;
    }

    function getParam(key) {
      var value = null;
      $.each(params, function(i) {
        if (params[i][0]==key) {
          value = params[i][1];
        }
      });
      return value;
    }

    function setLoadingIndicator(indicator) {
      loadingIndicator = indicator;
    }

    function setMainIndicator(indicator) {
      mainIndicator = indicator;
    }

    function refresh() {
      pagingOptionsChanged = true;
      clear();
      rowsChanged();
    }

    function setPagingOptions(args) {
      pagingOptionsChanged = false;

      if ((args.pageSize !== undefined) && (args.pageSize!=pageSize)) {
        currentRequestVersionNumber++;
        pageSize = args.pageSize;
        pagingOptionsChanged = true;
      }

      var newPageNum = Math.min(args.pageNum, Math.ceil(totalRows / pageSize));

      if ((args.pageNum !== undefined) && (pageNum!=newPageNum)) {
        currentRequestVersionNumber++;
        pageNum = newPageNum;
        pagingOptionsChanged = true;
      }

      // If we click "All" on pager, this is where we are
      if ((args.pageNum === undefined) && (args.pageSize === 0)) {
        currentRequestVersionNumber++;
        pageNum = 0;
        pageSize = 0;
        pagingOptionsChanged = false;
      }

      // Dirty fix for cases where the numbers don't add up
      if (totalRows / pageSize < pageNum) {
        currentRequestVersionNumber++;
        pageNum = 0;
        pagingOptionsChanged = true;
      }

      refresh();
      onPagingInfoChanged.notify(getPagingInfo());
    }

    function getPagingInfo() {
      // Ekohe Edit: Add rowsWithoutFilter
      // return {pageSize:pageSize, pageNum:pageNum, totalRows:totalRows};
      return {
        pageSize: pageSize,
        pageNum: pageNum,
        totalRows: totalRows,
        rowsWithoutFilter: rowsWithoutFilter
      };
    }

    return {
      // properties
      "data": data,
      "oldData": oldData,
      "connectionManager": connectionManager,
      "currentRequestVersionNumber": currentRequestVersionNumber,
      "lastRequestVersionNumber": lastRequestVersionNumber,

      // methods
      "clear": clear,
      "isDataLoaded": isDataLoaded,
      "ensureData": ensureData,
      "reloadData": reloadData,
      "setSort": setSort,
      "setSortWithoutRefresh": setSortWithoutRefresh,
      "getSortColumn": getSortColumn,
      "getSortDirection": getSortDirection,
      "getFilters": getFilters,
      "setFilterWithoutRefresh": setFilterWithoutRefresh,
      "setFilter": setFilter,
      "addFilterWithoutRefresh": addFilterWithoutRefresh,
      "addFilter": addFilter,
      "addFiltersWithoutRefresh": addFiltersWithoutRefresh,
      "addFilters": addFilters,
      "getParams": getParams,
      "getParam": getParam,
      "setParam": setParam,
      "setGrid": setGrid,
      "conditionalURI": conditionalURI,
      'getColumns': getColumns,

      // events
      "beforeRemoteRequest": beforeRemoteRequest,
      "onDataLoading": onDataLoading,
      "onPagingInfoChanged":  onPagingInfoChanged,
      "onDataLoaded": onDataLoaded,

      // pager
      "getPagingInfo": getPagingInfo,
      "setPagingOptions": setPagingOptions,

      "setLoadingIndicator": setLoadingIndicator,
      "setMainIndicator": setMainIndicator
    };
  }

  // WulinMaster.Data.RemoteModel
  $.extend(true, window, { WulinMaster: { Data: { RemoteModel: RemoteModel }}});
})(jQuery);
<|endoftext|>