<commit_before>angular.module('avAdmin')
  .directive('avAdminCreate', ['$q', 'Authmethod', 'ElectionsApi', '$state', '$i18next', function($q, Authmethod, ElectionsApi, $state, $i18next) {
    // we use it as something similar to a controller here
    function link(scope, element, attrs) {
        scope.creating = false;
        scope.log = '';
        scope.createElectionBool = true;

        if (ElectionsApi.currentElections.length === 0 && !!ElectionsApi.currentElection) {
          scope.elections = [ElectionsApi.currentElection];
        } else {
          scope.elections = ElectionsApi.currentElections;
          ElectionsApi.currentElections = [];
        }

        function getCreatePerm(el) {
            console.log("creating perm for election " + el.title);
            var deferred = $q.defer();
            Authmethod.getPerm("create", "AuthEvent", 0)
                .success(function(data) {
                    var perm = data['permission-token'];
                    el.perm = perm;
                    deferred.resolve(el);
                }).error(deferred.reject);

            return deferred.promise;
        }

        function logInfo(text) {
          scope.log += "
" + text + "
";
        }

        function logError(text) {
          scope.log += "
" + text + "
";
        }

        function createAuthEvent(el) {
            console.log("creating auth event for election " + el.title);
            var deferred = $q.defer();
            // Creating the authentication
            logInfo($i18next('avAdmin.create.creating', {title: el.title}));

            if (el.census.config.subject && el.census.auth_method !== 'email') {
              delete el.census.config.subject;
            }

            var d = {
                auth_method: el.census.auth_method,
                census: el.census.census,
                config: el.census.config,
                extra_fields: []
            };

            d.extra_fields = _.filter(el.census.extra_fields, function(ef) {
              var must = ef.must;
              delete ef.disabled;
              delete ef.must;
              if (_.contains(['bool', 'captcha'], ef.type)) {
                delete ef.min;
                delete ef.max;
              } else {
                if (!!ef.min) {
                  ef.min = parseInt(ef.min);
                }
                if (!!ef.max) {
                  ef.max = parseInt(ef.max);
                }
              }
              return !must;
            });

            Authmethod.createEvent(d)
                .success(function(data) {
                    el.id = data.id;
                    deferred.resolve(el);
                }).error(deferred.reject);
            return deferred.promise;
        }

        function addCensus(el) {
            console.log("adding census for election " + el.title);
            var deferred = $q.defer();
            // Adding the census
            logInfo($i18next('avAdmin.create.census', {title: el.title, id: el.id}));
            var voters = _.map(el.census.voters, function (i) { return i.metadata; });
            Authmethod.addCensus(el.id, voters, 'disabled')
                .success(function(data) {
                    deferred.resolve(el);
                }).error(deferred.reject);
            return deferred.promise;
        }

        function registerElection(el) {
            console.log("registering election " + el.title);
            var deferred = $q.defer();
            // Registering the election
            logInfo($i18next('avAdmin.create.reg', {title: el.title, id: el.id}));
            ElectionsApi.command(el, '', 'POST', el)
                .then(function(data) { deferred.resolve(el); })
                .catch(deferred.reject);
            return deferred.promise;
        }

        function createElection(el) {
            var deferred = $q.defer();
            if (scope.createElectionBool) {
              console.log("creating election " + el.title);
              // Creating the election
              logInfo($i18next('avAdmin.create.creatingEl', {title: el.title, id: el.id}));
              ElectionsApi.command(el, 'create', 'POST', {})
                .then(function(data) { deferred.resolve(el); })
                .catch(deferred.reject);
            } else {
              deferred.resolve(el);
            }
            return deferred.promise;
        }

        function addElection(i) {
          var deferred = $q.defer();
          if (i === scope.elections.length) {
            var el = scope.elections[i - 1];
            $state.go("admin.dashboard", {id: el.id});
            return;
          }

          var promise = deferred.promise;
          promise = promise
            .then(getCreatePerm)
            .then(createAuthEvent)
            .then(addCensus)
            .then(registerElection)
            .then(createElection)
            .then(function(el) {
                console.log("waiting for election " + el.title);
                waitForCreated(el.id, function () {
                  addElection(i+1);
                });
              })
              .catch(function(error) {
                scope.creating = false;
                scope.creating_text = '';
                logError(angular.toJson(error));
              });
          deferred.resolve(scope.elections[i]);
        }

        function createElections() {
            var deferred = $q.defer();
            addElection(0);
            var promise = deferred.promise;

            scope.creating = true;
        }

        function waitForCreated(id, f) {
          console.log("waiting for election id = " + id);
          ElectionsApi.getElection(id, true)
            .then(function(el) {
                var deferred = $q.defer();
                if (scope.createElectionBool && el.status === 'created' ||
                  !scope.createElectionBool && el.status === 'registered')
                {
                  f();
                } else {
                  setTimeout(function() { waitForCreated(id, f); }, 3000);
                }
            });
        }

        angular.extend(scope, {
          createElections: createElections,
        });
    }

    return {
      restrict: 'AE',
      scope: {
      },
      link: link,
      templateUrl: 'avAdmin/admin-directives/create/create.html'
    };
  }]);
<commit_msg>Using config authevent id instead of 0<commit_after>angular.module('avAdmin')
  .directive('avAdminCreate', ['$q', 'Authmethod', 'ElectionsApi', '$state', '$i18next', 'ConfigService',
    function($q, Authmethod, ElectionsApi, $state, $i18next, ConfigService) {
    // we use it as something similar to a controller here
    function link(scope, element, attrs) {
        var adminId = ConfigService.freeAuthId;
        scope.creating = false;
        scope.log = '';
        scope.createElectionBool = true;

        if (ElectionsApi.currentElections.length === 0 && !!ElectionsApi.currentElection) {
          scope.elections = [ElectionsApi.currentElection];
        } else {
          scope.elections = ElectionsApi.currentElections;
          ElectionsApi.currentElections = [];
        }

        function getCreatePerm(el) {
            console.log("creating perm for election " + el.title);
            var deferred = $q.defer();
            Authmethod.getPerm("create", "AuthEvent", adminId)
                .success(function(data) {
                    var perm = data['permission-token'];
                    el.perm = perm;
                    deferred.resolve(el);
                }).error(deferred.reject);

            return deferred.promise;
        }

        function logInfo(text) {
          scope.log += "
" + text + "
";
        }

        function logError(text) {
          scope.log += "
" + text + "
";
        }

        function createAuthEvent(el) {
            console.log("creating auth event for election " + el.title);
            var deferred = $q.defer();
            // Creating the authentication
            logInfo($i18next('avAdmin.create.creating', {title: el.title}));

            if (el.census.config.subject && el.census.auth_method !== 'email') {
              delete el.census.config.subject;
            }

            var d = {
                auth_method: el.census.auth_method,
                census: el.census.census,
                config: el.census.config,
                extra_fields: []
            };

            d.extra_fields = _.filter(el.census.extra_fields, function(ef) {
              var must = ef.must;
              delete ef.disabled;
              delete ef.must;
              if (_.contains(['bool', 'captcha'], ef.type)) {
                delete ef.min;
                delete ef.max;
              } else {
                if (!!ef.min) {
                  ef.min = parseInt(ef.min);
                }
                if (!!ef.max) {
                  ef.max = parseInt(ef.max);
                }
              }
              return !must;
            });

            Authmethod.createEvent(d)
                .success(function(data) {
                    el.id = data.id;
                    deferred.resolve(el);
                }).error(deferred.reject);
            return deferred.promise;
        }

        function addCensus(el) {
            console.log("adding census for election " + el.title);
            var deferred = $q.defer();
            // Adding the census
            logInfo($i18next('avAdmin.create.census', {title: el.title, id: el.id}));
            var voters = _.map(el.census.voters, function (i) { return i.metadata; });
            Authmethod.addCensus(el.id, voters, 'disabled')
                .success(function(data) {
                    deferred.resolve(el);
                }).error(deferred.reject);
            return deferred.promise;
        }

        function registerElection(el) {
            console.log("registering election " + el.title);
            var deferred = $q.defer();
            // Registering the election
            logInfo($i18next('avAdmin.create.reg', {title: el.title, id: el.id}));
            ElectionsApi.command(el, '', 'POST', el)
                .then(function(data) { deferred.resolve(el); })
                .catch(deferred.reject);
            return deferred.promise;
        }

        function createElection(el) {
            var deferred = $q.defer();
            if (scope.createElectionBool) {
              console.log("creating election " + el.title);
              // Creating the election
              logInfo($i18next('avAdmin.create.creatingEl', {title: el.title, id: el.id}));
              ElectionsApi.command(el, 'create', 'POST', {})
                .then(function(data) { deferred.resolve(el); })
                .catch(deferred.reject);
            } else {
              deferred.resolve(el);
            }
            return deferred.promise;
        }

        function addElection(i) {
          var deferred = $q.defer();
          if (i === scope.elections.length) {
            var el = scope.elections[i - 1];
            $state.go("admin.dashboard", {id: el.id});
            return;
          }

          var promise = deferred.promise;
          promise = promise
            .then(getCreatePerm)
            .then(createAuthEvent)
            .then(addCensus)
            .then(registerElection)
            .then(createElection)
            .then(function(el) {
                console.log("waiting for election " + el.title);
                waitForCreated(el.id, function () {
                  addElection(i+1);
                });
              })
              .catch(function(error) {
                scope.creating = false;
                scope.creating_text = '';
                logError(angular.toJson(error));
              });
          deferred.resolve(scope.elections[i]);
        }

        function createElections() {
            var deferred = $q.defer();
            addElection(0);
            var promise = deferred.promise;

            scope.creating = true;
        }

        function waitForCreated(id, f) {
          console.log("waiting for election id = " + id);
          ElectionsApi.getElection(id, true)
            .then(function(el) {
                var deferred = $q.defer();
                if (scope.createElectionBool && el.status === 'created' ||
                  !scope.createElectionBool && el.status === 'registered')
                {
                  f();
                } else {
                  setTimeout(function() { waitForCreated(id, f); }, 3000);
                }
            });
        }

        angular.extend(scope, {
          createElections: createElections,
        });
    }

    return {
      restrict: 'AE',
      scope: {
      },
      link: link,
      templateUrl: 'avAdmin/admin-directives/create/create.html'
    };
  }]);
<|endoftext|>


<commit_before>/************************************
 * Copyright (c) 2013 AnsibleWorks, Inc.
 *
 *
 *  JobEvents.js
 *
 *  Controller functions for the Job Events model.
 *
 */

'use strict';

function JobEventsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, JobEventList,
                        GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller,
                        ClearScope, ProcessErrors, GetBasePath, LookUpInit, ToggleChildren,
                        FormatDate)
{
    ClearScope('htmlTemplate');
    var list = JobEventList;
    list.base = $location.path();

    var defaultUrl = GetBasePath('jobs') + $routeParams.id  + '/job_events/'; //?parent__isnull=1';

    var view = GenerateList;
    var base = $location.path().replace(/^\//,'').split('/')[0];
    var scope = view.inject(list, { mode: 'edit' });

    $rootScope.flashMessage = null;
    scope.selected = [];
    scope.expand = true;    //on load, automatically expand all nodes

    scope.parentNode = 'parent-event';  // used in ngClass to dynamicall set row level class and control
    scope.childNode = 'child-event';    // link color and cursor

    function formatJSON(eventData) {
        //turn JSON event data into an html form
        var html = '';
        if (eventData['res']) {
           var n, rows;
           var found =  false;
           if (typeof eventData.res == 'string') {
              n = eventData['res'].match(/\n/g);
              rows = (n) ? n.length : 1;
              found = true;
              html += "Traceback:\n";
              html += "<textarea readonly class=\"input-xxlarge\" rows=\"" + rows + "\">" + eventData.res + "</textarea>\n";
           }
           else {
              for (var fld in eventData.res) {
                  if ( (fld == 'msg' || fld == 'stdout' || fld == 'stderr') &&
                       (eventData.res[fld] !== null && eventData.res[fld] !== '') ) {
                       html += "";
                       switch(fld) {
                          case 'msg':
                          case 'stdout':
                             html += 'Output:';
                             break;
                          case 'stderr':
                             html += 'Error:';
                             break;
                       }
                       html += "\n";
                       n = eventData['res'][fld].match(/\n/g);
                       rows = (n) ? n.length : 1;
                       html += "<textarea readonly class=\"input-xxlarge\" rows=\"" + rows + "\">" + eventData.res[fld] + "</textarea>\n";
                       found = true;
                  }
                  if ( fld == "results" && Array.isArray(eventData.res[fld]) && eventData.res[fld].length > 0 ) {
                     html += "Results:\n";
                     //html += "<textarea readonly class="
                     var txt = '';
                     for (var i=0; i < eventData.res[fld].length; i++) {
                         txt += eventData.res[fld][i];
                     }
                     n = txt.match(/\n/g);
                     rows = (n) ? n.length : 1;
                     html += "<textarea readonly class=\"input-xxlarge\" rows=\"" + rows + "\">" + txt + "</textarea>\n";
                     found = true;
                  }
                  if (fld == "rc" && eventData.res[fld] != 0) {
                     html += "Return Code:\n";
                     html += "<input type=\"text\" class=\"input-mini\" value=\"" + eventData.res[fld] + "\" readonly >\n";
                     found = true;
                  }
              }
           }
           html = (found) ? "<form class=\"event-form\">\n" + html + "</form>\n" : '';
        }
        if (eventData['host']) {
           html = "" + eventData['host'] + "\n" + html;
        }
        else {
           html = (html == '' ) ? null : html;
        }
        return html;
        }

    if (scope.RemovePostRefresh) {
       scope.RemovePostRefresh();
    }
    scope.RemovePostRefresh = scope.$on('PostRefresh', function() {
        // Initialize the parent levels
        var set = scope[list.name];
        var cDate;
        for (var i=0; i < set.length; i++) {
            set[i].event_display = set[i].event_display.replace(/^\u00a0*/g,'');
            if (set[i].event_level < 3 ) {
               set[i]['ngclick'] = "toggleChildren(" + set[i].id + ", \"" + set[i].related.children + "\")";
               set[i]['ngicon'] = 'icon-collapse-alt';
               set[i]['class'] = 'parentNode';
            }
            else {
               set[i]['class'] = 'childNode';
               set[i]['event_detail'] = formatJSON(set[i].event_data);
            }
            set[i]['show'] = true;
            set[i]['spaces'] = set[i].event_level * 24;
            if (scope.jobevents[i].failed) {
               scope.jobevents[i].status = 'error';
            }
            else if (scope.jobevents[i].changed) {
               scope.jobevents[i].status = 'changed';
            }
            else {
               scope.jobevents[i].status = 'success';
            }
            cDate = new Date(set[i].created);
            set[i].created = FormatDate(cDate);
        }
        });

    SearchInit({ scope: scope, set: 'jobevents', list: list, url: defaultUrl });

    var page = ($routeParams.page) ? parseInt($routeParams.page) - 1 : null;
    PaginateInit({ scope: scope, list: list, url: defaultUrl, page: page });

    // Called from Inventories tab, host failed events link:
    if ($routeParams.host) {
       scope[list.iterator + 'SearchField'] = 'host';
       scope[list.iterator + 'SearchValue'] = $routeParams.host;
       scope[list.iterator + 'SearchFieldLabel'] = list.fields['host'].label;
    }

    scope.search(list.iterator, $routeParams.page);

    scope.toggleChildren = function(id, children) {
        ToggleChildren({
            scope: scope,
            list: list,
            id: id,
            children: children
            });
        }

    LoadBreadCrumbs();

    scope.viewJobEvent = function(id) {
       var url = '/jobs/' + $routeParams.id + '/job_events/' + id;
       if (scope['jobeventPage']) {
          url += '?&page=' + (scope['jobeventPage'] + 1);
       }
       $location.url(url);
       }

    scope.refresh = function() {
       scope.expand = true;
       scope.search(list.iterator);
       }

    scope.jobDetails = function() {
       $location.path('/jobs/' + $routeParams.id);
       };

    scope.jobSummary = function() {
       $location.path('/jobs/' + $routeParams.id + '/job_host_summaries');
       };

}

JobEventsList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'JobEventList',
                           'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope',
                           'ProcessErrors','GetBasePath', 'LookUpInit', 'ToggleChildren', 'FormatDate'
                           ];

function JobEventsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, JobEventForm, GenerateForm,
                        Rest, Alert, ProcessErrors, LoadBreadCrumbs, ClearScope, GetBasePath, FormatDate, EventView)
{
   ClearScope('htmlTemplate');  //Garbage collection. Don't leave behind any listeners/watchers from the prior
                                //scope.
   // Inject dynamic view
   var form = JobEventForm;
   var generator = GenerateForm;
   var scope = GenerateForm.inject(form, {mode: 'edit', related: true});
   generator.reset();

   var defaultUrl = GetBasePath('base') + 'job_events/' + $routeParams.event_id + '/';
   var base = $location.path().replace(/^\//,'').split('/')[0];

   // Retrieve detail record and prepopulate the form
   Rest.setUrl(defaultUrl);
   Rest.get()
       .success( function(data, status, headers, config) {
          scope['event_display'] = data['event_display'].replace(/^\u00a0*/g,'');
          LoadBreadCrumbs({ path: '/jobs/' + $routeParams.job_id + '/job_events/' + $routeParams.event_id,
                title: scope['event_display'] });
          for (var fld in form.fields) {
              switch(fld) {
                  case 'status':
                     if (data['failed']) {
                        scope['status'] = 'error';
                     }
                     else if (data['changed']) {
                        scope['status'] = 'changed';
                     }
                     else {
                        scope['status'] = 'success';
                     }
                     break;
                  case 'created':
                     var cDate = new Date(data['created']);
                     scope['created'] = FormatDate(cDate);
                     break;
                  case 'host':
                     if (data['summary_fields'] && data['summary_fields']['host']) {
                        scope['host'] = data['summary_fields']['host']['name'];
                     }
                     break;
                  case 'id':
                  case 'task':
                  case 'play':
                     scope[fld] = data[fld];
                     break;
                  case 'start':
                  case 'end':
                    if (data['event_data'] && data['event_data']['res'] && data['event_data']['res'][fld] !== undefined) {
                       var cDate = new Date(data['event_data']['res'][fld]);
                       scope[fld] = FormatDate(cDate);
                    }
                    break;
                  case 'msg':
                  case 'stdout':
                  case 'stderr':
                  case 'delta':
                  case 'rc':
                     if (data['event_data'] && data['event_data']['res'] && data['event_data']['res'][fld] !== undefined) {
                        scope[fld] = data['event_data']['res'][fld];
                        if (form.fields[fld].type == 'textarea') {
                           var n = data['event_data']['res'][fld].match(/\n/g);
                           rows = (n) ? n.length : 1;
                           rows = (rows > 15) ? 5 : rows;
                           $('textarea[name="' + fld + '"]').attr('rows',rows);
                        }
                      }
                     break;
                  case 'conditional':
                     if (data['event_data']['res']) {
                        scope[fld] = data['event_data']['res']['is_conditional'];
                     }
                     break;
                  case 'module_name':
                  case 'module_args':
                     if (data['event_data']['res'] && data['event_data']['res']['invocation']) {
                        scope[fld] = data['event_data']['res']['invocation'][fld];
                     }
                     break;
              }
          }
          })
      .error( function(data, status, headers, config) {
          ProcessErrors(scope, data, status, null,
              { hdr: 'Error!', msg: 'Failed to retrieve host: ' + $routeParams.event_id + '. GET status: ' + status });
          });

      scope.navigateBack = function() {
          var url = '/jobs/' + $routeParams.job_id + '/job_events';
          if ($routeParams.page) {
             url += '?page=' + $routeParams.page;
          }
          $location.url(url);
          }

      scope.rawView = function() {
          EventView({"event_id": scope.id });
          }

}

JobEventsEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'JobEventForm',
                          'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ClearScope', 'GetBasePath',
                          'FormatDate', 'EventView'
                          ];
<commit_msg>Limited textarea rows to 15 on Job Events page.<commit_after>/************************************
 * Copyright (c) 2013 AnsibleWorks, Inc.
 *
 *
 *  JobEvents.js
 *
 *  Controller functions for the Job Events model.
 *
 */

'use strict';

function JobEventsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, JobEventList,
                        GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller,
                        ClearScope, ProcessErrors, GetBasePath, LookUpInit, ToggleChildren,
                        FormatDate)
{
    ClearScope('htmlTemplate');
    var list = JobEventList;
    list.base = $location.path();

    var defaultUrl = GetBasePath('jobs') + $routeParams.id  + '/job_events/'; //?parent__isnull=1';

    var view = GenerateList;
    var base = $location.path().replace(/^\//,'').split('/')[0];
    var scope = view.inject(list, { mode: 'edit' });

    $rootScope.flashMessage = null;
    scope.selected = [];
    scope.expand = true;    //on load, automatically expand all nodes

    scope.parentNode = 'parent-event';  // used in ngClass to dynamicall set row level class and control
    scope.childNode = 'child-event';    // link color and cursor

    function formatJSON(eventData) {
        //turn JSON event data into an html form
        var html = '';
        if (eventData['res']) {
           var n, rows;
           var found =  false;
           if (typeof eventData.res == 'string') {
              n = eventData['res'].match(/\n/g);
              rows = (n) ? n.length : 1;
              rows = (rows > 10) ? 10 : rows;
              found = true;
              html += "Traceback:\n";
              html += "<textarea readonly class=\"input-xxlarge\" rows=\"" + rows + "\">" + eventData.res + "</textarea>\n";
           }
           else {
              for (var fld in eventData.res) {
                  if ( (fld == 'msg' || fld == 'stdout' || fld == 'stderr') &&
                       (eventData.res[fld] !== null && eventData.res[fld] !== '') ) {
                       html += "";
                       switch(fld) {
                          case 'msg':
                          case 'stdout':
                             html += 'Output:';
                             break;
                          case 'stderr':
                             html += 'Error:';
                             break;
                       }
                       html += "\n";
                       n = eventData['res'][fld].match(/\n/g);
                       rows = (n) ? n.length : 1;
                       rows = (rows > 10) ? 10 : rows;
                       html += "<textarea readonly class=\"input-xxlarge\" rows=\"" + rows + "\">" + eventData.res[fld] + "</textarea>\n";
                       found = true;
                  }
                  if ( fld == "results" && Array.isArray(eventData.res[fld]) && eventData.res[fld].length > 0 ) {
                     html += "Results:\n";
                     //html += "<textarea readonly class="
                     var txt = '';
                     for (var i=0; i < eventData.res[fld].length; i++) {
                         txt += eventData.res[fld][i];
                     }
                     n = txt.match(/\n/g);
                     rows = (n) ? n.length : 1;
                     rows = (rows > 10) ? 10 : rows;
                     html += "<textarea readonly class=\"input-xxlarge\" rows=\"" + rows + "\">" + txt + "</textarea>\n";
                     found = true;
                  }
                  if (fld == "rc" && eventData.res[fld] != 0) {
                     html += "Return Code:\n";
                     html += "<input type=\"text\" class=\"input-mini\" value=\"" + eventData.res[fld] + "\" readonly >\n";
                     found = true;
                  }
              }
           }
           html = (found) ? "<form class=\"event-form\">\n" + html + "</form>\n" : '';
        }
        if (eventData['host']) {
           html = "" + eventData['host'] + "\n" + html;
        }
        else {
           html = (html == '' ) ? null : html;
        }
        return html;
        }

    if (scope.RemovePostRefresh) {
       scope.RemovePostRefresh();
    }
    scope.RemovePostRefresh = scope.$on('PostRefresh', function() {
        // Initialize the parent levels
        var set = scope[list.name];
        var cDate;
        for (var i=0; i < set.length; i++) {
            set[i].event_display = set[i].event_display.replace(/^\u00a0*/g,'');
            if (set[i].event_level < 3 ) {
               set[i]['ngclick'] = "toggleChildren(" + set[i].id + ", \"" + set[i].related.children + "\")";
               set[i]['ngicon'] = 'icon-collapse-alt';
               set[i]['class'] = 'parentNode';
            }
            else {
               set[i]['class'] = 'childNode';
               set[i]['event_detail'] = formatJSON(set[i].event_data);
            }
            set[i]['show'] = true;
            set[i]['spaces'] = set[i].event_level * 24;
            if (scope.jobevents[i].failed) {
               scope.jobevents[i].status = 'error';
            }
            else if (scope.jobevents[i].changed) {
               scope.jobevents[i].status = 'changed';
            }
            else {
               scope.jobevents[i].status = 'success';
            }
            cDate = new Date(set[i].created);
            set[i].created = FormatDate(cDate);
        }
        });

    SearchInit({ scope: scope, set: 'jobevents', list: list, url: defaultUrl });

    var page = ($routeParams.page) ? parseInt($routeParams.page) - 1 : null;
    PaginateInit({ scope: scope, list: list, url: defaultUrl, page: page });

    // Called from Inventories tab, host failed events link:
    if ($routeParams.host) {
       scope[list.iterator + 'SearchField'] = 'host';
       scope[list.iterator + 'SearchValue'] = $routeParams.host;
       scope[list.iterator + 'SearchFieldLabel'] = list.fields['host'].label;
    }

    scope.search(list.iterator, $routeParams.page);

    scope.toggleChildren = function(id, children) {
        ToggleChildren({
            scope: scope,
            list: list,
            id: id,
            children: children
            });
        }

    LoadBreadCrumbs();

    scope.viewJobEvent = function(id) {
       var url = '/jobs/' + $routeParams.id + '/job_events/' + id;
       if (scope['jobeventPage']) {
          url += '?&page=' + (scope['jobeventPage'] + 1);
       }
       $location.url(url);
       }

    scope.refresh = function() {
       scope.expand = true;
       scope.search(list.iterator);
       }

    scope.jobDetails = function() {
       $location.path('/jobs/' + $routeParams.id);
       };

    scope.jobSummary = function() {
       $location.path('/jobs/' + $routeParams.id + '/job_host_summaries');
       };

}

JobEventsList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'JobEventList',
                           'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope',
                           'ProcessErrors','GetBasePath', 'LookUpInit', 'ToggleChildren', 'FormatDate'
                           ];

function JobEventsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, JobEventForm, GenerateForm,
                        Rest, Alert, ProcessErrors, LoadBreadCrumbs, ClearScope, GetBasePath, FormatDate, EventView)
{
   ClearScope('htmlTemplate');  //Garbage collection. Don't leave behind any listeners/watchers from the prior
                                //scope.
   // Inject dynamic view
   var form = JobEventForm;
   var generator = GenerateForm;
   var scope = GenerateForm.inject(form, {mode: 'edit', related: true});
   generator.reset();

   var defaultUrl = GetBasePath('base') + 'job_events/' + $routeParams.event_id + '/';
   var base = $location.path().replace(/^\//,'').split('/')[0];

   // Retrieve detail record and prepopulate the form
   Rest.setUrl(defaultUrl);
   Rest.get()
       .success( function(data, status, headers, config) {
          scope['event_display'] = data['event_display'].replace(/^\u00a0*/g,'');
          LoadBreadCrumbs({ path: '/jobs/' + $routeParams.job_id + '/job_events/' + $routeParams.event_id,
                title: scope['event_display'] });
          for (var fld in form.fields) {
              switch(fld) {
                  case 'status':
                     if (data['failed']) {
                        scope['status'] = 'error';
                     }
                     else if (data['changed']) {
                        scope['status'] = 'changed';
                     }
                     else {
                        scope['status'] = 'success';
                     }
                     break;
                  case 'created':
                     var cDate = new Date(data['created']);
                     scope['created'] = FormatDate(cDate);
                     break;
                  case 'host':
                     if (data['summary_fields'] && data['summary_fields']['host']) {
                        scope['host'] = data['summary_fields']['host']['name'];
                     }
                     break;
                  case 'id':
                  case 'task':
                  case 'play':
                     scope[fld] = data[fld];
                     break;
                  case 'start':
                  case 'end':
                    if (data['event_data'] && data['event_data']['res'] && data['event_data']['res'][fld] !== undefined) {
                       var cDate = new Date(data['event_data']['res'][fld]);
                       scope[fld] = FormatDate(cDate);
                    }
                    break;
                  case 'msg':
                  case 'stdout':
                  case 'stderr':
                  case 'delta':
                  case 'rc':
                     if (data['event_data'] && data['event_data']['res'] && data['event_data']['res'][fld] !== undefined) {
                        scope[fld] = data['event_data']['res'][fld];
                        if (form.fields[fld].type == 'textarea') {
                           var n = data['event_data']['res'][fld].match(/\n/g);
                           rows = (n) ? n.length : 1;
                           rows = (rows > 15) ? 5 : rows;
                           $('textarea[name="' + fld + '"]').attr('rows',rows);
                        }
                      }
                     break;
                  case 'conditional':
                     if (data['event_data']['res']) {
                        scope[fld] = data['event_data']['res']['is_conditional'];
                     }
                     break;
                  case 'module_name':
                  case 'module_args':
                     if (data['event_data']['res'] && data['event_data']['res']['invocation']) {
                        scope[fld] = data['event_data']['res']['invocation'][fld];
                     }
                     break;
              }
          }
          })
      .error( function(data, status, headers, config) {
          ProcessErrors(scope, data, status, null,
              { hdr: 'Error!', msg: 'Failed to retrieve host: ' + $routeParams.event_id + '. GET status: ' + status });
          });

      scope.navigateBack = function() {
          var url = '/jobs/' + $routeParams.job_id + '/job_events';
          if ($routeParams.page) {
             url += '?page=' + $routeParams.page;
          }
          $location.url(url);
          }

      scope.rawView = function() {
          EventView({"event_id": scope.id });
          }

}

JobEventsEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'JobEventForm',
                          'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ClearScope', 'GetBasePath',
                          'FormatDate', 'EventView'
                          ];
<|endoftext|>


<commit_before>'use strict';

var workersUtils = require('../utils');
var controllersUtils = require('../../app/utils/controllers');
var request = require('request');
var SINCE_ID_STORE_PREFIX = 'facebook:since:';
var SERVER = 'https://graph.facebook.com/v2.5';
var facebookJobs = {
  A_FB_SYNC: facebookSyncJob,
  A_FB_SIGNUP: facebookSignupJob,
  A_FB_LOGIN: facebookLoginJob,
};

module.exports = facebookJobs;

function facebookSignupJob(context, event) {
  return context.db.collection('users').findOne({
    _id: event.contents.user_id,
  })
  .then(pairFacebookFriends.bind(null, context));
}

function facebookLoginJob(context, event) {
  return context.db.collection('users').findOne({
    _id: event.contents.user_id,
  })
  .then(pairFacebookFriends.bind(null, context));
}

// https://developers.facebook.com/docs/graph-api/reference/v2.5/user/friends
function pairFacebookFriends(context, user) {
  return new Promise(function pairFriendsPromise(resolve, reject) {
    request.get(
      SERVER + '/me/friends?access_token=' +
      user.auth.facebook.accessToken,
      function pairFriendsCallback(err, res, body) {
        if(err) {
          return reject(err);
        }
        if(200 > res.statusCode || 300 <= res.statusCode) {
          return reject(new Error('E_BAD_RESPONSE'));
        }
        context.logger.debug('Retrieved facebook friends:', res.statusCode, body);
        // Update friends that are know in the platform
        return context.db.collection('users').find({
          'auth.facebook.id': { $in: JSON.parse(body).data.map(function(friend) {
            return friend.id;
          }) },
        }, {
          _id: '',
        }).toArray().then(function(friends) {
          var friendsIds = friends.map(function(friend) {
            return friend._id;
          });

          if(!friendsIds.length) {
            return Promise.resolve();
          }

          return Promise.all([
            context.db.collection('users').updateMany({
              _id: { $in: friendsIds },
            }, {
              $addToSet: {
                friends_ids: user._id,
              },
            }),
            context.db.collection('users').updateOne({
              _id: user._id,
            }, {
              $addToSet: {
                friends_ids: { $each: friendsIds },
              },
            }),
          ]);
        })
        .then(resolve)
        .catch(reject);
      }
    );
  });
}

// https://developers.facebook.com/docs/graph-api/reference/v2.5/user/feed
function facebookSyncJob(context) {
  return workersUtils.getCurrentTrips(context)
  .then(function handleCurrentTrips(tripsEvents) {
    if(!tripsEvents.length) {
      return Promise.resolve();
    }
    return Promise.all(tripsEvents.map(function(tripEvent) {
      context.logger.debug('Retrieving trip facebook users for:', tripEvent.trip.title);
      return context.db.collection('users').find({
        _id: { $in: tripEvent.trip.friends_ids.concat(tripEvent.owner_id) },
        'auth.facebook': { $exists: true },
      }).toArray()
      .then(function(users) {
        context.logger.debug('Found ' + users.length + ' facebook users for:', tripEvent.trip.title);
        return Promise.all(users.map(function(user) {
          var newSince;

          context.logger.debug('Getting ' + user.contents.name + ' twitts');
          return context.store.get(
            SINCE_ID_STORE_PREFIX + tripEvent._id.toString() + ':' +
            user._id.toString()
          )
          .then(function(since) {
            since = since || Math.floor(
              (tripEvent.created.seal_date).getTime() / 1000
            );
            return new Promise(function pairFriendsPromise(resolve, reject) {
              request.get(
                SERVER + '/me/posts' +
                '?access_token=' + user.auth.facebook.accessToken +
                '&fields=description,caption,link,created_time,id,photos,' +
                'application,from,icon,message,message_tags,name,picture,place,' +
                'status_type,type' +
                '&since=' + since,
                function retrieveStatusCallback(err, res, body) {
                  var statuses;

                  if(err) {
                    return reject(err);
                  }
                  if(200 > res.statusCode || 300 <= res.statusCode) {
                    return reject(new Error('E_BAD_RESPONSE'));
                  }
                  try {
                    statuses = JSON.parse(body).data || [];
                  } catch(err) {
                    return reject(err);
                  }
                  context.logger.debug('Retrieved facebook statuses:', res.statusCode, body);
                  context.logger.debug('Fetched ' + statuses.length +
                    ' statuses sent by ' + user.contents.name, since);
                  if(!statuses.length) {
                    return resolve();
                  }
                  newSince = Math.round(
                    (new Date(statuses[0].created_time)).getTime() / 1000
                  );
                  statuses = statuses.filter(function(status) {
                    return status.message &&
                      -1 !== ['photo', 'status', 'link'].indexOf(status.type);
                  });
                  Promise.all(statuses.map(function(status) {
                    var statusDate = new Date(status.created_time);

                    if(tripEvent.created.seal_date.getTime() > statusDate.getTime()) {
                      return Promise.resolve();
                    }
                    return context.db.collection('events').findOneAndUpdate({
                      'contents.facebookId': status.id,
                    }, {
                      $set: {
                        'contents.facebookId': status.id,
                        'contents.text': status.message,
                        'contents.geo': status.place && status.place.location &&
                          'undefined' === typeof status.place.location.latitude &&
                          'undefined' === typeof status.place.location.longitude ? [
                            status.place.location.latitude,
                            status.place.location.longitude,
                            0,
                          ] : [],
                        'contents.user_name': status.from.name,
                      },
                      $setOnInsert: {
                        _id: context.createObjectId(),
                        owner_id: user._id,
                        'contents.trip_id': tripEvent._id,
                        'contents.type': 'facebook-' + status.type,
                        trip: tripEvent.trip,
                        created: controllersUtils.getDateSeal(statusDate),
                      },
                    }, {
                      upsert: true,
                      returnOriginal: false,
                    })
                    .then(function(result) {
                      context.bus.trigger({
                        exchange: 'A_TRIP_UPDATED',
                        contents: {
                          trip_id: tripEvent._id,
                          event_id: result.value._id,
                          users_ids: tripEvent.trip.friends_ids.concat([tripEvent.owner_id]),
                        },
                      });
                    });
                  }))
                  .then(context.store.set.bind(
                    null,
                    SINCE_ID_STORE_PREFIX + tripEvent._id.toString() + ':' +
                    user._id.toString(),
                    newSince
                  ))
                  .then(resolve)
                  .catch(reject);
                }
              );
            });
          });
        }));
      });
    }));
  });
}
<commit_msg>More feedback about fb syncs<commit_after>'use strict';

var workersUtils = require('../utils');
var controllersUtils = require('../../app/utils/controllers');
var request = require('request');
var YError = require('yerror');
var SINCE_ID_STORE_PREFIX = 'facebook:since:';
var SERVER = 'https://graph.facebook.com/v2.5';
var facebookJobs = {
  A_FB_SYNC: facebookSyncJob,
  A_FB_SIGNUP: facebookSignupJob,
  A_FB_LOGIN: facebookLoginJob,
};

module.exports = facebookJobs;

function facebookSignupJob(context, event) {
  return context.db.collection('users').findOne({
    _id: event.contents.user_id,
  })
  .then(pairFacebookFriends.bind(null, context));
}

function facebookLoginJob(context, event) {
  return context.db.collection('users').findOne({
    _id: event.contents.user_id,
  })
  .then(pairFacebookFriends.bind(null, context));
}

// https://developers.facebook.com/docs/graph-api/reference/v2.5/user/friends
function pairFacebookFriends(context, user) {
  return new Promise(function pairFriendsPromise(resolve, reject) {
    request.get(
      SERVER + '/me/friends?access_token=' +
      user.auth.facebook.accessToken,
      function pairFriendsCallback(err, res, body) {
        if(err) {
          return reject(err);
        }
        if(200 > res.statusCode || 300 <= res.statusCode) {
          return reject(new Error('E_BAD_RESPONSE'));
        }
        context.logger.debug('Retrieved facebook friends:', res.statusCode, body);
        // Update friends that are know in the platform
        return context.db.collection('users').find({
          'auth.facebook.id': { $in: JSON.parse(body).data.map(function(friend) {
            return friend.id;
          }) },
        }, {
          _id: '',
        }).toArray().then(function(friends) {
          var friendsIds = friends.map(function(friend) {
            return friend._id;
          });

          if(!friendsIds.length) {
            return Promise.resolve();
          }

          return Promise.all([
            context.db.collection('users').updateMany({
              _id: { $in: friendsIds },
            }, {
              $addToSet: {
                friends_ids: user._id,
              },
            }),
            context.db.collection('users').updateOne({
              _id: user._id,
            }, {
              $addToSet: {
                friends_ids: { $each: friendsIds },
              },
            }),
          ]);
        })
        .then(resolve)
        .catch(reject);
      }
    );
  });
}

// https://developers.facebook.com/docs/graph-api/reference/v2.5/user/feed
function facebookSyncJob(context) {
  return workersUtils.getCurrentTrips(context)
  .then(function handleCurrentTrips(tripsEvents) {
    if(!tripsEvents.length) {
      return Promise.resolve();
    }
    return Promise.all(tripsEvents.map(function(tripEvent) {
      context.logger.debug('Retrieving trip facebook users for:', tripEvent.trip.title);
      return context.db.collection('users').find({
        _id: { $in: tripEvent.trip.friends_ids.concat(tripEvent.owner_id) },
        'auth.facebook': { $exists: true },
      }).toArray()
      .then(function(users) {
        context.logger.debug('Found ' + users.length + ' facebook users for:', tripEvent.trip.title);
        return Promise.all(users.map(function(user) {
          var newSince;

          context.logger.debug('Getting ' + user.contents.name + ' statuses.');
          return context.store.get(
            SINCE_ID_STORE_PREFIX + tripEvent._id.toString() + ':' +
            user._id.toString()
          )
          .then(function(since) {
            since = since || Math.floor(
              (tripEvent.created.seal_date).getTime() / 1000
            );
            return new Promise(function pairFriendsPromise(resolve, reject) {
              request.get(
                SERVER + '/me/posts' +
                '?access_token=' + user.auth.facebook.accessToken +
                '&fields=description,caption,link,created_time,id,photos,' +
                'application,from,icon,message,message_tags,name,picture,place,' +
                'status_type,type' +
                '&since=' + since,
                function retrieveStatusCallback(err, res, body) {
                  var statuses;

                  if(err) {
                    return reject(err);
                  }
                  if(200 > res.statusCode || 300 <= res.statusCode) {
                    context.logger.debug(
                      'Facebook statuses retrieval failed:', res.statusCode, body
                    );
                    return resolve();
                  }
                  try {
                    statuses = JSON.parse(body).data || [];
                  } catch(err) {
                    return reject(err);
                  }
                  context.logger.debug('Retrieved facebook statuses:', res.statusCode, body);
                  context.logger.debug(
                    'Fetched ' + statuses.length +
                    ' statuses sent by ' + user.contents.name + ' since ' +
                    (new Date(since * 1000)).toISOString()
                  );
                  if(!statuses.length) {
                    return resolve();
                  }
                  newSince = Math.round(
                    (new Date(statuses[0].created_time)).getTime() / 1000
                  );
                  statuses = statuses.filter(function(status) {
                    return status.message &&
                      -1 !== ['photo', 'status', 'link'].indexOf(status.type);
                  });
                  Promise.all(statuses.map(function(status) {
                    var statusDate = new Date(status.created_time);

                    if(tripEvent.created.seal_date.getTime() > statusDate.getTime()) {
                      return Promise.resolve();
                    }
                    return context.db.collection('events').findOneAndUpdate({
                      'contents.facebookId': status.id,
                    }, {
                      $set: {
                        'contents.facebookId': status.id,
                        'contents.text': status.message,
                        'contents.geo': status.place && status.place.location &&
                          'undefined' === typeof status.place.location.latitude &&
                          'undefined' === typeof status.place.location.longitude ? [
                            status.place.location.latitude,
                            status.place.location.longitude,
                            0,
                          ] : [],
                        'contents.user_name': status.from.name,
                      },
                      $setOnInsert: {
                        _id: context.createObjectId(),
                        owner_id: user._id,
                        'contents.trip_id': tripEvent._id,
                        'contents.type': 'facebook-' + status.type,
                        trip: tripEvent.trip,
                        created: controllersUtils.getDateSeal(statusDate),
                      },
                    }, {
                      upsert: true,
                      returnOriginal: false,
                    })
                    .then(function(result) {
                      context.bus.trigger({
                        exchange: 'A_TRIP_UPDATED',
                        contents: {
                          trip_id: tripEvent._id,
                          event_id: result.value._id,
                          users_ids: tripEvent.trip.friends_ids.concat([tripEvent.owner_id]),
                        },
                      });
                    });
                  }))
                  .then(context.store.set.bind(
                    null,
                    SINCE_ID_STORE_PREFIX + tripEvent._id.toString() + ':' +
                    user._id.toString(),
                    newSince
                  ))
                  .then(resolve)
                  .catch(reject);
                }
              );
            });
          });
        }));
      });
    }));
  });
}
<|endoftext|>


<commit_before>var q = require('q')
  , pg = require('pg');

module.exports = {
  configure: function(config) {
    module.config = config;
    return 'configured';
  },
  select: function(opts) {
    var sql = 'select '
      , params
      , ids = opts.params
      , fields = opts.fields
      , table = opts.table;

    // List the fields to select
    if (fields.length == 1) {
      sql += fields[0];
    } else {
      sql += fields.join(',');
      // fields = ['name','email']
      // => select name,email from ...
    }

    // The rest of the SQL
    sql += ' from ' + table + ' where id in (' + paramList(ids.length) + ')';

    console.log('Query: ', sql);
    console.log('Params: ', ids);

    return query(sql, ids);
  },
  selectAll: function(tableName) {
    return query('select * from ' + tableName);
  },
  selectWhere: function(opts) {
    var sql = 'select '
      , params = opts.params
      , fields = opts.fields
      , table = opts.table
      , paramName = opts.searchBy || 'id';

    if (fields.length == 1) {
      sql += fields[0];
    } else {
      sql += fields.join(',');
    }

    sql += ' from ' + table + ' where ' + paramName + ' in (' + paramList(params.length) + ');';

    console.log('Query: ', sql);
    console.log('Params: ', params);

    return query(sql, params);
  },
  insert: function(item, tableName) {
    var sql = 'INSERT INTO ' + tableName + ' ('
      , props = Object.getOwnPropertyNames(item)
      , fields
      , values = [];

    if (props.length == 1) {
      fields = props[0];
      values.push(getValue(item[fields]));
    } else {
      fields = props.join(',');
      props.forEach(function (propName) {
        values.push(getValue(item[propName]));
      });
    }

    sql += fields + ') VALUES (' + paramList(props.length) + ')';

    return query(sql, values).then(function() {
      return 'Query successful!';
    });
  },
  removeWhere: function (opts) {
    var sql = 'DELETE FROM ' + opts.tableName
      , params = opts.params
      , paramName = opts.removeBy || 'id';

    sql += ' WHERE ' + paramName + ' in (' + paramList(params.length) + ');';

    console.log('Query:', sql);
    return query(sql, params);
  }
  query: query
}

function getValue(value) {
  switch (value) {
    case null:
    return 'NULL';
    case undefined:
    case '':
    return '""';
  }
  return value;
}

function query(sql, params) {
  var deferred = q.defer();
  var p = params || [];

  pg.connect(module.config, function(err, client, done) {
    if(err) {
      console.error(err);
    }

    // If no params passed
    if(p.length < 1) {
      client.query(sql, function(error, result) {
        if(error) {
          console.error('ONOEZ: ', error)
        }
        var res = result.rows;
        done();
        deferred.resolve(res);
      });
    } else {
      client.query(sql, p, function(error, result) {
        if(error) {
          console.error('ONOEZ: ', error)
        }
        var res = result.rows;
        done();
        deferred.resolve(res);
      });
    }
  });

  return deferred.promise;
}

function paramList(count) {
  var index = 1
    , params = [];
  while (index <= count) {
    params.push('$' + index);
    index += 1;
  }
  return params.join();
}
// paramList(3)
// => $1,$2,$3<commit_msg>Fix of syntax error<commit_after>var q = require('q')
  , pg = require('pg');

module.exports = {
  configure: function(config) {
    module.config = config;
    return 'configured';
  },
  select: function(opts) {
    var sql = 'select '
      , params
      , ids = opts.params
      , fields = opts.fields
      , table = opts.table;

    // List the fields to select
    if (fields.length == 1) {
      sql += fields[0];
    } else {
      sql += fields.join(',');
      // fields = ['name','email']
      // => select name,email from ...
    }

    // The rest of the SQL
    sql += ' from ' + table + ' where id in (' + paramList(ids.length) + ')';

    console.log('Query: ', sql);
    console.log('Params: ', ids);

    return query(sql, ids);
  },
  selectAll: function(tableName) {
    return query('select * from ' + tableName);
  },
  selectWhere: function(opts) {
    var sql = 'select '
      , params = opts.params
      , fields = opts.fields
      , table = opts.table
      , paramName = opts.searchBy || 'id';

    if (fields.length == 1) {
      sql += fields[0];
    } else {
      sql += fields.join(',');
    }

    sql += ' from ' + table + ' where ' + paramName + ' in (' + paramList(params.length) + ');';

    console.log('Query: ', sql);
    console.log('Params: ', params);

    return query(sql, params);
  },
  insert: function(item, tableName) {
    var sql = 'INSERT INTO ' + tableName + ' ('
      , props = Object.getOwnPropertyNames(item)
      , fields
      , values = [];

    if (props.length == 1) {
      fields = props[0];
      values.push(getValue(item[fields]));
    } else {
      fields = props.join(',');
      props.forEach(function (propName) {
        values.push(getValue(item[propName]));
      });
    }

    sql += fields + ') VALUES (' + paramList(props.length) + ')';

    return query(sql, values).then(function() {
      return 'Query successful!';
    });
  },
  removeWhere: function (opts) {
    var sql = 'DELETE FROM ' + opts.tableName
      , params = opts.params
      , paramName = opts.removeBy || 'id';

    sql += ' WHERE ' + paramName + ' in (' + paramList(params.length) + ');';

    console.log('Query:', sql);
    return query(sql, params);
  },
  query: query
}

function getValue(value) {
  switch (value) {
    case null:
    return 'NULL';
    case undefined:
    case '':
    return '""';
  }
  return value;
}

function query(sql, params) {
  var deferred = q.defer();
  var p = params || [];

  pg.connect(module.config, function(err, client, done) {
    if(err) {
      console.error(err);
    }

    // If no params passed
    if(p.length < 1) {
      client.query(sql, function(error, result) {
        if(error) {
          console.error('ONOEZ: ', error)
        }
        var res = result.rows;
        done();
        deferred.resolve(res);
      });
    } else {
      client.query(sql, p, function(error, result) {
        if(error) {
          console.error('ONOEZ: ', error)
        }
        var res = result.rows;
        done();
        deferred.resolve(res);
      });
    }
  });

  return deferred.promise;
}

function paramList(count) {
  var index = 1
    , params = [];
  while (index <= count) {
    params.push('$' + index);
    index += 1;
  }
  return params.join();
}
// paramList(3)
// => $1,$2,$3<|endoftext|>


<commit_before>(function() {

  // Global object reference.
  var root = this;

  // Global export.
  var Quilt = root.Quilt = {};

  // Current library version.
  Quilt.VERSION = '0.0.1';

  // Find dashes in attribute names.
  var undasher = /-([a-z]|[0-9])/ig;

  // Camel case data attributes.
  var camel = function(match, letter) {
    return (letter + '').toUpperCase();
  };

  // # Quilt.View
  // Provide a structure for declaring functionality through data attributes.
  var View = Quilt.View = Backbone.View.extend({

    constructor: function(options) {
      if (options && options.template) this.template = options.template;
      Backbone.View.apply(this, arguments);
    },

    // After executing the template function, search the view for relevant
    // patches, match them with handlers and execute them.  If a handler
    // returns a view, store it for clean up.
    render: function() {

      // Dispose of old views.
      if (this.views) _.invoke(this.views, 'dispose');

      // Views without a template don't need to render patches.
      if (!this.template) return this;

      // Render the template.
      this.$el.html(this.template());

      // Render patches.
      _.each(this.$('*'), this._renderPatches, this);

      return this;
    },

    addView: function(view) {
      if (!this.views) this.views = [];
      this.views.push(view);
    },

    renderView: function(view) {
      this.addView(view.render());
    },

    // Dispose of child views.
    dispose: function() {
      this.stopListening();
      this.undelegateEvents();
      if (this.views) _.invoke(this.views, 'dispose');
      return this;
    },

    _renderPatches: document.createElement('a').dataset ?

    function(el) {
      for (var name in el.dataset) this._renderPatch(el, name);
    } :

    function(el) {
      var names;
      var attrs = el.attributes;

      // The attributes array may be altered while we iterate so isolate the
      // data attribute names.
      for (var i = 0, length = attrs.length; i < length; i++) {
        var attr = attrs[i];

        // IE8- will enumerate unspecified attributes.
        if (!attr.specified) continue;

        // Bail unless we have a data attribute.
        var name = attr.name;
        if (name.lastIndexOf('data-', 0) !== 0) continue;

        if (!names) names = [];
        names.push(name);
      }

      // Bail out if no data attributes were found.
      if (!names) return;

      for (var i = 0, length = names.length; i < length; i++) {
        this._renderPatch(el, names[i].slice(5).replace(undasher, camel));
      }
    },

    _renderPatch: function(el, name) {
      // Bail on attributes with no corresponding patch.
      var patch = Quilt.patches[name];
      if (!patch) return;

      // Execute the handler.
      var view = patch.call(this, el, $(el).data(name));

      // Render the view if appropriate.
      if (view instanceof View) this.renderView(view);
    }

  });

  // # Quilt.patches
  //
  // Patch handlers should be specified in camel case.  The arguments to
  // each handler will be a DOM element and the value of the data attribute.
  // The handler will be called with the parent view as context.
  //
  //     Quilt.patches.exampleAttr = function(element, options) {
  //       // Called for elements with a "data-example-attr" attribute.
  //     };
  //
  var patches = Quilt.patches = {};

  // # ref
  //
  // Create a reference to a specific DOM element, accessible from the view
  // as '$' + name.

  patches.ref = function(el, name) {
    this['$' + name] = $(el);
  };

  // # Show
  //
  // Listen for changes to an attribute and show the element if the value is
  // truthy, hiding it otherwise.

  var Show = Quilt.Show = Quilt.View.extend({

    initialize: function(options) {
      if (!this.model) return;
      this.attr = options.attr;
      this.listenTo(this.model, 'change:' + this.attr, this.render);
    },

    render: function() {
      if (!this.model) return this;
      var value = this.model.get(this.attr);
      this.$el.toggleClass('hide', this.invert ? !!value : !value);
      return this;
    }

  });

  patches.show = function(el, attr) {
    return new Show({
      el: el,
      attr: attr,
      model: this.model
    });
  };

  // # Hide
  //
  // Listen for changes to an attribute and hide the element if the value is
  // truthy, showing it otherwise.

  var Hide = Quilt.Hide = Show.extend({invert: true});

  patches.hide = function(el, attr) {
    return new Hide({
      el: el,
      attr: attr,
      model: this.model
    });
  };

  // # Html
  //
  // Listen for changes to an attribute, updating the element's content with
  // it's value.

  var Html = Quilt.Html = Quilt.View.extend({

    value: function() {
      return this.model.get(this.attr);
    },

    initialize: function(options) {
      this.attr = options.attr;
      if (!this.model) return;
      this.listenTo(this.model, 'change:' + this.attr, this.render);
    },

    render: function() {
      if (!this.model) return this;
      this.$el.html(this.value());
      return this;
    }

  });

  patches.html = function(el, attr) {
    return new Html({
      el: el,
      attr: attr,
      model: this.model
    });
  };

  // # Text
  //
  // Listen for changes to an attribute, updating the element's content with
  // it's escaped value.

  var Text = Quilt.Text = Html.extend({

    value: function() {
      return this.model.escape(this.attr);
    }

  });

  patches.text = function(el, attr) {
    return new Text({
      el: el,
      attr: attr,
      model: this.model
    });
  };

  // # Attrs
  //
  // Listen for changes to attributes and update the element's attributes
  // accordingly.

  var Attrs = Quilt.Attrs = Quilt.View.extend({

    accessor: 'attr',

    initialize: function(options) {
      if (!this.model) return;
      _.each(_.keys(this.map = options.map), this.setAndListen, this);
    },

    setAndListen: function(target) {
      var source = this.map[target];
      var event = 'change:' + source;

      // Set initial value.
      this.$el[this.accessor](target, this.model.get(source));

      // Listen for changes.
      this.listenTo(this.model, event, function(model, value) {
        this.$el[this.accessor](target, value);
      });
    }

  });

  patches.attrs = function(el, attrs) {
    return new Attrs({
      el: el,
      map: attrs,
      model: this.model
    });
  };

  // # Props
  //
  // Listen for changes to attributes and update the element's properties
  // accordingly.

  var Props = Quilt.Props = Attrs.extend({accessor: 'prop'});

  patches.props = function(el, props) {
    return new Props({
      el: el,
      map: props,
      model: this.model
    });
  };

  // # Css
  //
  // Listen for changes to attributes and update the element's style
  // accordingly.

  var Css = Quilt.Css = Attrs.extend({accessor: 'css'});

  patches.css = function(el, css) {
    return new Css({
      el: el,
      map: css,
      model: this.model
    });
  };

})();
<commit_msg>One more thing...<commit_after>(function() {

  // Global object reference.
  var root = this;

  // Global export.
  var Quilt = root.Quilt = {};

  // Current library version.
  Quilt.VERSION = '0.0.1';

  // Find dashes in attribute names.
  var undasher = /-([a-z]|[0-9])/ig;

  // Camel case data attributes.
  var camel = function(match, letter) {
    return (letter + '').toUpperCase();
  };

  // # Quilt.View
  // Provide a structure for declaring functionality through data attributes.
  var View = Quilt.View = Backbone.View.extend({

    constructor: function(options) {
      if (options && options.template) this.template = options.template;
      Backbone.View.apply(this, arguments);
    },

    // After executing the template function, search the view for relevant
    // patches, match them with handlers and execute them.  If a handler
    // returns a view, store it for clean up.
    render: function() {

      // Dispose of old views.
      if (this.views) _.invoke(this.views, 'dispose');

      // Views without a template don't need to render patches.
      if (!this.template) return this;

      // Render the template.
      this.$el.html(this.template());

      // Render patches.
      _.each(this.$('*'), this._renderPatches, this);

      return this;
    },

    addView: function(view) {
      if (!this.views) this.views = [];
      this.views.push(view);
    },

    renderView: function(view) {
      this.addView(view.render());
    },

    // Dispose of child views.
    dispose: function() {
      this.stopListening();
      this.undelegateEvents();
      if (this.views) _.invoke(this.views, 'dispose');
      return this;
    },

    _renderPatches: document.createElement('a').dataset ?

    function(el) {
      for (var name in el.dataset) this._renderPatch(el, name);
    } :

    function(el) {
      var names;
      var attrs = el.attributes;

      // The attributes array may be altered while we iterate so isolate the
      // data attribute names.
      for (var i = 0, length = attrs.length; i < length; i++) {
        var attr = attrs[i];

        // IE8- will enumerate unspecified attributes.
        if (!attr.specified) continue;

        // Bail unless we have a data attribute.
        var name = attr.name;
        if (name.lastIndexOf('data-', 0) !== 0) continue;

        if (!names) names = [];
        names.push(name.slice(5).replace(undasher, camel));
      }

      // Bail out if no data attributes were found.
      if (!names) return;

      for (var i = 0, length = names.length; i < length; i++) {
        this._renderPatch(el, names[i]);
      }
    },

    _renderPatch: function(el, name) {
      // Bail on attributes with no corresponding patch.
      var patch = Quilt.patches[name];
      if (!patch) return;

      // Execute the handler.
      var view = patch.call(this, el, $(el).data(name));

      // Render the view if appropriate.
      if (view instanceof View) this.renderView(view);
    }

  });

  // # Quilt.patches
  //
  // Patch handlers should be specified in camel case.  The arguments to
  // each handler will be a DOM element and the value of the data attribute.
  // The handler will be called with the parent view as context.
  //
  //     Quilt.patches.exampleAttr = function(element, options) {
  //       // Called for elements with a "data-example-attr" attribute.
  //     };
  //
  var patches = Quilt.patches = {};

  // # ref
  //
  // Create a reference to a specific DOM element, accessible from the view
  // as '$' + name.

  patches.ref = function(el, name) {
    this['$' + name] = $(el);
  };

  // # Show
  //
  // Listen for changes to an attribute and show the element if the value is
  // truthy, hiding it otherwise.

  var Show = Quilt.Show = Quilt.View.extend({

    initialize: function(options) {
      if (!this.model) return;
      this.attr = options.attr;
      this.listenTo(this.model, 'change:' + this.attr, this.render);
    },

    render: function() {
      if (!this.model) return this;
      var value = this.model.get(this.attr);
      this.$el.toggleClass('hide', this.invert ? !!value : !value);
      return this;
    }

  });

  patches.show = function(el, attr) {
    return new Show({
      el: el,
      attr: attr,
      model: this.model
    });
  };

  // # Hide
  //
  // Listen for changes to an attribute and hide the element if the value is
  // truthy, showing it otherwise.

  var Hide = Quilt.Hide = Show.extend({invert: true});

  patches.hide = function(el, attr) {
    return new Hide({
      el: el,
      attr: attr,
      model: this.model
    });
  };

  // # Html
  //
  // Listen for changes to an attribute, updating the element's content with
  // it's value.

  var Html = Quilt.Html = Quilt.View.extend({

    value: function() {
      return this.model.get(this.attr);
    },

    initialize: function(options) {
      this.attr = options.attr;
      if (!this.model) return;
      this.listenTo(this.model, 'change:' + this.attr, this.render);
    },

    render: function() {
      if (!this.model) return this;
      this.$el.html(this.value());
      return this;
    }

  });

  patches.html = function(el, attr) {
    return new Html({
      el: el,
      attr: attr,
      model: this.model
    });
  };

  // # Text
  //
  // Listen for changes to an attribute, updating the element's content with
  // it's escaped value.

  var Text = Quilt.Text = Html.extend({

    value: function() {
      return this.model.escape(this.attr);
    }

  });

  patches.text = function(el, attr) {
    return new Text({
      el: el,
      attr: attr,
      model: this.model
    });
  };

  // # Attrs
  //
  // Listen for changes to attributes and update the element's attributes
  // accordingly.

  var Attrs = Quilt.Attrs = Quilt.View.extend({

    accessor: 'attr',

    initialize: function(options) {
      if (!this.model) return;
      _.each(_.keys(this.map = options.map), this.setAndListen, this);
    },

    setAndListen: function(target) {
      var source = this.map[target];
      var event = 'change:' + source;

      // Set initial value.
      this.$el[this.accessor](target, this.model.get(source));

      // Listen for changes.
      this.listenTo(this.model, event, function(model, value) {
        this.$el[this.accessor](target, value);
      });
    }

  });

  patches.attrs = function(el, attrs) {
    return new Attrs({
      el: el,
      map: attrs,
      model: this.model
    });
  };

  // # Props
  //
  // Listen for changes to attributes and update the element's properties
  // accordingly.

  var Props = Quilt.Props = Attrs.extend({accessor: 'prop'});

  patches.props = function(el, props) {
    return new Props({
      el: el,
      map: props,
      model: this.model
    });
  };

  // # Css
  //
  // Listen for changes to attributes and update the element's style
  // accordingly.

  var Css = Quilt.Css = Attrs.extend({accessor: 'css'});

  patches.css = function(el, css) {
    return new Css({
      el: el,
      map: css,
      model: this.model
    });
  };

})();
<|endoftext|>


<commit_before>/**
 * @fileoverview Range input replacement
 * @author NathanG
 * @license MIT
 * @version 0.0.3
 */

(function() {
  'use strict';

  (function(Range) {
    var define = window.define || null;

    if(typeof define === 'function' && define.amd) {
      define('range', [], function(){ return Range; });
    } else {
      window.Range = Range;
    }
  })((function(document, window) {

    /**
     * Helper methods
     *
     * @class H
     * @private
     */
    var H = {
      /** custom event cache */
      _events: {},

      /**
       * @param eventName {string} - name of event to be created
       */
      createEvent: function(eventName) {
        var event;

        if (document.createEvent) {
          event = document.createEvent('HTMLEvents');
          event.initEvent(eventName, true, true);
        } else {
          event = document.createEventObject();
          event.eventType = eventName;
        }

        event.eventName = eventName;

        this._events[eventName] = event;

        return event;
      },

      /**
       * @param el {object} - dom node to recieve event
       * @param eventName {string} - name of event to fire
       */
      fireEvent: function(el, eventName) {
        var event = this._events[eventName] || this.createEvent(eventName);

        if (document.createEvent) {
          el.dispatchEvent(event);
        } else {
          el.fireEvent('on' + event.eventType, event);
        }
      }
    };

    /**
     * Represents a range input
     *
     * @class Range
     * @param {object} el - range input to recieve facade
     */
    var Range = function(el) {
      this.input = el;

      this.value = parseFloat(el.value);
      this.max = parseFloat(el.getAttribute('max')) || 100;
      this.min = parseFloat(el.getAttribute('min')) || 0;
      this.step = parseFloat(el.getAttribute('step')) || 1;
    };

    /** @memberof Range */
    Range.prototype = {
      init: function() {
        this._render();
        this._bindEvents();
        this._setValue(this.value);
        this._list();

        return this;
      },

      _list: function() {
        if(this.input.getAttribute('list')) {
          this._generateTicks();

          var pointerWidth = this.pointerWidth;
          var hpw = pointerWidth / 2;

          this.ticks.style.padding = ['0', hpw, 'px'].join('');
          this.ticks.style.width = '100%';
          this.ticks.style.position = 'absolute';
        }
      },

      _render: function() {
        var input = this.input;
        this.el = this._template();

        input.style.display = 'none';

        input.parentNode.insertBefore(this.el, input.nextSibling);
        this._getDimensions();

        this.track.style.paddingRight = [this.pointerWidth, 'px'].join('');
      },

      // TODO: Tick positioning is wrong
      _generateTicks: function() {
        var el = document.createElement('div');
        var inner = this._generateTicksInner();

        el.appendChild(inner);

        el.className = 'ticks';

        this._generateTickEls(inner);
        this.ticks = el;

        this.el.appendChild(this.ticks);
      },

      _generateTicksInner: function() {
        var inner = document.createElement('div');

        inner.className = 'ticks-inner';
        inner.style.width = '100%';
        inner.style.position = 'relative';

        return inner;
      },

      _generateTickEls: function(inner) {
        var steps = (this.max - this.min) / this.step;
        var stepPercent = 100 / steps;

        var offset;

        for(var i = 0; i <= steps; i++) {
          offset = stepPercent * i;
          inner.appendChild(this._generateTick(offset));
        }
      },

      /**
       * @private
       * @param {integer} offset - tick offset in %
       */
      _generateTick: function(offset) {
        var tick = document.createElement('div');

        tick.className = 'tick';
        tick.style.position = 'absolute';
        tick.style.left = [offset, '%'].join('');

        return tick;
      },

      _getDimensions: function() {
        this.pointerWidth = this.pointer.offsetWidth;
        var rect = this.el.getBoundingClientRect();

        this.xMin = rect.left;
        this.xMax = rect.right - this.xMin;
      },

      _template: function() {

        var el = this._rangeEl();
        this.track = this._trackEl();
        this.pointer = this._pointerEl();

        el.appendChild(this.track);
        this.track.appendChild(this.pointer);

        return el;
      },

      _rangeEl: function() {
        var  el = document.createElement('div');
        var width = this.pointerWidth || 0;

        el.className = 'range-replacement';
        el.style.position = 'relative';
        el.style.paddingRight = [width, 'px'].join('');

        return el;
      },

      _trackEl: function() {
        var track = document.createElement('div');
        track.className = 'track';

        return track;
      },

      _pointerEl: function() {
        var pointer = document.createElement('div');

        pointer.className = 'point';
        pointer.style.position = 'relative';

        return pointer;
      },

      _bindEvents: function() {
        var that = this;

        this.el.addEventListener('mousedown', function(e) {
          that._onMouseDown(e);
        });

        this.el.addEventListener('mouseup', function(e) {
          that._onMouseUp(e);
        });
      },

      /**
       * update element dimensions, and reset value and pointer position
       */
      update: function() {
        // TODO: check round and limit this for old browsers
        this.value = parseFloat(this.input.value);

        this._getDimensions();
        this._setValue(this.value);
      },

      _onMouseDown: function(e) {
        this.oldValue = this.value;

        this._input(e);

        var that = this;

        var onMove = function(e) {
          that._input(e);
        };

        var onUp = function() {
          that._change();

          window.removeEventListener('mousemove',  onMove);
          window.removeEventListener('mouseup', onUp);
        };

        window.addEventListener('mousemove',  onMove);
        window.addEventListener('mouseup', onUp);

        H.fireEvent(this.input, 'mousedown');
      },

      _onMouseUp: function() {
        this._change();

        H.fireEvent(this.input, 'mouseup');
        H.fireEvent(this.input, 'click');
      },

      _getMouseX: (function() {
        var out;

        if(typeof window.event === 'undefined') {
          out = function(e) {
            return e.pageX;
          };
        } else {
          out = function() {
            return window.event.clientX;
          };
        }

        return out;
      })(),

      _input: function(e) {
        // OPTIMIZE: How not ot call this each time?
        this.update();

        var x = this._getMouseX(e);

        var offsetX = x - this.xMin;
        var from = [0, this.xMax];
        var to = [this.min, this.max];

        var scaled = this._scale(offsetX, from, to);
        var value = parseFloat(this._limitToRange(scaled));

        this._setValue(value);

        // TODO: ie8 dosent like doing this...
        H.fireEvent(this.input, 'input');
      },

      _setValue: function(value) {
        // round to nearest step limit between min and max
        var rounded = this._limitToRange(this._round(value));

        // set pointer position only when value changes
        if(rounded !== this.oldInputValue) {
          this.oldInputValue = this.value;
          this.input.value = this.newValue = rounded;
          console.log(this.min)
          var percent = (rounded / this.max * 100) || 0;
          this.pointer.style.left = [percent, '%'].join('');
        }
      },

      _change: function() {
        var newValue = this.newValue;
        var input = this.input;

        if(this.oldValue !== newValue) {
          this.value = newValue;
          input.value = this.oldValue = this.value;
          H.fireEvent(input, 'change');
        }
      },

      _limitToRange: function(n) {
        return Math.min(Math.max(n, this.min), this.max);
      },

      /**
       * @private
       * @param {number} value - number to be rounded
       * @param {array} rangeFrom - Source range: [srcLow, srcHigh]
       * @param {array} rangeTo - Destination range: [destLow, destHigh]
       * @returns {number} - value scaled between destLow, and destHigh
       */
      _scale: function(value, rangeFrom, rangeTo) {
        var srcLow = rangeFrom[0];
        var destLow = rangeTo[0];
        var destHigh = rangeTo[1];

        var preMapped = (value - srcLow) / (rangeFrom[1] - srcLow);
        return preMapped * (destHigh - destLow) + destLow;
      },

      /**
       * @private
       * @param {number} n - to be rounded
       * @returns {integer} - n rounded to nearest this.step
       */
      _round: function(n) {
        return Math.round(n / this.step) * this.step;
      }
    };

    /**
     * @param {object} el - input to replace
     * @returns {object} Range instance
     */
    Range['new'] = function(el) { // ie8 dont like .new
      // return Range.ranges.push(new Range(el).init());
      return new Range(el).init();
    };

    /**
     * @param {string} [selector] - css selector for ranges to replace
     * @returns {array} Range instances
     */
    Range.init = function(selector) {
      selector = selector || 'input[type=range]';
      var els = document.querySelectorAll(selector);
      var ranges = [];

      for(var i = 0, l = els.length; i < l; i++) {
        ranges.push(this['new'](els[i]));
      }

      return ranges;
    };

    return Range;

  })(document, window));
}).call(window);
<commit_msg>work with min value<commit_after>/**
 * @fileoverview Range input replacement
 * @author NathanG
 * @license MIT
 * @version 0.0.3
 */

(function() {
  'use strict';

  (function(Range) {
    var define = window.define || null;

    if(typeof define === 'function' && define.amd) {
      define('range', [], function(){ return Range; });
    } else {
      window.Range = Range;
    }
  })((function(document, window) {

    /**
     * Helper methods
     *
     * @class H
     * @private
     */
    var H = {
      /** custom event cache */
      _events: {},

      /**
       * @param eventName {string} - name of event to be created
       */
      createEvent: function(eventName) {
        var event;

        if (document.createEvent) {
          event = document.createEvent('HTMLEvents');
          event.initEvent(eventName, true, true);
        } else {
          event = document.createEventObject();
          event.eventType = eventName;
        }

        event.eventName = eventName;

        this._events[eventName] = event;

        return event;
      },

      /**
       * @param el {object} - dom node to recieve event
       * @param eventName {string} - name of event to fire
       */
      fireEvent: function(el, eventName) {
        var event = this._events[eventName] || this.createEvent(eventName);

        if (document.createEvent) {
          el.dispatchEvent(event);
        } else {
          el.fireEvent('on' + event.eventType, event);
        }
      }
    };

    /**
     * Represents a range input
     *
     * @class Range
     * @param {object} el - range input to recieve facade
     */
    var Range = function(el) {
      this.input = el;

      this.value = parseFloat(el.value);
      this.max = parseFloat(el.getAttribute('max')) || 100;
      this.min = parseFloat(el.getAttribute('min')) || 0;
      this.step = parseFloat(el.getAttribute('step')) || 1;
    };

    /** @memberof Range */
    Range.prototype = {
      init: function() {
        this._render();
        this._bindEvents();
        this._setValue(this.value);
        this._list();

        return this;
      },

      _list: function() {
        if(this.input.getAttribute('list')) {
          this._generateTicks();

          var pointerWidth = this.pointerWidth;
          var hpw = pointerWidth / 2;

          this.ticks.style.padding = ['0', hpw, 'px'].join('');
          this.ticks.style.width = '100%';
          this.ticks.style.position = 'absolute';
        }
      },

      _render: function() {
        var input = this.input;
        this.el = this._template();

        input.style.display = 'none';

        input.parentNode.insertBefore(this.el, input.nextSibling);
        this._getDimensions();

        this.track.style.paddingRight = [this.pointerWidth, 'px'].join('');
      },

      // TODO: Tick positioning is wrong
      _generateTicks: function() {
        var el = document.createElement('div');
        var inner = this._generateTicksInner();

        el.appendChild(inner);

        el.className = 'ticks';

        this._generateTickEls(inner);
        this.ticks = el;

        this.el.appendChild(this.ticks);
      },

      _generateTicksInner: function() {
        var inner = document.createElement('div');

        inner.className = 'ticks-inner';
        inner.style.width = '100%';
        inner.style.position = 'relative';

        return inner;
      },

      _generateTickEls: function(inner) {
        var steps = (this.max - this.min) / this.step;
        var stepPercent = 100 / steps;

        var offset;

        for(var i = 0; i <= steps; i++) {
          offset = stepPercent * i;
          inner.appendChild(this._generateTick(offset));
        }
      },

      /**
       * @private
       * @param {integer} offset - tick offset in %
       */
      _generateTick: function(offset) {
        var tick = document.createElement('div');

        tick.className = 'tick';
        tick.style.position = 'absolute';
        tick.style.left = [offset, '%'].join('');

        return tick;
      },

      _getDimensions: function() {
        this.pointerWidth = this.pointer.offsetWidth;
        var rect = this.el.getBoundingClientRect();

        this.xMin = rect.left;
        this.xMax = rect.right - this.xMin;
      },

      _template: function() {

        var el = this._rangeEl();
        this.track = this._trackEl();
        this.pointer = this._pointerEl();

        el.appendChild(this.track);
        this.track.appendChild(this.pointer);

        return el;
      },

      _rangeEl: function() {
        var  el = document.createElement('div');
        var width = this.pointerWidth || 0;

        el.className = 'range-replacement';
        el.style.position = 'relative';
        el.style.paddingRight = [width, 'px'].join('');

        return el;
      },

      _trackEl: function() {
        var track = document.createElement('div');
        track.className = 'track';

        return track;
      },

      _pointerEl: function() {
        var pointer = document.createElement('div');

        pointer.className = 'point';
        pointer.style.position = 'relative';

        return pointer;
      },

      _bindEvents: function() {
        var that = this;

        this.el.addEventListener('mousedown', function(e) {
          that._onMouseDown(e);
        });

        this.el.addEventListener('mouseup', function(e) {
          that._onMouseUp(e);
        });
      },

      /**
       * update element dimensions, and reset value and pointer position
       */
      update: function() {
        // TODO: check round and limit this for old browsers
        this.value = this._roundAndLimit(parseFloat(this.input.value));

        this._getDimensions();
        this._setValue(this.value);
      },

      _onMouseDown: function(e) {
        this.oldValue = this.value;

        this._input(e);

        var that = this;

        var onMove = function(e) {
          that._input(e);
        };

        var onUp = function() {
          that._change();

          window.removeEventListener('mousemove',  onMove);
          window.removeEventListener('mouseup', onUp);
        };

        window.addEventListener('mousemove',  onMove);
        window.addEventListener('mouseup', onUp);

        H.fireEvent(this.input, 'mousedown');
      },

      _onMouseUp: function() {
        this._change();

        H.fireEvent(this.input, 'mouseup');
        H.fireEvent(this.input, 'click');
      },

      _getMouseX: (function() {
        var out;

        if(typeof window.event === 'undefined') {
          out = function(e) {
            return e.pageX;
          };
        } else {
          out = function() {
            return window.event.clientX;
          };
        }

        return out;
      })(),

      _input: function(e) {
        // OPTIMIZE: How not ot call this each time?
        this.update();

        var x = this._getMouseX(e);

        var offsetX = x - this.xMin;
        var from = [0, this.xMax];
        var to = [this.min, this.max];

        var scaled = this._scale(offsetX, from, to);

        this._setValue(scaled);

        // TODO: ie8 dosent like doing this...
        H.fireEvent(this.input, 'input');
      },

      _setValue: function(value) {
        var rounded = this._roundAndLimit(value);

        // set pointer position only when value changes
        if(rounded !== this.oldInputValue) {
          this.oldInputValue = this.value;
          this.input.value = this.newValue = rounded;

          var min = this.min;

          var percent = ((rounded - min) / (this.max - min) * 100) || 0;
          this.pointer.style.left = [percent, '%'].join('');
        }
      },

      _change: function() {
        var newValue = this.newValue;
        var input = this.input;

        if(this.oldValue !== newValue) {
          input.value = this.oldValue = this.value = newValue;
          H.fireEvent(input, 'change');
        }
      },

      /**
       * round to nearest step limit between min and max
       * @private
       */
      _roundAndLimit: function(n) {
        var rounded = Math.round(n / this.step) * this.step;
        return Math.min(Math.max(rounded, this.min), this.max);
      },

      /**
       * @private
       * @param {number} value - number to be rounded
       * @param {array} rangeFrom - Source range: [srcLow, srcHigh]
       * @param {array} rangeTo - Destination range: [destLow, destHigh]
       * @returns {number} - value scaled between destLow, and destHigh
       */
      _scale: function(value, rangeFrom, rangeTo) {
        var srcLow = rangeFrom[0];
        var destLow = rangeTo[0];
        var destHigh = rangeTo[1];

        var preMapped = (value - srcLow) / (rangeFrom[1] - srcLow);
        return preMapped * (destHigh - destLow) + destLow;
      }
    };

    /**
     * @param {object} el - input to replace
     * @returns {object} Range instance
     */
    Range['new'] = function(el) { // ie8 dont like .new
      return new Range(el).init();
    };

    /**
     * @param {string} [selector] - css selector for ranges to replace
     * @returns {array} Range instances
     */
    Range.init = function(selector) {
      selector = selector || 'input[type=range]';
      var els = document.querySelectorAll(selector);
      var ranges = [];

      for(var i = 0, l = els.length; i < l; i++) {
        ranges.push(this['new'](els[i]));
      }

      return ranges;
    };

    return Range;

  })(document, window));
}).call(window);

<|endoftext|>