<commit_before>var fs = require('fs');
var inquirer = require('inquirer');
var _ = require('underscore');
var M = require('mstring')

inquirer.prompt({
  type: 'list',
  name: 'category',
  message: '☂  Hackathon Starter',
  choices: ['Authentication', 'API Examples', 'Exit'],
  filter: function(val) {
    return val.toLowerCase();
  }
}, function(answer) {
  if (answer.category === 'authentication') {
    inquirer.prompt({
      type: 'checkbox',
      message: 'Select Authentication Providers:',
      name: 'auth',
      choices: [
        { name: 'Facebook', checked: true },
        { name: 'GitHub', checked: true },
        { name: 'Google', checked: true },
        { name: 'Twitter', checked: true },
        { name: 'Local', checked: true },
        { name: 'LinkedIn' },
        { name: 'Instagram' }
      ],
      validate: function(answer) {
        if (answer.length < 1) {
          return "You must choose at least one authentication method.";
        }
        return true;
      },
      filter: function(val) {
        return _.map(val, function(auth) {
          return auth.toLowerCase();
        });
      }
    }, function(answer) {
      var passportFile = 'config/passport.js';
      if (_.contains(answer.auth, 'facebook')) {
        var search = "var FacebookStrategy = require('passport-facebook').Strategy;";
        var strategy = M(function() {
          /***
          // Sign in with Facebook.

          passport.use(new FacebookStrategy(secrets.facebook, function(req, accessToken, refreshToken, profile, done) {
            if (req.user) {
              User.findOne({ $or: [{ facebook: profile.id }, { email: profile.email }] }, function(err, existingUser) {
                if (existingUser) {
                  req.flash('errors', { msg: 'There is already a Facebook account that belongs to you. Sign in with that account or delete it, then link it with your current account.' });
                  done(err);
                } else {
                  User.findById(req.user.id, function(err, user) {
                    user.facebook = profile.id;
                    user.tokens.push({ kind: 'facebook', accessToken: accessToken });
                    user.profile.name = user.profile.name || profile.displayName;
                    user.profile.gender = user.profile.gender || profile._json.gender;
                    user.profile.picture = user.profile.picture || 'https://graph.facebook.com/' + profile.id + '/picture?type=large';
                    user.save(function(err) {
                      req.flash('info', { msg: 'Facebook account has been linked.' });
                      done(err, user);
                    });
                  });
                }
              });
            } else {
              User.findOne({ facebook: profile.id }, function(err, existingUser) {
                if (existingUser) return done(null, existingUser);
                User.findOne({ email: profile._json.email }, function(err, existingEmailUser) {
                  if (existingEmailUser) {
                    req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with Facebook manually from Account Settings.' });
                    done(err);
                  } else {
                    var user = new User();
                    user.email = profile._json.email;
                    user.facebook = profile.id;
                    user.tokens.push({ kind: 'facebook', accessToken: accessToken });
                    user.profile.name = profile.displayName;
                    user.profile.gender = profile._json.gender;
                    user.profile.picture = 'https://graph.facebook.com/' + profile.id + '/picture?type=large';
                    user.profile.location = (profile._json.location) ? profile._json.location.name : '';
                    user.save(function(err) {
                      done(err, user);
                    });
                  }
                });
              });
            }
          }));

          ***/
        });
        var body = fs.readFileSync(passportFile).toString();
        if (body.indexOf(search) < 0) {
          body = body.split('\n');
          var idx = body.lastIndexOf("var passport = require('passport');");
          body.splice(idx + 1, 0, search);
          var idx2 = body.lastIndexOf("passport.deserializeUser(function(id, done) {");
          body.splice(idx2 + 6, 0, strategy);

          var output = body.join('\n');
          fs.writeFileSync(passportFile, output);
        }
      }
    });
  } else if (answer.category === 'api examples') {
    console.log('selected api examples');
  }
});
<commit_msg>Added Facebook auth remover<commit_after>var _ = require('underscore');
var colors = require('colors');
var fs = require('fs');
var inquirer = require('inquirer');
var M = require('mstring')

inquirer.prompt({
  type: 'list',
  name: 'category',
  message: 'Hackathon Starter Generator:',
  choices: ['Authentication', 'API Examples', 'Exit'],
  filter: function(val) {
    return val.toLowerCase();
  }
}, function(answer) {
  if (answer.category === 'authentication') {
    inquirer.prompt({
      type: 'checkbox',
      message: 'Select Authentication Providers:',
      name: 'auth',
      choices: [
        { name: 'Facebook', checked: true },
        { name: 'GitHub', checked: true },
        { name: 'Google', checked: true },
        { name: 'Twitter', checked: true },
        { name: 'Local', checked: true },
        { name: 'LinkedIn' },
        { name: 'Instagram' }
      ],
      validate: function(answer) {
        if (answer.length < 1) {
          return "You must choose at least one authentication method.";
        }
        return true;
      },
      filter: function(val) {
        return _.map(val, function(auth) {
          return auth.toLowerCase();
        });
      }
    }, function(answer) {
      var passportConfig = 'config/passport.js';
      var userModel  = 'models/User.js';
      var profileTemplate = 'views/account/profile.jade';
      var loginTemplate = 'views/account/login.jade';

      var facebookStrategyRequire = "var FacebookStrategy = require('passport-facebook').Strategy;";
      var facebookStrategy = M(function() {
        /***
         // Sign in with Facebook.

         passport.use(new FacebookStrategy(secrets.facebook, function(req, accessToken, refreshToken, profile, done) {
            if (req.user) {
              User.findOne({ $or: [{ facebook: profile.id }, { email: profile.email }] }, function(err, existingUser) {
                if (existingUser) {
                  req.flash('errors', { msg: 'There is already a Facebook account that belongs to you. Sign in with that account or delete it, then link it with your current account.' });
                  done(err);
                } else {
                  User.findById(req.user.id, function(err, user) {
                    user.facebook = profile.id;
                    user.tokens.push({ kind: 'facebook', accessToken: accessToken });
                    user.profile.name = user.profile.name || profile.displayName;
                    user.profile.gender = user.profile.gender || profile._json.gender;
                    user.profile.picture = user.profile.picture || 'https://graph.facebook.com/' + profile.id + '/picture?type=large';
                    user.save(function(err) {
                      req.flash('info', { msg: 'Facebook account has been linked.' });
                      done(err, user);
                    });
                  });
                }
              });
            } else {
              User.findOne({ facebook: profile.id }, function(err, existingUser) {
                if (existingUser) return done(null, existingUser);
                User.findOne({ email: profile._json.email }, function(err, existingEmailUser) {
                  if (existingEmailUser) {
                    req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with Facebook manually from Account Settings.' });
                    done(err);
                  } else {
                    var user = new User();
                    user.email = profile._json.email;
                    user.facebook = profile.id;
                    user.tokens.push({ kind: 'facebook', accessToken: accessToken });
                    user.profile.name = profile.displayName;
                    user.profile.gender = profile._json.gender;
                    user.profile.picture = 'https://graph.facebook.com/' + profile.id + '/picture?type=large';
                    user.profile.location = (profile._json.location) ? profile._json.location.name : '';
                    user.save(function(err) {
                      done(err, user);
                    });
                  }
                });
              });
            }
          }));

         ***/
      });

      var body = fs.readFileSync(passportConfig).toString();

      if (_.contains(answer.auth, 'facebook')) {

        if (body.indexOf(facebookStrategyRequire) < 0) {
          body = body.split('\n');
          var idx = body.lastIndexOf("var passport = require('passport');");
          body.splice(idx + 1, 0, facebookStrategyRequire);
          var idx2 = body.lastIndexOf("passport.deserializeUser(function(id, done) {");
          body.splice(idx2 + 6, 0, facebookStrategy);
          var output = body.join('\n');
          fs.writeFileSync(passportConfig, output);
          console.log('Facebook authentication has been added.');
        } else {
          console.log('Facebook authentication is already active.');
        }
      } else {
        // If unchecked, delete Facebook auth completely.
        body = body.split('\n');
        var idx = body.lastIndexOf(facebookStrategyRequire);
        body.splice(idx, 1);
        var idx2 = body.lastIndexOf('// Sign in with Facebook.');
        body.splice(idx2, 47);
        var output = body.join('\n');
        fs.writeFileSync(passportConfig, output);
        console.log('Facebook authentication has been deleted.');
      }
    });
  } else if (answer.category === 'api examples') {
    console.log('selected api examples');
  }
});
<|endoftext|>


<commit_before>/**
 * @class crm.Views.Offline.List
 *
 * @extends argos._ListBase
 * @requires argos._ListBase
 *
 *
 */
import declare from 'dojo/_base/declare';
import _ListBase from 'argos/_ListBase';
import _CardLayoutListMixin from '../_CardLayoutListMixin';
import _OfflineRightDrawerListMixin from './_OfflineRightDrawerListMixin';
import _MetricListMixin from '../_MetricListMixin';
import TotalMetricWidget from './TotalMetricWidget';
import Store from 'argos/Store/PouchDB';

export default declare('crm.Views.Offline.List', [_ListBase, _OfflineRightDrawerListMixin, _MetricListMixin, _CardLayoutListMixin], {
  id: 'offline_list',
  idProperty: 'id',
  detailView: 'offline_detail',
  offlineSupport: true,
  enableSearch: false,
  enableActions: true,
  resourceKind: 'offline',

  titleText: 'Recently Viewed',

  OFFLINE_DB_NAME: 'crm-offline',
  metricWidgetCtor: TotalMetricWidget,

  itemTemplate: new Simplate([
    '
{%: $$.getTitle($) %}
',
  ]),
  getTitle: function getTitle(entry) {
    return entry && entry.doc && entry.doc.entity && entry.doc.entity.$descriptor;
  },
  createStore: function createStore() {
    return new Store({
      databaseName: this.OFFLINE_DB_NAME,
    });
  },
  getActiveEntityFilters: function getActiveEntityFilters() {
    return Object.keys(this.entityMappings)
      .map((entityName) => {
        const prefs = App.preferences && App.preferences.offlineEntityFilters || [];
        const entityPref = prefs.filter((pref) => {
          return pref.name === entityName;
        });
        return entityPref[0];
      })
      .filter((f) => f && f.enabled);
  },
  _buildQueryExpression: function _buildQueryExpression() {
    const filters = this.getActiveEntityFilters();
    return function queryFn(doc, emit) {
      // If the user has entity filters stored in preferences, filter based on that
      if (App.preferences && App.preferences.offlineEntityFilters) {
        filters.forEach((f) => {
          if (doc.entityName === f.name) {
            emit(doc);
          }
        });
      } else {
        // User has no entity filter preferences (from right drawer)
        emit(doc);
      }
    };
  },
  _hasValidOptions: function _hasValidOptions(options) {
    return options;
  },
  _applyStateToWidgetOptions: function _applyStateToWidgetOptions(widgetOptions) {
    const options = widgetOptions;
    options.OFFLINE_DB_NAME = this.OFFLINE_DB_NAME;
    options.activeEntityFilters = this.getActiveEntityFilters();
    return options;
  },
  _applyStateToQueryOptions: function _applyStateToQueryOptions(queryOptions) {
    delete queryOptions.count;
    delete queryOptions.start;
    queryOptions.include_docs = true;
    return queryOptions;
  },
  createIndicatorLayout: function createIndicatorLayout() {
    return [];
  },
  getItemIconClass: function getItemIconClass(entry) {
    const {
      entityName,
    } = entry.doc;
    const mapping = this.entityMappings[entityName];
    const {
      iconClass,
    } = mapping;
    let results = '';
    if (iconClass) {
      results = `fa ${iconClass} fa-2x`;
    }
    return results;
  },
  // Localization
  entityText: {
    'Contact': 'Contacts',
    'Account': 'Accounts',
    'Opportunity': 'Opportunities',
    'Ticket': 'Tickets',
    'Lead': 'Leads',
    'Activity': 'Activities',
    'History': 'History',
  },
  entityMappings: {
    'Contact': {
      iconClass: 'fa-user',
    },
    'Account': {
      iconClass: 'fa-building-o',
    },
    'Opportunity': {
      iconClass: 'fa-money',
    },
    'Ticket': {
      iconClass: 'fa-clipboard',
    },
    'Lead': {
      iconClass: 'fa-filter',
    },
    'Activity': {
      iconClass: 'fa-calendar-o',
    },
    'History': {
      iconClass: 'fa-history',
    },
  },
});
<commit_msg>Recently viewed now sorting correctly.<commit_after>/**
 * @class crm.Views.Offline.List
 *
 * @extends argos._ListBase
 * @requires argos._ListBase
 *
 *
 */
import declare from 'dojo/_base/declare';
import _ListBase from 'argos/_ListBase';
import _CardLayoutListMixin from '../_CardLayoutListMixin';
import _OfflineRightDrawerListMixin from './_OfflineRightDrawerListMixin';
import _MetricListMixin from '../_MetricListMixin';
import TotalMetricWidget from './TotalMetricWidget';
import Store from 'argos/Store/PouchDB';

export default declare('crm.Views.Offline.List', [_ListBase, _OfflineRightDrawerListMixin, _MetricListMixin, _CardLayoutListMixin], {
  id: 'offline_list',
  idProperty: 'id',
  detailView: 'offline_detail',
  offlineSupport: true,
  enableSearch: false,
  enableActions: true,
  resourceKind: 'offline',

  titleText: 'Recently Viewed',

  OFFLINE_DB_NAME: 'crm-offline',
  metricWidgetCtor: TotalMetricWidget,

  itemTemplate: new Simplate([
    '
{%: $$.getTitle($) %}
',
  ]),
  getTitle: function getTitle(entry) {
    return entry && entry.doc && entry.doc.entity && entry.doc.entity.$descriptor;
  },
  createStore: function createStore() {
    return new Store({
      databaseName: this.OFFLINE_DB_NAME,
    });
  },
  getActiveEntityFilters: function getActiveEntityFilters() {
    return Object.keys(this.entityMappings)
      .map((entityName) => {
        const prefs = App.preferences && App.preferences.offlineEntityFilters || [];
        const entityPref = prefs.filter((pref) => {
          return pref.name === entityName;
        });
        return entityPref[0];
      })
      .filter((f) => f && f.enabled);
  },
  _buildQueryExpression: function _buildQueryExpression() {
    const filters = this.getActiveEntityFilters();
    return function queryFn(doc, emit) {
      // If the user has entity filters stored in preferences, filter based on that
      if (App.preferences && App.preferences.offlineEntityFilters) {
        filters.forEach((f) => {
          if (doc.entityName === f.name) {
            emit(doc.createDate);
          }
        });
      } else {
        // User has no entity filter preferences (from right drawer)
        emit(doc.createDate);
      }
    };
  },
  _hasValidOptions: function _hasValidOptions(options) {
    return options;
  },
  _applyStateToWidgetOptions: function _applyStateToWidgetOptions(widgetOptions) {
    const options = widgetOptions;
    options.OFFLINE_DB_NAME = this.OFFLINE_DB_NAME;
    options.activeEntityFilters = this.getActiveEntityFilters();
    return options;
  },
  _applyStateToQueryOptions: function _applyStateToQueryOptions(queryOptions) {
    delete queryOptions.count;
    delete queryOptions.start;
    queryOptions.include_docs = true;
    queryOptions.descending = true;
    return queryOptions;
  },
  createIndicatorLayout: function createIndicatorLayout() {
    return [];
  },
  getItemIconClass: function getItemIconClass(entry) {
    const {
      entityName,
    } = entry.doc;
    const mapping = this.entityMappings[entityName];
    const {
      iconClass,
    } = mapping;
    let results = '';
    if (iconClass) {
      results = `fa ${iconClass} fa-2x`;
    }
    return results;
  },
  // Localization
  entityText: {
    'Contact': 'Contacts',
    'Account': 'Accounts',
    'Opportunity': 'Opportunities',
    'Ticket': 'Tickets',
    'Lead': 'Leads',
    'Activity': 'Activities',
    'History': 'History',
  },
  entityMappings: {
    'Contact': {
      iconClass: 'fa-user',
    },
    'Account': {
      iconClass: 'fa-building-o',
    },
    'Opportunity': {
      iconClass: 'fa-money',
    },
    'Ticket': {
      iconClass: 'fa-clipboard',
    },
    'Lead': {
      iconClass: 'fa-filter',
    },
    'Activity': {
      iconClass: 'fa-calendar-o',
    },
    'History': {
      iconClass: 'fa-history',
    },
  },
});
<|endoftext|>


<commit_before>/*
 * This implements link hinting. Typing "F" will enter link-hinting mode, where all clickable items on
 * the page have a hint marker displayed containing a sequence of letters. Typing those letters will select
 * a link.
 *
 * The characters we use to show link hints are a user-configurable option. By default they're the home row.
 * The CSS which is used on the link hints is also a configurable option.
 */

var hintMarkers = [];
var hintMarkerContainingDiv = null;
// The characters that were typed in while in "link hints" mode.
var hintKeystrokeQueue = [];
var linkHintsModeActivated = false;
var shouldOpenLinkHintInNewTab = false;
var shouldOpenLinkHintWithQueue = false;
// Whether link hint's "open in current/new tab" setting is currently toggled
var openLinkModeToggle = false;
// Whether we have added to the page the CSS needed to display link hints.
var linkHintsCssAdded = false;

/*
 * Generate an XPath describing what a clickable element is.
 * The final expression will be something like "//button | //xhtml:button | ..."
 */
var clickableElementsXPath = (function() {
  var clickableElements = ["a", "textarea", "button", "select", "input[not(@type='hidden')]",
                           "*[@onclick or @tabindex or @role='link' or @role='button']"];
  var xpath = [];
  for (var i in clickableElements)
    xpath.push("//" + clickableElements[i], "//xhtml:" + clickableElements[i]);
  return xpath.join(" | ")
})();

// We need this as a top-level function because our command system doesn't yet support arguments.
function activateLinkHintsModeToOpenInNewTab() { activateLinkHintsMode(true, false); }

function activateLinkHintsModeWithQueue() { activateLinkHintsMode(true, true); }

function activateLinkHintsMode(openInNewTab, withQueue) {
  if (!linkHintsCssAdded)
    addCssToPage(linkHintCss); // linkHintCss is declared by vimiumFrontend.js
  linkHintCssAdded = true;
  linkHintsModeActivated = true;
  setOpenLinkMode(openInNewTab, withQueue);
  buildLinkHints();
  document.addEventListener("keydown", onKeyDownInLinkHintsMode, true);
  document.addEventListener("keyup", onKeyUpInLinkHintsMode, true);
}

function setOpenLinkMode(openInNewTab, withQueue) {
  shouldOpenLinkHintInNewTab = openInNewTab;
  shouldOpenLinkHintWithQueue = withQueue;
  if (shouldOpenLinkHintWithQueue) {
    HUD.show("Open multiple links in a new tab");
  } else {
    if (shouldOpenLinkHintInNewTab)
      HUD.show("Open link in new tab");
    else
      HUD.show("Open link in current tab");
  }
}

/*
 * Builds and displays link hints for every visible clickable item on the page.
 */
function buildLinkHints() {
  var visibleElements = getVisibleClickableElements();

  // Initialize the number used to generate the character hints to be as many digits as we need to
  // highlight all the links on the page; we don't want some link hints to have more chars than others.
  var digitsNeeded = Math.ceil(logXOfBase(visibleElements.length, settings.linkHintCharacters.length));
  var linkHintNumber = 0;
  for (var i = 0, count = visibleElements.length; i < count; i++) {
    hintMarkers.push(createMarkerFor(visibleElements[i], linkHintNumber, digitsNeeded));
    linkHintNumber++;
  }
  // Note(philc): Append these markers as top level children instead of as child nodes to the link itself,
  // because some clickable elements cannot contain children, e.g. submit buttons. This has the caveat
  // that if you scroll the page and the link has position=fixed, the marker will not stay fixed.
  // Also note that adding these nodes to document.body all at once is significantly faster than one-by-one.
  hintMarkerContainingDiv = document.createElement("div");
  hintMarkerContainingDiv.className = "internalVimiumHintMarker";
  for (var i = 0; i < hintMarkers.length; i++)
    hintMarkerContainingDiv.appendChild(hintMarkers[i]);
  document.documentElement.appendChild(hintMarkerContainingDiv);
}

function logXOfBase(x, base) { return Math.log(x) / Math.log(base); }

/*
 * Returns all clickable elements that are not hidden and are in the current viewport.
 * We prune invisible elements partly for performance reasons, but moreso it's to decrease the number
 * of digits needed to enumerate all of the links on screen.
 */
function getVisibleClickableElements() {
  var resultSet = document.evaluate(clickableElementsXPath, document.body,
    function (namespace) {
      return namespace == "xhtml" ? "http://www.w3.org/1999/xhtml" : null;
    },
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

  var visibleElements = [];

  // Find all visible clickable elements.
  for (var i = 0, count = resultSet.snapshotLength; i < count; i++) {
    var element = resultSet.snapshotItem(i);
    var clientRect = element.getClientRects()[0];

    if (isVisible(element, clientRect))
      visibleElements.push({element: element, rect: clientRect});

    // If the link has zero dimensions, it may be wrapping visible
    // but floated elements. Check for this.
    if (clientRect && (clientRect.width == 0 || clientRect.height == 0)) {
      for (var j = 0, childrenCount = element.children.length; j < childrenCount; j++) {
        if (window.getComputedStyle(element.children[j], null).getPropertyValue('float') != 'none') {
          var childClientRect = element.children[j].getClientRects()[0];
          if (isVisible(element.children[j], childClientRect)) {
            visibleElements.push({element: element.children[j], rect: childClientRect});
            break;
          }
        }
      }
    }
  }
  return visibleElements;
}

/*
 * Returns true if element is visible.
 */
function isVisible(element, clientRect) {
  // Exclude links which have just a few pixels on screen, because the link hints won't show for them anyway.
  var zoomFactor = currentZoomLevel / 100.0;
  if (!clientRect || clientRect.top < 0 || clientRect.top * zoomFactor >= window.innerHeight - 4 ||
      clientRect.left < 0 || clientRect.left * zoomFactor >= window.innerWidth - 4)
    return false;

  if (clientRect.width < 3 || clientRect.height < 3)
    return false;

  // eliminate invisible elements (see test_harnesses/visibility_test.html)
  var computedStyle = window.getComputedStyle(element, null);
  if (computedStyle.getPropertyValue('visibility') != 'visible' ||
      computedStyle.getPropertyValue('display') == 'none')
    return false;

  return true;
}

function onKeyDownInLinkHintsMode(event) {
  console.log("Key Down");
  if (event.keyCode == keyCodes.shiftKey && !openLinkModeToggle) {
    // Toggle whether to open link in a new or current tab.
    setOpenLinkMode(!shouldOpenLinkHintInNewTab, shouldOpenLinkHintWithQueue);
    openLinkModeToggle = true;
  }

  var keyChar = getKeyChar(event);
  if (!keyChar)
    return;

  // TODO(philc): Ignore keys that have modifiers.
  if (isEscape(event)) {
    deactivateLinkHintsMode();
  } else if (event.keyCode == keyCodes.backspace || event.keyCode == keyCodes.deleteKey) {
    if (hintKeystrokeQueue.length == 0) {
      deactivateLinkHintsMode();
    } else {
      hintKeystrokeQueue.pop();
      updateLinkHints();
    }
  } else if (settings.linkHintCharacters.indexOf(keyChar) >= 0) {
    hintKeystrokeQueue.push(keyChar);
    updateLinkHints();
  } else {
    return;
  }

  event.stopPropagation();
  event.preventDefault();
}

function onKeyUpInLinkHintsMode(event) {
  if (event.keyCode == keyCodes.shiftKey && openLinkModeToggle) {
    // Revert toggle on whether to open link in new or current tab.
    setOpenLinkMode(!shouldOpenLinkHintInNewTab, shouldOpenLinkHintWithQueue);
    openLinkModeToggle = false;
  }
  event.stopPropagation();
  event.preventDefault();
}

/*
 * Updates the visibility of link hints on screen based on the keystrokes typed thus far. If only one
 * link hint remains, click on that link and exit link hints mode.
 */
function updateLinkHints() {
  var matchString = hintKeystrokeQueue.join("");
  var linksMatched = highlightLinkMatches(matchString);
  if (linksMatched.length == 0)
    deactivateLinkHintsMode();
  else if (linksMatched.length == 1) {
    var matchedLink = linksMatched[0];
    if (isSelectable(matchedLink)) {
      matchedLink.focus();
      // When focusing a textbox, put the selection caret at the end of the textbox's contents.
      matchedLink.setSelectionRange(matchedLink.value.length, matchedLink.value.length);
      deactivateLinkHintsMode();
    } else {
      // When we're opening the link in the current tab, don't navigate to the selected link immediately;
      // we want to give the user some feedback depicting which link they've selected by focusing it.
      if (shouldOpenLinkHintWithQueue) {
        simulateClick(matchedLink);
        resetLinkHintsMode();
      } else if (shouldOpenLinkHintInNewTab) {
        simulateClick(matchedLink);
        matchedLink.focus();
        deactivateLinkHintsMode();
      } else {
        setTimeout(function() { simulateClick(matchedLink); }, 400);
        matchedLink.focus();
        deactivateLinkHintsMode();
      }
    }
  }
}

/*
 * Selectable means the element has a text caret; this is not the same as "focusable".
 */
function isSelectable(element) {
  var selectableTypes = ["search", "text", "password"];
  return (element.tagName == "INPUT" && selectableTypes.indexOf(element.type) >= 0) ||
      element.tagName == "TEXTAREA";
}

/*
 * Hides link hints which do not match the given search string. To allow the backspace key to work, this
 * will also show link hints which do match but were previously hidden.
 */
function highlightLinkMatches(searchString) {
  var linksMatched = [];
  for (var i = 0; i < hintMarkers.length; i++) {
    var linkMarker = hintMarkers[i];
    if (linkMarker.getAttribute("hintString").indexOf(searchString) == 0) {
      if (linkMarker.style.display == "none")
        linkMarker.style.display = "";
      var childNodes = linkMarker.childNodes;
      for (var j = 0, childNodesCount = childNodes.length; j < childNodesCount; j++)
        childNodes[j].className = (j >= searchString.length) ? "" : "matchingCharacter";
      linksMatched.push(linkMarker.clickableItem);
    } else {
      linkMarker.style.display = "none";
    }
  }
  return linksMatched;
}

/*
 * Converts a number like "8" into a hint string like "JK". This is used to sequentially generate all of
 * the hint text. The hint string will be "padded with zeroes" to ensure its length is equal to numHintDigits.
 */
function numberToHintString(number, numHintDigits) {
  var base = settings.linkHintCharacters.length;
  var hintString = [];
  var remainder = 0;
  do {
    remainder = number % base;
    hintString.unshift(settings.linkHintCharacters[remainder]);
    number -= remainder;
    number /= Math.floor(base);
  } while (number > 0);

  // Pad the hint string we're returning so that it matches numHintDigits.
  for (var i = 0; i < numHintDigits - hintString.length; i++)
    hintString.unshift(settings.linkHintCharacters[0]);
  return hintString.join("");
}

function simulateClick(link) {
  var event = document.createEvent("MouseEvents");
  // When "clicking" on a link, dispatch the event with the appropriate meta key (CMD on Mac, CTRL on windows)
  // to open it in a new tab if necessary.
  var metaKey = (platform == "Mac" && shouldOpenLinkHintInNewTab);
  var ctrlKey = (platform != "Mac" && shouldOpenLinkHintInNewTab);
  event.initMouseEvent("click", true, true, window, 1, 0, 0, 0, 0, ctrlKey, false, false, metaKey, 0, null);

  // Debugging note: Firefox will not execute the link's default action if we dispatch this click event,
  // but Webkit will. Dispatching a click on an input box does not seem to focus it; we do that separately
  link.dispatchEvent(event);
}

function deactivateLinkHintsMode() {
  if (hintMarkerContainingDiv)
    hintMarkerContainingDiv.parentNode.removeChild(hintMarkerContainingDiv);
  hintMarkerContainingDiv = null;
  hintMarkers = [];
  hintKeystrokeQueue = [];
  document.removeEventListener("keydown", onKeyDownInLinkHintsMode, true);
  document.removeEventListener("keyup", onKeyUpInLinkHintsMode, true);
  linkHintsModeActivated = false;
  HUD.hide();
}

function resetLinkHintsMode() {
  deactivateLinkHintsMode();
  activateLinkHintsModeWithQueue();
}

/*
 * Creates a link marker for the given link.
 */
function createMarkerFor(link, linkHintNumber, linkHintDigits) {
  var hintString = numberToHintString(linkHintNumber, linkHintDigits);
  var marker = document.createElement("div");
  marker.className = "internalVimiumHintMarker vimiumHintMarker";
  var innerHTML = [];
  // Make each hint character a span, so that we can highlight the typed characters as you type them.
  for (var i = 0; i < hintString.length; i++)
    innerHTML.push("<span>" + hintString[i].toUpperCase() + "</span>");
  marker.innerHTML = innerHTML.join("");
  marker.setAttribute("hintString", hintString);

  // Note: this call will be expensive if we modify the DOM in between calls.
  var clientRect = link.rect;
  // The coordinates given by the window do not have the zoom factor included since the zoom is set only on
  // the document node.
  var zoomFactor = currentZoomLevel / 100.0;
  marker.style.left = clientRect.left + window.scrollX / zoomFactor + "px";
  marker.style.top = clientRect.top  + window.scrollY / zoomFactor + "px";

  marker.clickableItem = link.element;
  return marker;
}
<commit_msg>Fix a crucial bug with insufficient padding of link hints.<commit_after>/*
 * This implements link hinting. Typing "F" will enter link-hinting mode, where all clickable items on
 * the page have a hint marker displayed containing a sequence of letters. Typing those letters will select
 * a link.
 *
 * The characters we use to show link hints are a user-configurable option. By default they're the home row.
 * The CSS which is used on the link hints is also a configurable option.
 */

var hintMarkers = [];
var hintMarkerContainingDiv = null;
// The characters that were typed in while in "link hints" mode.
var hintKeystrokeQueue = [];
var linkHintsModeActivated = false;
var shouldOpenLinkHintInNewTab = false;
var shouldOpenLinkHintWithQueue = false;
// Whether link hint's "open in current/new tab" setting is currently toggled
var openLinkModeToggle = false;
// Whether we have added to the page the CSS needed to display link hints.
var linkHintsCssAdded = false;

/*
 * Generate an XPath describing what a clickable element is.
 * The final expression will be something like "//button | //xhtml:button | ..."
 */
var clickableElementsXPath = (function() {
  var clickableElements = ["a", "textarea", "button", "select", "input[not(@type='hidden')]",
                           "*[@onclick or @tabindex or @role='link' or @role='button']"];
  var xpath = [];
  for (var i in clickableElements)
    xpath.push("//" + clickableElements[i], "//xhtml:" + clickableElements[i]);
  return xpath.join(" | ")
})();

// We need this as a top-level function because our command system doesn't yet support arguments.
function activateLinkHintsModeToOpenInNewTab() { activateLinkHintsMode(true, false); }

function activateLinkHintsModeWithQueue() { activateLinkHintsMode(true, true); }

function activateLinkHintsMode(openInNewTab, withQueue) {
  if (!linkHintsCssAdded)
    addCssToPage(linkHintCss); // linkHintCss is declared by vimiumFrontend.js
  linkHintCssAdded = true;
  linkHintsModeActivated = true;
  setOpenLinkMode(openInNewTab, withQueue);
  buildLinkHints();
  document.addEventListener("keydown", onKeyDownInLinkHintsMode, true);
  document.addEventListener("keyup", onKeyUpInLinkHintsMode, true);
}

function setOpenLinkMode(openInNewTab, withQueue) {
  shouldOpenLinkHintInNewTab = openInNewTab;
  shouldOpenLinkHintWithQueue = withQueue;
  if (shouldOpenLinkHintWithQueue) {
    HUD.show("Open multiple links in a new tab");
  } else {
    if (shouldOpenLinkHintInNewTab)
      HUD.show("Open link in new tab");
    else
      HUD.show("Open link in current tab");
  }
}

/*
 * Builds and displays link hints for every visible clickable item on the page.
 */
function buildLinkHints() {
  var visibleElements = getVisibleClickableElements();

  // Initialize the number used to generate the character hints to be as many digits as we need to
  // highlight all the links on the page; we don't want some link hints to have more chars than others.
  var digitsNeeded = Math.ceil(logXOfBase(visibleElements.length, settings.linkHintCharacters.length));
  var linkHintNumber = 0;
  for (var i = 0, count = visibleElements.length; i < count; i++) {
    hintMarkers.push(createMarkerFor(visibleElements[i], linkHintNumber, digitsNeeded));
    linkHintNumber++;
  }
  // Note(philc): Append these markers as top level children instead of as child nodes to the link itself,
  // because some clickable elements cannot contain children, e.g. submit buttons. This has the caveat
  // that if you scroll the page and the link has position=fixed, the marker will not stay fixed.
  // Also note that adding these nodes to document.body all at once is significantly faster than one-by-one.
  hintMarkerContainingDiv = document.createElement("div");
  hintMarkerContainingDiv.className = "internalVimiumHintMarker";
  for (var i = 0; i < hintMarkers.length; i++)
    hintMarkerContainingDiv.appendChild(hintMarkers[i]);
  document.documentElement.appendChild(hintMarkerContainingDiv);
}

function logXOfBase(x, base) { return Math.log(x) / Math.log(base); }

/*
 * Returns all clickable elements that are not hidden and are in the current viewport.
 * We prune invisible elements partly for performance reasons, but moreso it's to decrease the number
 * of digits needed to enumerate all of the links on screen.
 */
function getVisibleClickableElements() {
  var resultSet = document.evaluate(clickableElementsXPath, document.body,
    function (namespace) {
      return namespace == "xhtml" ? "http://www.w3.org/1999/xhtml" : null;
    },
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

  var visibleElements = [];

  // Find all visible clickable elements.
  for (var i = 0, count = resultSet.snapshotLength; i < count; i++) {
    var element = resultSet.snapshotItem(i);
    var clientRect = element.getClientRects()[0];

    if (isVisible(element, clientRect))
      visibleElements.push({element: element, rect: clientRect});

    // If the link has zero dimensions, it may be wrapping visible
    // but floated elements. Check for this.
    if (clientRect && (clientRect.width == 0 || clientRect.height == 0)) {
      for (var j = 0, childrenCount = element.children.length; j < childrenCount; j++) {
        if (window.getComputedStyle(element.children[j], null).getPropertyValue('float') != 'none') {
          var childClientRect = element.children[j].getClientRects()[0];
          if (isVisible(element.children[j], childClientRect)) {
            visibleElements.push({element: element.children[j], rect: childClientRect});
            break;
          }
        }
      }
    }
  }
  return visibleElements;
}

/*
 * Returns true if element is visible.
 */
function isVisible(element, clientRect) {
  // Exclude links which have just a few pixels on screen, because the link hints won't show for them anyway.
  var zoomFactor = currentZoomLevel / 100.0;
  if (!clientRect || clientRect.top < 0 || clientRect.top * zoomFactor >= window.innerHeight - 4 ||
      clientRect.left < 0 || clientRect.left * zoomFactor >= window.innerWidth - 4)
    return false;

  if (clientRect.width < 3 || clientRect.height < 3)
    return false;

  // eliminate invisible elements (see test_harnesses/visibility_test.html)
  var computedStyle = window.getComputedStyle(element, null);
  if (computedStyle.getPropertyValue('visibility') != 'visible' ||
      computedStyle.getPropertyValue('display') == 'none')
    return false;

  return true;
}

function onKeyDownInLinkHintsMode(event) {
  console.log("Key Down");
  if (event.keyCode == keyCodes.shiftKey && !openLinkModeToggle) {
    // Toggle whether to open link in a new or current tab.
    setOpenLinkMode(!shouldOpenLinkHintInNewTab, shouldOpenLinkHintWithQueue);
    openLinkModeToggle = true;
  }

  var keyChar = getKeyChar(event);
  if (!keyChar)
    return;

  // TODO(philc): Ignore keys that have modifiers.
  if (isEscape(event)) {
    deactivateLinkHintsMode();
  } else if (event.keyCode == keyCodes.backspace || event.keyCode == keyCodes.deleteKey) {
    if (hintKeystrokeQueue.length == 0) {
      deactivateLinkHintsMode();
    } else {
      hintKeystrokeQueue.pop();
      updateLinkHints();
    }
  } else if (settings.linkHintCharacters.indexOf(keyChar) >= 0) {
    hintKeystrokeQueue.push(keyChar);
    updateLinkHints();
  } else {
    return;
  }

  event.stopPropagation();
  event.preventDefault();
}

function onKeyUpInLinkHintsMode(event) {
  if (event.keyCode == keyCodes.shiftKey && openLinkModeToggle) {
    // Revert toggle on whether to open link in new or current tab.
    setOpenLinkMode(!shouldOpenLinkHintInNewTab, shouldOpenLinkHintWithQueue);
    openLinkModeToggle = false;
  }
  event.stopPropagation();
  event.preventDefault();
}

/*
 * Updates the visibility of link hints on screen based on the keystrokes typed thus far. If only one
 * link hint remains, click on that link and exit link hints mode.
 */
function updateLinkHints() {
  var matchString = hintKeystrokeQueue.join("");
  var linksMatched = highlightLinkMatches(matchString);
  if (linksMatched.length == 0)
    deactivateLinkHintsMode();
  else if (linksMatched.length == 1) {
    var matchedLink = linksMatched[0];
    if (isSelectable(matchedLink)) {
      matchedLink.focus();
      // When focusing a textbox, put the selection caret at the end of the textbox's contents.
      matchedLink.setSelectionRange(matchedLink.value.length, matchedLink.value.length);
      deactivateLinkHintsMode();
    } else {
      // When we're opening the link in the current tab, don't navigate to the selected link immediately;
      // we want to give the user some feedback depicting which link they've selected by focusing it.
      if (shouldOpenLinkHintWithQueue) {
        simulateClick(matchedLink);
        resetLinkHintsMode();
      } else if (shouldOpenLinkHintInNewTab) {
        simulateClick(matchedLink);
        matchedLink.focus();
        deactivateLinkHintsMode();
      } else {
        setTimeout(function() { simulateClick(matchedLink); }, 400);
        matchedLink.focus();
        deactivateLinkHintsMode();
      }
    }
  }
}

/*
 * Selectable means the element has a text caret; this is not the same as "focusable".
 */
function isSelectable(element) {
  var selectableTypes = ["search", "text", "password"];
  return (element.tagName == "INPUT" && selectableTypes.indexOf(element.type) >= 0) ||
      element.tagName == "TEXTAREA";
}

/*
 * Hides link hints which do not match the given search string. To allow the backspace key to work, this
 * will also show link hints which do match but were previously hidden.
 */
function highlightLinkMatches(searchString) {
  var linksMatched = [];
  for (var i = 0; i < hintMarkers.length; i++) {
    var linkMarker = hintMarkers[i];
    if (linkMarker.getAttribute("hintString").indexOf(searchString) == 0) {
      if (linkMarker.style.display == "none")
        linkMarker.style.display = "";
      var childNodes = linkMarker.childNodes;
      for (var j = 0, childNodesCount = childNodes.length; j < childNodesCount; j++)
        childNodes[j].className = (j >= searchString.length) ? "" : "matchingCharacter";
      linksMatched.push(linkMarker.clickableItem);
    } else {
      linkMarker.style.display = "none";
    }
  }
  return linksMatched;
}

/*
 * Converts a number like "8" into a hint string like "JK". This is used to sequentially generate all of
 * the hint text. The hint string will be "padded with zeroes" to ensure its length is equal to numHintDigits.
 */
function numberToHintString(number, numHintDigits) {
  var base = settings.linkHintCharacters.length;
  var hintString = [];
  var remainder = 0;
  do {
    remainder = number % base;
    hintString.unshift(settings.linkHintCharacters[remainder]);
    number -= remainder;
    number /= Math.floor(base);
  } while (number > 0);

  // Pad the hint string we're returning so that it matches numHintDigits.
  for (var i = 0, count = numHintDigits - hintString.length; i < count; i++)
    hintString.unshift(settings.linkHintCharacters[0]);
  return hintString.join("");
}

function simulateClick(link) {
  var event = document.createEvent("MouseEvents");
  // When "clicking" on a link, dispatch the event with the appropriate meta key (CMD on Mac, CTRL on windows)
  // to open it in a new tab if necessary.
  var metaKey = (platform == "Mac" && shouldOpenLinkHintInNewTab);
  var ctrlKey = (platform != "Mac" && shouldOpenLinkHintInNewTab);
  event.initMouseEvent("click", true, true, window, 1, 0, 0, 0, 0, ctrlKey, false, false, metaKey, 0, null);

  // Debugging note: Firefox will not execute the link's default action if we dispatch this click event,
  // but Webkit will. Dispatching a click on an input box does not seem to focus it; we do that separately
  link.dispatchEvent(event);
}

function deactivateLinkHintsMode() {
  if (hintMarkerContainingDiv)
    hintMarkerContainingDiv.parentNode.removeChild(hintMarkerContainingDiv);
  hintMarkerContainingDiv = null;
  hintMarkers = [];
  hintKeystrokeQueue = [];
  document.removeEventListener("keydown", onKeyDownInLinkHintsMode, true);
  document.removeEventListener("keyup", onKeyUpInLinkHintsMode, true);
  linkHintsModeActivated = false;
  HUD.hide();
}

function resetLinkHintsMode() {
  deactivateLinkHintsMode();
  activateLinkHintsModeWithQueue();
}

/*
 * Creates a link marker for the given link.
 */
function createMarkerFor(link, linkHintNumber, linkHintDigits) {
  var hintString = numberToHintString(linkHintNumber, linkHintDigits);
  var marker = document.createElement("div");
  marker.className = "internalVimiumHintMarker vimiumHintMarker";
  var innerHTML = [];
  // Make each hint character a span, so that we can highlight the typed characters as you type them.
  for (var i = 0; i < hintString.length; i++)
    innerHTML.push("<span>" + hintString[i].toUpperCase() + "</span>");
  marker.innerHTML = innerHTML.join("");
  marker.setAttribute("hintString", hintString);

  // Note: this call will be expensive if we modify the DOM in between calls.
  var clientRect = link.rect;
  // The coordinates given by the window do not have the zoom factor included since the zoom is set only on
  // the document node.
  var zoomFactor = currentZoomLevel / 100.0;
  marker.style.left = clientRect.left + window.scrollX / zoomFactor + "px";
  marker.style.top = clientRect.top  + window.scrollY / zoomFactor + "px";

  marker.clickableItem = link.element;
  return marker;
}
<|endoftext|>


<commit_before>// ==========================================================================
// Project:   SproutCore - JavaScript Application Framework
// Copyright: ©2006-2009 Sprout Systems, Inc. and contributors.
//            portions copyright @2009 Apple, Inc.
// License:   Licened under MIT license (see license.js)
// ==========================================================================

/*global module test htmlbody ok equals same stop start */

module("TODO: Test SC.CollectionView Methods");
<commit_msg>Remove empty unit test<commit_after><|endoftext|>


<commit_before>//----------------------------------------------------------------------

//----------------------------------------------------------------------

/**
* @class
* @extends {Vector2}
* @classdesc This class represents a point (x,y) into an 2D space
* @param {Number} x The x component.
* @param {Number} y The y component.
*/
var Point = function (x,y){
    Vector2.call(this,x,y);
};

Point.prototype = new Vector2();
Point.prototype.constructor = Point;

//----------------------------------------------------------------------

/**
 * @class
 * @classdesc Enum for shape values.
 * @enum {number}
 */
var Shape = {
  /** @member {number} */
  /** Rectangular shape.*/
  RECTANGLE : 0,
  /** @member {number} */
  /** Circular shape.*/
  CIRCLE : 1
};

//----------------------------------------------------------------------

// BEHAVIOURS
/**
* @class
* @extends {Script}
* @classdesc This class represents a behaviour for a GameElement.
* @param {Object} param The parameters of the behaviour.
* The object param contains these callbacks:

start : This function runs when the engine start at first time.

update : This function runs each frame.

onEnterCollision : This function runs when the game element collides with other game element.

onExitCollision : This function runs when the collision is finished.

onDestroy : This function runs when the game element is destroyed.

*/
var Behaviour = function (param) {
    Script.call(this);

    this.gameElement = null;

    if(param !== null){
      this._start = param.start || function(){};
      this._update = param.update || function(){};
      this._onEnterCollision = param.onEnterCollision || function(){};
      this._onExitCollision = param.onExitCollision || function(){};
      this._onDestroy = param.onDestroy || function(){};
    }
};

Behaviour.prototype = new Script();
Behaviour.prototype.constructor = Behaviour;

//----------------------------------------------------------------------

Behaviour.prototype.start = function () {
  this._start();
  this.gameElement.body.getBox2dBody().SetFixedRotation(true);
};

//----------------------------------------------------------------------

Behaviour.prototype.update = function (){
  this._update();
};

//----------------------------------------------------------------------

Behaviour.prototype.onEnterCollision = function (otherGameObject, contact){
  this._onEnterCollision(otherGameObject.getName());
};

//----------------------------------------------------------------------

Behaviour.prototype.onExitCollision = function (otherGameObject, contact){
  this._onExitCollision(otherGameObject.getName());
};

//----------------------------------------------------------------------

Behaviour.prototype.onDestroy = function (){
  this._onDestroy();
};

//----------------------------------------------------------------------

Behaviour.KEY_CONTROL = {
  update: function () {

    var v = 2;

    if (Input.isKeyPressed(37)) {
      // LEFT
      this.gameElement.setVelocity(new Vector2(-v,0));
    }else if (Input.isKeyPressed(38)) {
      // UP
      this.gameElement.setVelocity(new Vector2(0,v));
    }else if (Input.isKeyPressed(39)) {
      // RIGHT
      this.gameElement.setVelocity(new Vector2(v,0));
    }else if (Input.isKeyPressed(40)) {
      // DOWN
      this.gameElement.setVelocity(new Vector2(0,-v));
    }
  }
};

//----------------------------------------------------------------------

/**
* @class
* @extends {Script}
* @classdesc This class represents a GameElement.
* @param {Object} param The parameters of the gameelement.
* The object param contains these fields:

position : {Point} The position.

texture : {String} The name of the texture.

width : {Number} The width.

height : {Number} The height.

shape : {Shape} The shape.

behaviour : {Object} The behaviour's parameters.

isStatic : {Bool} True if static.

isSensor : {Bool} True if sensor.

name : {String} The name of the game element.

*/
var GameElement = function (param) {

  if(param !== null){

    var position = param.position || new Point(0,0);
    var texture = param.texture || null;
    var width = param.width || 1;
    var height = param.height || 1;
    var shape = param.shape || Shape.RECTANGLE;
    var behaviour = param.behaviour || {};
    var isStatic = param.isStatic || false;
    var isSensor = param.isSensor || false;
    var name = param.name || null;

    this.spriteBuilder = new SpriteBuilder();

    this.spriteBuilder.create(texture,position,width,height).
      setName(name).
      setStatic(isStatic).
      setRigidBody(1,0.5,0.5); // set physics properties

    var col = null;

    if(shape == Shape.RECTANGLE)
      col = new AABBCollider(width,height,isSensor); // set a Rectangle Collider
    else if(shape == Shape.CIRCLE)
      col = new CircleCollider(Math.max(width,height)/2,isSensor); // set a Circle Collider

    this.spriteBuilder.setCollider(col);

    this.gameObject = this.spriteBuilder.end();

    if(behaviour !== null)
      this.addBehaviour(behaviour);

    // get the transform and the physic body
    this.body = this.gameObject.getComponent(RigidBody);
    this.transform = this.gameObject.getTransform();
  }

};

/**
* Returns the name of the game element.
* @returns {String} The name.
*/
GameElement.prototype.getName = function () {
  return this.gameObject.getName();
};

/**
* Returns the position of the game element.
* @returns {Point} The name.
*/
GameElement.prototype.getPosition = function () {
  return this.transform.getPosition();
};

/**
* Moves the game element to a position.
* @param {Point} position The new position.
*/
GameElement.prototype.moveTo = function (position) {
  this.body.getBox2dBody().SetPosition(new b2Vec2(position.x,position.y));
};

/**
* Applies a force to the game element.
* @param {Point} vector The force vector.
*/
GameElement.prototype.applyForce = function (vector) {
  this.body.getBox2dBody().ApplyForce(new b2Vec2(vector.x,vector.y),this.body.GetWorldCenter());
};

/**
* Sets the velocity of the game element.
* @param {Point} vector The velocity vector.
*/
GameElement.prototype.setVelocity = function (vector) {

  var linear = this.body.getBox2dBody().GetLinearVelocity();

  linear.x = vector.x;
  linear.y = vector.y;
};

/**
* Rotates the game element.
* @param {number} angle The angle in degrees.
*/
GameElement.prototype.rotate = function (angle) {

};

/**
* Adds a new behaviour to the game element.
* @param {Object} behaviourParam The behaviour's parameters.
*/
GameElement.prototype.addBehaviour = function (behaviourParam) {
  var b = new Behaviour(behaviourParam);
  b.gameElement = this;
  this.gameObject.addComponent(b);
};

/**
* Destroys the game element.
*/
GameElement.prototype.destroy = function () {
  this.gameObject.destroy();
};

//----------------------------------------------------------------------

// API

//----------------------------------------------------------------------

/**
* @namespace
*/
var Game = {}; // namespace

//----------------------------------------------------------------------

/**
* Initializes the engine.
*/
Game.init = function () {
  // inicializa motor

  Thief.init();
  Thief.createAndSetScene("test");

  // camera

  var canvas = document.getElementById("glcanvas");
  var screenW = canvas.width;
  var screenH = canvas.height;
  var zoom = 5;
  var aspect = (screenW/screenH);
  var w = 1*aspect;
  var h = 1;

  var camBuilder = new CameraBuilder();

  var cam =
  camBuilder.create(new Vector3(0,0,15)).
    setOrtho(w*zoom,h*zoom, -100,100).
  end();

  Thief.addGameObjectToScene(cam);
  Thief.setCamera(cam);
};

//----------------------------------------------------------------------

/**
* Starts the engine.
*/
Game.run = function () {
  Thief.run();
};

//----------------------------------------------------------------------
/**
* Adds a game element to the the engine.
* @param {GameElement} elem The game element.
*/
Game.add = function(elem){
  Thief.addGameObjectToScene(elem.gameObject);
};

//----------------------------------------------------------------------

/**
* @namespace
*/
Game.create = {}; // namespace

//----------------------------------------------------------------------

/**
* Returns a game element.
* @param {Object} param The parameters of the gameelement.
* The object param contains these fields:

position : {Point} The position.

texture : {String} The name of the texture.

width : {Number} The width.

height : {Number} The height.

shape : {Shape} The shape.

behaviour : {Object} The behaviour's parameters.

isStatic : {Bool} True if static.

isSensor : {Bool} True if sensor.

name : {String} The name of the game element.

* @returns {GameElement} The game element.
*/
Game.create.gameElement = function (param){
  return new GameElement(param);
};

//----------------------------------------------------------------------

/**
* Returns a tile.
* @param {Point} position The position.
* @param {String} texture The name of the texture.
* @returns {GameElement} The tile.
*/
Game.create.tile = function (position,texture){
  return Game.create.gameElement({
    position : position,
    texture : texture,
    width : 1,
    height : 1,
    shape : Shape.RECTANGLE,
    isStatic : true,
    isSensor : true
  });
};

//----------------------------------------------------------------------
<commit_msg>Fix doc.<commit_after>//----------------------------------------------------------------------

//----------------------------------------------------------------------

/**
* @class
* @extends {Vector2}
* @classdesc This class represents a point (x,y) into an 2D space
* @param {Number} x The x component.
* @param {Number} y The y component.
*/
var Point = function (x,y){
    Vector2.call(this,x,y);
};

Point.prototype = new Vector2();
Point.prototype.constructor = Point;

//----------------------------------------------------------------------

/**
 * @class
 * @classdesc Enum for shape values.
 * @enum {number}
 */
var Shape = {
  /** @member {number} */
  /** Rectangular shape.*/
  RECTANGLE : 0,
  /** @member {number} */
  /** Circular shape.*/
  CIRCLE : 1
};

//----------------------------------------------------------------------

// BEHAVIOURS
/**
* @class
* @extends {Script}
* @classdesc This class represents a behaviour for a GameElement.
* @param {Object} param The parameters of the behaviour.
* The object param contains these callbacks:

start : This function runs when the engine starts at first time.

update : This function runs each frame.

onEnterCollision : This function runs when the game element collides with other game element.

onExitCollision : This function runs when the collision is finished.

onDestroy : This function runs when the game element is destroyed.

*/
var Behaviour = function (param) {
    Script.call(this);

    this.gameElement = null;

    if(param !== null){
      this._start = param.start || function(){};
      this._update = param.update || function(){};
      this._onEnterCollision = param.onEnterCollision || function(){};
      this._onExitCollision = param.onExitCollision || function(){};
      this._onDestroy = param.onDestroy || function(){};
    }
};

Behaviour.prototype = new Script();
Behaviour.prototype.constructor = Behaviour;

//----------------------------------------------------------------------

Behaviour.prototype.start = function () {
  this._start();
  this.gameElement.body.getBox2dBody().SetFixedRotation(true);
};

//----------------------------------------------------------------------

Behaviour.prototype.update = function (){
  this._update();
};

//----------------------------------------------------------------------

Behaviour.prototype.onEnterCollision = function (otherGameObject, contact){
  this._onEnterCollision(otherGameObject.getName());
};

//----------------------------------------------------------------------

Behaviour.prototype.onExitCollision = function (otherGameObject, contact){
  this._onExitCollision(otherGameObject.getName());
};

//----------------------------------------------------------------------

Behaviour.prototype.onDestroy = function (){
  this._onDestroy();
};

//----------------------------------------------------------------------

Behaviour.KEY_CONTROL = {
  update: function () {

    var v = 2;

    if (Input.isKeyPressed(37)) {
      // LEFT
      this.gameElement.setVelocity(new Vector2(-v,0));
    }else if (Input.isKeyPressed(38)) {
      // UP
      this.gameElement.setVelocity(new Vector2(0,v));
    }else if (Input.isKeyPressed(39)) {
      // RIGHT
      this.gameElement.setVelocity(new Vector2(v,0));
    }else if (Input.isKeyPressed(40)) {
      // DOWN
      this.gameElement.setVelocity(new Vector2(0,-v));
    }
  }
};

//----------------------------------------------------------------------

/**
* @class
* @extends {Script}
* @classdesc This class represents a GameElement.
* @param {Object} param The parameters of the gameelement.
* The object param contains these fields:

position : {Point} The position.

texture : {String} The name of the texture.

width : {Number} The width.

height : {Number} The height.

shape : {Shape} The shape.

behaviour : {Object} The behaviour's parameters.

isStatic : {Bool} True if static.

isSensor : {Bool} True if sensor.

name : {String} The name of the game element.

*/
var GameElement = function (param) {

  if(param !== null){

    var position = param.position || new Point(0,0);
    var texture = param.texture || null;
    var width = param.width || 1;
    var height = param.height || 1;
    var shape = param.shape || Shape.RECTANGLE;
    var behaviour = param.behaviour || {};
    var isStatic = param.isStatic || false;
    var isSensor = param.isSensor || false;
    var name = param.name || null;

    this.spriteBuilder = new SpriteBuilder();

    this.spriteBuilder.create(texture,position,width,height).
      setName(name).
      setStatic(isStatic).
      setRigidBody(1,0.5,0.5); // set physics properties

    var col = null;

    if(shape == Shape.RECTANGLE)
      col = new AABBCollider(width,height,isSensor); // set a Rectangle Collider
    else if(shape == Shape.CIRCLE)
      col = new CircleCollider(Math.max(width,height)/2,isSensor); // set a Circle Collider

    this.spriteBuilder.setCollider(col);

    this.gameObject = this.spriteBuilder.end();

    if(behaviour !== null)
      this.addBehaviour(behaviour);

    // get the transform and the physic body
    this.body = this.gameObject.getComponent(RigidBody);
    this.transform = this.gameObject.getTransform();
  }

};

/**
* Returns the name of the game element.
* @returns {String} The name.
*/
GameElement.prototype.getName = function () {
  return this.gameObject.getName();
};

/**
* Returns the position of the game element.
* @returns {Point} The name.
*/
GameElement.prototype.getPosition = function () {
  return this.transform.getPosition();
};

/**
* Moves the game element to a position.
* @param {Point} position The new position.
*/
GameElement.prototype.moveTo = function (position) {
  this.body.getBox2dBody().SetPosition(new b2Vec2(position.x,position.y));
};

/**
* Applies a force to the game element.
* @param {Point} vector The force vector.
*/
GameElement.prototype.applyForce = function (vector) {
  this.body.getBox2dBody().ApplyForce(new b2Vec2(vector.x,vector.y),this.body.GetWorldCenter());
};

/**
* Sets the velocity of the game element.
* @param {Point} vector The velocity vector.
*/
GameElement.prototype.setVelocity = function (vector) {

  var linear = this.body.getBox2dBody().GetLinearVelocity();

  linear.x = vector.x;
  linear.y = vector.y;
};

/**
* Rotates the game element.
* @param {number} angle The angle in degrees.
*/
GameElement.prototype.rotate = function (angle) {

};

/**
* Adds a new behaviour to the game element.
* @param {Object} behaviourParam The behaviour's parameters.
*/
GameElement.prototype.addBehaviour = function (behaviourParam) {
  var b = new Behaviour(behaviourParam);
  b.gameElement = this;
  this.gameObject.addComponent(b);
};

/**
* Destroys the game element.
*/
GameElement.prototype.destroy = function () {
  this.gameObject.destroy();
};

//----------------------------------------------------------------------

// API

//----------------------------------------------------------------------

/**
* @namespace
*/
var Game = {}; // namespace

//----------------------------------------------------------------------

/**
* Initializes the engine.
*/
Game.init = function () {
  // inicializa motor

  Thief.init();
  Thief.createAndSetScene("test");

  // camera

  var canvas = document.getElementById("glcanvas");
  var screenW = canvas.width;
  var screenH = canvas.height;
  var zoom = 5;
  var aspect = (screenW/screenH);
  var w = 1*aspect;
  var h = 1;

  var camBuilder = new CameraBuilder();

  var cam =
  camBuilder.create(new Vector3(0,0,15)).
    setOrtho(w*zoom,h*zoom, -100,100).
  end();

  Thief.addGameObjectToScene(cam);
  Thief.setCamera(cam);
};

//----------------------------------------------------------------------

/**
* Starts the engine.
*/
Game.run = function () {
  Thief.run();
};

//----------------------------------------------------------------------
/**
* Adds a game element to the the engine.
* @param {GameElement} elem The game element.
*/
Game.add = function(elem){
  Thief.addGameObjectToScene(elem.gameObject);
};

//----------------------------------------------------------------------

/**
* @namespace
*/
Game.create = {}; // namespace

//----------------------------------------------------------------------

/**
* Returns a game element.
* @param {Object} param The parameters of the gameelement.
* The object param contains these fields:

position : {Point} The position.

texture : {String} The name of the texture.

width : {Number} The width.

height : {Number} The height.

shape : {Shape} The shape.

behaviour : {Object} The behaviour's parameters.

isStatic : {Bool} True if static.

isSensor : {Bool} True if sensor.

name : {String} The name of the game element.

* @returns {GameElement} The game element.
*/
Game.create.gameElement = function (param){
  return new GameElement(param);
};

//----------------------------------------------------------------------

/**
* Returns a tile.
* @param {Point} position The position.
* @param {String} texture The name of the texture.
* @returns {GameElement} The tile.
*/
Game.create.tile = function (position,texture){
  return Game.create.gameElement({
    position : position,
    texture : texture,
    width : 1,
    height : 1,
    shape : Shape.RECTANGLE,
    isStatic : true,
    isSensor : true
  });
};

//----------------------------------------------------------------------
<|endoftext|>


<commit_before>
function WS4Redis(options, $) {
  'use strict';
  var opts, ws, deferred, timer, timer_interval = 0, closed = false;
  var heartbeat_interval = null, missed_heartbeats = 0;

  if (this === undefined) {
    return new WS4Redis(options, $);
  }
  if (options.uri === undefined) {
    throw new Error('No Websocket URI in options');
  }
  if ($ === undefined) {
    $ = jQuery;
  }
  opts = $.extend({ heartbeat_msg: null }, options);
  connect(opts.uri);

  function connect(uri) {
    try {
      console.log("Connecting to " + uri + " ...");
      deferred = $.Deferred();
      ws = new WebSocket(uri);
      ws.onopen = on_open;
      ws.onmessage = on_message;
      ws.onerror = on_error;
      ws.onclose = on_close;
      timer = null;
    } catch (err) {
      deferred.reject(new Error(err));
    }
  }

  function send_heartbeat() {
    try {
      missed_heartbeats++;
      if (missed_heartbeats > 3) {
        throw new Error("Too many missed heartbeats.");
      }
      ws.send(opts.heartbeat_msg);
    } catch(e) {
      clearInterval(heartbeat_interval);
      heartbeat_interval = null;
      console.warn("Closing connection. Reason: " + e.message);
      ws.close();
    }
  }

  function on_open() {
    console.log('Connected!');
    timer_interval = 500;
    deferred.resolve();
    if (opts.heartbeat_msg && heartbeat_interval === null) {
      missed_heartbeats = 0;
      heartbeat_interval = setInterval(send_heartbeat, 5000);
    }
  }

  function on_close(evt) {
    if (closed) {
      return;
    }
    console.log("Connection closed!");
    if (!timer) {
      // try to reconnect
      timer = setTimeout(function() {
        connect(ws.url);
      }, timer_interval);
      timer_interval = Math.min(timer_interval + 500, 5000);
    }
  }

  function on_error(evt) {
    console.error("Websocket connection is broken!");
    deferred.reject(new Error(evt));
  }

  function on_message(evt) {
    if (opts.heartbeat_msg && evt.data === opts.heartbeat_msg) {
      // reset the counter for missed heartbeats
      missed_heartbeats = 0;
    } else if (typeof opts.receive_message === 'function') {
      return opts.receive_message(evt.data);
    }
  }

  this.send_message = function(message) {
    ws.send(message);
  }

  this.close = function() {
    closed = true;
    if (heartbeat_interval) {
      clearInterval(heartbeat_interval);
    }
    ws.close();
  }
}
<commit_msg>Improve logging for websockets<commit_after>
function WS4Redis(options, $) {
  'use strict';
  var opts, ws, deferred, timer, timer_interval = 0, closed = false;
  var heartbeat_interval = null, missed_heartbeats = 0;

  if (this === undefined) {
    return new WS4Redis(options, $);
  }
  if (options.uri === undefined) {
    throw new Error('No Websocket URI in options');
  }
  if ($ === undefined) {
    $ = jQuery;
  }
  opts = $.extend({ heartbeat_msg: null }, options);
  connect(opts.uri);

  function connect(uri) {
    try {
//      console.log("Connecting to " + uri + " ...");
      deferred = $.Deferred();
      ws = new WebSocket(uri);
      ws.onopen = on_open;
      ws.onmessage = on_message;
      ws.onerror = on_error;
      ws.onclose = on_close;
      timer = null;
    } catch (err) {
      deferred.reject(new Error(err));
    }
  }

  function send_heartbeat() {
    try {
      missed_heartbeats++;
      if (missed_heartbeats > 3) {
        throw new Error("Too many missed heartbeats.");
      }
      ws.send(opts.heartbeat_msg);
    } catch(e) {
      clearInterval(heartbeat_interval);
      heartbeat_interval = null;
      console.warn("Closing connection. Reason: " + e.message);
      ws.close();
    }
  }

  function on_open() {
    console.log("Connected to "+ws.url);
    timer_interval = 500;
    deferred.resolve();
    if (opts.heartbeat_msg && heartbeat_interval === null) {
      missed_heartbeats = 0;
      heartbeat_interval = setInterval(send_heartbeat, 5000);
    }
  }

  function on_close(evt) {
    if (closed) {
      return;
    }
    console.log("Connection to "+ws.url+"closed!");
    if (!timer) {
      // try to reconnect
      timer = setTimeout(function() {
        connect(ws.url);
      }, timer_interval);
      timer_interval = Math.min(timer_interval + 500, 5000);
    }
  }

  function on_error(evt) {
    console.error("Websocket connection is broken!");
    deferred.reject(new Error(evt));
  }

  function on_message(evt) {
    if (opts.heartbeat_msg && evt.data === opts.heartbeat_msg) {
      // reset the counter for missed heartbeats
      missed_heartbeats = 0;
    } else if (typeof opts.receive_message === 'function') {
      return opts.receive_message(evt.data);
    }
  }

  this.send_message = function(message) {
    ws.send(message);
  }

  this.close = function() {
    closed = true;
    if (heartbeat_interval) {
      clearInterval(heartbeat_interval);
    }
    ws.close();
  }
}
<|endoftext|>


<commit_before>/**
 * @file RoundStep component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import RoundStepItem from '../_RoundStepItem';

class RoundStep extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            activatedStep: props.activatedStep,
            finishedStep: props.finishedStep
        };

        this.touchTapHandler = ::this.touchTapHandler;

    }

    touchTapHandler(activatedStep) {

        const {onChange} = this.props;

        this.setState({
            activatedStep
        }, () => {
            onChange && onChange({
                activatedStep,
                finishedStep: this.state.finishedStep
            });
        });

    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.activatedStep !== this.state.activatedStep
            || nextProps.finishedStep !== this.state.finishedStep) {
            this.setState({
                activatedStep: nextProps.activatedStep,
                finishedStep: nextProps.finishedStep
            });
        }
    }

    render() {

        const {className, style, steps, showFinishedStepIcon, finishedStepIconCls, disabled} = this.props,
            {activatedStep, finishedStep} = this.state;

        return (

                {
                    steps && steps.map((item, index) => {
                        return (
                            <RoundStepItem key={index}
                                           index={index}
                                           className={item.className}
                                           style={{
                                               ...item.style,
                                               width: `${100 / steps.length}%`,
                                               zIndex: steps.length - index
                                           }}
                                           activatedStep={activatedStep}
                                           finishedStep={finishedStep}
                                           value={item}
                                           isFirst={index === 0}
                                           isLast={index === steps.length - 1}
                                           showFinishedStepIcon={showFinishedStepIcon}
                                           finishedStepIconCls={finishedStepIconCls}
                                           disabled={disabled}
                                           onTouchTap={this.touchTapHandler}/>
                        );
                    })
                }

        );

    }
};

RoundStep.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * The render content of step.
     */
    steps: PropTypes.arrayOf(PropTypes.shape({

        /**
         * The CSS class name of step element.
         */
        className: PropTypes.string,

        /**
         * Override the styles of the step element.
         */
        style: PropTypes.object,

        /**
         * The text value of step.
         */
        title: PropTypes.string

    })).isRequired,

    /**
     * Sets the step as active.
     */
    activatedStep: PropTypes.number,

    /**
     * The final step.
     */
    finishedStep: PropTypes.number,

    showFinishedStepIcon: PropTypes.bool,
    finishedStepIconCls: PropTypes.string,

    disabled: PropTypes.bool,

    /**
     * Callback function fired when step change.
     */
    onChange: PropTypes.func

};

RoundStep.defaultProps = {

    className: null,
    style: null,

    steps: null,

    activatedStep: 0,
    finishedStep: 0,

    showFinishedStepIcon: true,
    finishedStepIconCls: 'fa fa-check',
    disabled: false

};

export default RoundStep;<commit_msg>update RoundStep<commit_after>/**
 * @file RoundStep component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import RoundStepItem from '../_RoundStepItem';

class RoundStep extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            activatedStep: props.activatedStep,
            finishedStep: props.finishedStep
        };

        this.touchTapHandler = ::this.touchTapHandler;

    }

    touchTapHandler(activatedStep) {

        const {onChange} = this.props;

        this.setState({
            activatedStep
        }, () => {
            onChange && onChange({
                activatedStep,
                finishedStep: this.state.finishedStep
            });
        });

    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.activatedStep !== this.state.activatedStep
            || nextProps.finishedStep !== this.state.finishedStep) {
            this.setState({
                activatedStep: nextProps.activatedStep,
                finishedStep: nextProps.finishedStep
            });
        }
    }

    render() {

        const {className, style, steps, showFinishedStepIcon, finishedStepIconCls, disabled} = this.props,
            {activatedStep, finishedStep} = this.state,

            stepClassName = classNames('round-step', {
                [className]: className
            });

        return (

                {
                    steps && steps.map((item, index) =>
                        <RoundStepItem key={index}
                                       index={index}
                                       className={item.className}
                                       style={{
                                           ...item.style,
                                           width: `${100 / steps.length}%`,
                                           zIndex: steps.length - index
                                       }}
                                       activatedStep={activatedStep}
                                       finishedStep={finishedStep}
                                       value={item}
                                       isFirst={index === 0}
                                       isLast={index === steps.length - 1}
                                       showFinishedStepIcon={showFinishedStepIcon}
                                       finishedStepIconCls={finishedStepIconCls}
                                       disabled={disabled}
                                       onTouchTap={this.touchTapHandler}/>
                    )
                }

        );

    }
};

RoundStep.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * The render content of step.
     */
    steps: PropTypes.arrayOf(PropTypes.shape({

        /**
         * The CSS class name of step element.
         */
        className: PropTypes.string,

        /**
         * Override the styles of the step element.
         */
        style: PropTypes.object,

        /**
         * The text value of step.
         */
        title: PropTypes.string

    })).isRequired,

    /**
     * Sets the step as active.
     */
    activatedStep: PropTypes.number,

    /**
     * The final step.
     */
    finishedStep: PropTypes.number,

    showFinishedStepIcon: PropTypes.bool,
    finishedStepIconCls: PropTypes.string,

    disabled: PropTypes.bool,

    /**
     * Callback function fired when step change.
     */
    onChange: PropTypes.func

};

RoundStep.defaultProps = {

    className: null,
    style: null,

    steps: null,

    activatedStep: 0,
    finishedStep: 0,

    showFinishedStepIcon: true,
    finishedStepIconCls: 'fa fa-check',
    disabled: false

};

export default RoundStep;<|endoftext|>


<commit_before>/**
* Subject controller definition
* @scope Controllers
*/
define(['./module'], function (controllers) {
  'use strict';
  controllers.controller('SubjectCtrl', function ($scope,SubjectService,ScheduleService,ngProgress) {
    /*
     * lists all the currently selected subjects
     */
    $scope.subjects = SubjectService.get();
    /*
     * emits an event to rootScope when subjects are changed
     */
    $scope.$watchCollection('subjects', function() {
      $scope.changeGroup();
    });
    /*
     * loads the autocomplete service
     */
    $scope.autocomplete = function(name){
      return SubjectService.autoComplete(name);
    };
    /*
     * adds a selected subject to the subjects list
     */
    $scope.onSelect  = function($item){
      SubjectService.add($item);
      ngProgress.start();
    };
    /*
     * removes a subject from the selected subjects
     */
    $scope.removeSubject = function(code){
      SubjectService.del(code);
    };
    /*
     * changes the check status for the given children
     */
    $scope.checkChange = function(items,value, isChild){
      items.forEach(function(item){
        item.isChecked = value;
        if(item.hasOwnProperty('groups')){
          $scope.checkChange(item.groups,value,true);
        }
      });
      if ( isChild === undefined ) {
        $scope.changeGroup();
      }
    };
    /*
     * toggles the group and emits the scheduleChange event
     */
    var emit = function() {
      ScheduleService.setSubjectQuery(SubjectService.getQuery());
      $scope.$emit('scheduleChange');
      ngProgress.set(50);
    };
    /*
     * Formats the input on the typeahead
     */
    $scope.formatInput = function(){
      return '';
    };
    /*
     * checks every 5 seconds for changes if a change is detected it fires
     * a subject change event
     */
    $scope.changeGroup = function(){
      var throttledEmit = _.throttle(emit, 5000, { 'leading': false, 'trailing': true });
      throttledEmit();
    };
  });
});
<commit_msg>fixing progress bar<commit_after>/**
* Subject controller definition
* @scope Controllers
*/
define(['./module'], function (controllers) {
  'use strict';
  controllers.controller('SubjectCtrl', function ($scope,SubjectService,ScheduleService,ngProgress) {
    /*
     * lists all the currently selected subjects
     */
    $scope.subjects = SubjectService.get();
    /*
     * emits an event to rootScope when subjects are changed
     */
    $scope.$watchCollection('subjects', function() {
      $scope.changeGroup();
    });
    /*
     * loads the autocomplete service
     */
    $scope.autocomplete = function(name){
      return SubjectService.autoComplete(name);
    };
    /*
     * adds a selected subject to the subjects list
     */
    $scope.onSelect  = function($item){
      SubjectService.add($item);
      ngProgress.start();
    };
    /*
     * removes a subject from the selected subjects
     */
    $scope.removeSubject = function(code){
      SubjectService.del(code);
    };
    /*
     * changes the check status for the given children
     */
    $scope.checkChange = function(items,value, isChild){
      items.forEach(function(item){
        item.isChecked = value;
        if(item.hasOwnProperty('groups')){
          $scope.checkChange(item.groups,value,true);
        }
      });
      if ( isChild === undefined ) {
        $scope.changeGroup();
      }
    };
    /*
     * toggles the group and emits the scheduleChange event
     */
    var emit = function() {
      ScheduleService.setSubjectQuery(SubjectService.getQuery());
      $scope.$emit('scheduleChange');
      if(ngProgress.status() === 0){
      } else if(ngProgress.status() < 50){
        ngProgress.set(50);
      }

    };
    /*
     * Formats the input on the typeahead
     */
    $scope.formatInput = function(){
      return '';
    };
    /*
     * checks every 5 seconds for changes if a change is detected it fires
     * a subject change event
     */
    $scope.changeGroup = function(){
      var throttledEmit = _.throttle(emit, 5000, { 'leading': false, 'trailing': true });
      throttledEmit();
    };
  });
});
<|endoftext|>


<commit_before>import { Context } from "./context";
import { PreparedContext } from "./prepared-context";

import {
  setPassword,
  getPassword,
  setProperty,
  removeSensibleValues
} from "./util";
import { version, engines } from "../package.json";
import { GithubProvider } from "github-repository-provider";
import { BitbucketProvider } from "bitbucket-repository-provider";
import { LocalProvider } from "local-repository-provider";
import { AggregationProvider } from "aggregation-repository-provider";
import { satisfies } from "semver";

const program = require("caporal"),
  prompt = require("prompt");

process.on("uncaughtException", err => console.error(err));
process.on("unhandledRejection", reason => console.error(reason));

const properties = {};

program
  .description("Keep npm package in sync with its template")
  .version(version)
  .option("--dry", "do not create branch/pull request", program.BOOL, false)
  .option(
    "-k, --keystore <account/service>",
    "keystore",
    /^[\w\-]+\/.*/,
    "arlac77/GitHub for Mac SSH key passphrase — github.com"
  )
  .option("-s, --save", "save keystore")
  .option("-d --define <key=value>", "set provider option", values => {
    if (!Array.isArray(values)) {
      values = [values];
    }

    values.forEach(value => {
      const [k, v] = value.split(/=/);
      setProperty(properties, k, v);
    });
  })
  .option(
    "--list-providers",
    "list providers with options and exit",
    program.BOOL,
    false
  )
  .option(
    "--list-properties",
    "list all properties (if given of the first repo) and exit",
    program.BOOL,
    false
  )
  .option(
    "-t, --template <identifier>",
    "template repository",
    /^([\w\-]+\/[\w\-]+)|((git|ssh|https?):\/\/.*)$/
  )
  .option(
    "--usage",
    "track packages using template in package.json",
    program.BOOL,
    false
  )
  .argument("[repos...]", "repos to merge")
  .action(async (args, options, logger) => {
    if (options.save) {
      prompt.start();
      const schema = {
        properties: {
          password: {
            required: true,
            hidden: true
          }
        }
      };
      prompt.get(schema, async (err, result) => {
        if (err) {
          logger.error(err);
          return;
        }

        try {
          await setPassword(result.password, options);
        } catch (e) {
          logger.error(err);
          return;
        }
        logger.info("password set");
      });
    }

    try {
      const pass = await getPassword(options);
      const aggregationProvider = new AggregationProvider();

      if (pass !== null && pass !== undefined) {
        if (properties.GithubProvider === undefined) {
          properties.GithubProvider = {};
        }
        properties.GithubProvider.auth = pass;
      }

      [BitbucketProvider, GithubProvider, LocalProvider].forEach(provider => {
        let options = provider.optionsFromEnvironment(process.env);

        if (options !== undefined || properties[provider.name] !== undefined) {
          options = Object.assign({}, options, properties[provider.name]);
          aggregationProvider.providers.push(new provider(options));
        }
      });

      if (options.listProviders) {
        logger.info(
          Array.from(
            aggregationProvider.providers.map(
              p =>
                `${p.name}: ${JSON.stringify(removeSensibleValues(p.config))}`
            )
          ).join("\n")
        );

        return;
      }

      const context = new Context(aggregationProvider, {
        templateBranchName: options.template,
        dry: options.dry,
        trackUsedByModule: options.usage,
        logger,
        properties
      });

      if (args.repos.length === 0 && options.listProperties) {
        logger.info(
          JSON.stringify(removeSensibleValues(context.properties), undefined, 2)
        );
        return;
      }

      for (const repo of args.repos) {
        const pc = new PreparedContext(context, repo);
        await pc.initialize();

        if (options.listProperties) {
          logger.info(
            JSON.stringify(removeSensibleValues(pc.properties), undefined, 2)
          );
          return;
        }

        await pc.execute();
      }
    } catch (err) {
      logger.error(err);
    }
  });

if (!satisfies(process.versions.node, engines.node)) {
  console.error(`require node ${engines.node} (not ${process.versions.node})`);
  process.exit(-1);
}

program.parse(process.argv);
<commit_msg>fix(cli): exit -1 in case of error<commit_after>import { Context } from "./context";
import { PreparedContext } from "./prepared-context";

import {
  setPassword,
  getPassword,
  setProperty,
  removeSensibleValues
} from "./util";
import { version, engines } from "../package.json";
import { GithubProvider } from "github-repository-provider";
import { BitbucketProvider } from "bitbucket-repository-provider";
import { LocalProvider } from "local-repository-provider";
import { AggregationProvider } from "aggregation-repository-provider";
import { satisfies } from "semver";

const program = require("caporal"),
  prompt = require("prompt");

process.on("uncaughtException", err => console.error(err));
process.on("unhandledRejection", reason => console.error(reason));

const properties = {};

program
  .description("Keep npm package in sync with its template")
  .version(version)
  .option("--dry", "do not create branch/pull request", program.BOOL, false)
  .option(
    "-k, --keystore <account/service>",
    "keystore",
    /^[\w\-]+\/.*/,
    "arlac77/GitHub for Mac SSH key passphrase — github.com"
  )
  .option("-s, --save", "save keystore")
  .option("-d --define <key=value>", "set provider option", values => {
    if (!Array.isArray(values)) {
      values = [values];
    }

    values.forEach(value => {
      const [k, v] = value.split(/=/);
      setProperty(properties, k, v);
    });
  })
  .option(
    "--list-providers",
    "list providers with options and exit",
    program.BOOL,
    false
  )
  .option(
    "--list-properties",
    "list all properties (if given of the first repo) and exit",
    program.BOOL,
    false
  )
  .option(
    "-t, --template <identifier>",
    "template repository",
    /^([\w\-]+\/[\w\-]+)|((git|ssh|https?):\/\/.*)$/
  )
  .option(
    "--usage",
    "track packages using template in package.json",
    program.BOOL,
    false
  )
  .argument("[repos...]", "repos to merge")
  .action(async (args, options, logger) => {
    if (options.save) {
      prompt.start();
      const schema = {
        properties: {
          password: {
            required: true,
            hidden: true
          }
        }
      };
      prompt.get(schema, async (err, result) => {
        if (err) {
          logger.error(err);
          return;
        }

        try {
          await setPassword(result.password, options);
        } catch (e) {
          logger.error(err);
          return;
        }
        logger.info("password set");
      });
    }

    try {
      const pass = await getPassword(options);
      const aggregationProvider = new AggregationProvider();

      if (pass !== null && pass !== undefined) {
        if (properties.GithubProvider === undefined) {
          properties.GithubProvider = {};
        }
        properties.GithubProvider.auth = pass;
      }

      [BitbucketProvider, GithubProvider, LocalProvider].forEach(provider => {
        let options = provider.optionsFromEnvironment(process.env);

        if (options !== undefined || properties[provider.name] !== undefined) {
          options = Object.assign({}, options, properties[provider.name]);
          aggregationProvider.providers.push(new provider(options));
        }
      });

      if (options.listProviders) {
        logger.info(
          Array.from(
            aggregationProvider.providers.map(
              p =>
                `${p.name}: ${JSON.stringify(removeSensibleValues(p.config))}`
            )
          ).join("\n")
        );

        return;
      }

      const context = new Context(aggregationProvider, {
        templateBranchName: options.template,
        dry: options.dry,
        trackUsedByModule: options.usage,
        logger,
        properties
      });

      if (args.repos.length === 0 && options.listProperties) {
        logger.info(
          JSON.stringify(removeSensibleValues(context.properties), undefined, 2)
        );
        return;
      }

      for (const repo of args.repos) {
        const pc = new PreparedContext(context, repo);
        await pc.initialize();

        if (options.listProperties) {
          logger.info(
            JSON.stringify(removeSensibleValues(pc.properties), undefined, 2)
          );
          return;
        }

        await pc.execute();
      }
    } catch (err) {
      logger.error(err);
      process.exit(-1);
    }
  });

if (!satisfies(process.versions.node, engines.node)) {
  console.error(`require node ${engines.node} (not ${process.versions.node})`);
  process.exit(-1);
}

program.parse(process.argv);
<|endoftext|>