<commit_before>'use strict';

var app = angular.module('myApp.logs.directives', []);

app.directive('logging', ['Logs', 'Location', 'Box', '$routeParams', 'gettextCatalog', 'pagination_labels', '$pusher', '$rootScope', '$location', '$mdDialog', 'showToast', function(Logs, Location, Box, $routeParams, gettextCatalog, pagination_labels, $pusher, $rootScope, $location, $mdDialog, showToast) {

  var link = function(scope,element,attrs,controller) {

    scope.loading  = true;
    var ap_mac = $routeParams.ap_mac;

    scope.pagination_labels = pagination_labels;
    scope.query = {
      // order:   '-timestamp',
      query:   $routeParams.q,
      ap_mac:  $routeParams.ap_mac,
      limit:   $routeParams.per || 25,
      page:    $routeParams.page || 1,
      options: [5,10,25,50,100],
    };

    $routeParams.start || $routeParams.end ? scope.date_range = true : scope.date_range = false;

    var ap_descriptions = {};
    var ap_slugs = {};
    var location;

    var fetchBoxes = function() {
      Box.get({location_id: $routeParams.id}).$promise.then(function(results) {
        for (var i = 0, len = results.boxes.length; i < len; i++) {
          ap_descriptions[results.boxes[i].calledstationid] = results.boxes[i].description;
          ap_slugs[results.boxes[i].calledstationid] = results.boxes[i].slug;
        }
      });
    };

    var setApAttributes = function() {
      for (var i = 0, len = scope.logs.length; i < len; i++) {
        scope.logs[i].ap_name = ap_descriptions[scope.logs[i].ap_mac];
        scope.logs[i].ap_slug = ap_slugs[scope.logs[i].ap_mac];
      }
    };

    scope.expandRow = function(log) {
      if (log.show_detail && log.show_detail === true) {
        log.show_detail = false;
      } else {
        log.show_detail = true;
      }
    };

    scope.updatePage = function(page) {
      var hash  = {};
      hash.start  = scope.start;
      hash.end    = scope.end;
      hash.page  = scope.query.page;
      hash.per    = scope.query.limit;
      hash.q      = scope.query.query;
      hash.ap_mac = scope.query.ap_mac;
      $location.search(hash);
    };

    var start_time;
    var end_time;

    if ($routeParams.start && $routeParams.end && $routeParams.end - $routeParams.start <= 3600) {
      start_time = $routeParams.start;
      end_time = $routeParams.end;
    } else {
      if ($routeParams.end - $routeParams.start > 3600) {
        showToast(gettextCatalog.getString('Period should not be longer than an hour'));
      } else if ($routeParams.start || $routeParams.end) {
        showToast(gettextCatalog.getString('Selected range period not valid'));
      }
      start_time = Math.round((new Date().getTime() - 60*60*1000) / 1000);
      end_time = Math.round((new Date().getTime()) / 1000);
    }

    function rangeCtrl($scope, startFull, endFull) {
      $scope.startFull = startFull;
      $scope.endFull = endFull;
      $scope.saveRange = function() {
        if ($scope.startFull && $scope.endFull) {
          // converting the moment picker time format - this could really do with some work:
          var startTimestamp = Math.floor(moment($scope.startFull).utc().toDate().getTime() / 1000);
          var endTimestamp = Math.floor(moment($scope.endFull).utc().toDate().getTime() / 1000);
          if (startTimestamp > endTimestamp) {
            showToast(gettextCatalog.getString('Selected range period not valid'));
          } else if ((endTimestamp - startTimestamp) < 300 || (endTimestamp - startTimestamp) > 3600) {
            // check that the selected range period is between five minutes and thirty days
            showToast(gettextCatalog.getString('Range period should be between five minutes and one hour'));
          } else {
            scope.start = startTimestamp;
            scope.end = endTimestamp;
            scope.date_range = true;
            scope.updatePage();
            $mdDialog.cancel();
          }
        }
      };

      $scope.close = function() {
        $mdDialog.cancel();
      };
    }

    scope.openMomentRange = function() {
      if ($routeParams.start && $routeParams.end) {
        scope.startFull = moment($routeParams.start * 1000).format('MM/DD/YYYY h:mm A');
        scope.endFull = moment($routeParams.end * 1000).format('MM/DD/YYYY h:mm A');
      }
      $mdDialog.show({
        templateUrl: 'components/locations/clients/_client_date_range.html',
        parent: angular.element(document.body),
        clickOutsideToClose:true,
        locals: {
          startFull: scope.startFull,
          endFull:   scope.endFull
        },
        controller: rangeCtrl
      });
    };

    scope.clearRangeFilter = function() {
      scope.start = undefined;
      scope.end = undefined;
      scope.date_range = false;
      scope.updatePage();
    };

    scope.clearQueryFilter = function() {
      scope.query.query = undefined;
      scope.updatePage();
    };

    scope.filterByMac = function(mac) {
      scope.query.ap_mac = mac;
      scope.updatePage();
    };

    scope.clearAPFilter = function() {
      scope.query.ap_mac = undefined;
      scope.updatePage();
    };

    scope.clearAllFilters = function() {
      scope.query.ap_mac = undefined;
      scope.query.query = undefined;
      scope.start = undefined;
      scope.end = undefined;
      scope.date_range = false;
      scope.updatePage();
    };

    scope.onPaginate = function (page, limit) {
      scope.query.page = page;
      scope.query.limit = limit;
      scope.updatePage();
    };

    var getLogs = function() {
      Logs.query({
        location_id: scope.location.id,
        ap_mac: ap_mac,
        page: scope.query.page,
        per: scope.query.limit,
        start_time: start_time,
        end_time: end_time,
        q: scope.query.query
      }).$promise.then(function(res) {
        console.log(res)
        scope.logs = res.data;
        setApAttributes();
        // scope._links = res._links;
        scope.loading = undefined;
      }, function() {
        scope.loading = undefined;
      });
    };

    var init = function() {
      Location.get({id: $routeParams.id}, function(data) {
        scope.location = data;
        fetchBoxes();
        getLogs();
      }, function(err){
        console.log(err);
      });
    };

    init();
  };

  return {
    link: link,
    scope: {
      loading: '='
    },
    templateUrl: 'components/locations/logging/_index.html'
  };

}]);

<commit_msg>limit<commit_after>'use strict';

var app = angular.module('myApp.logs.directives', []);

app.directive('logging', ['Logs', 'Location', 'Box', '$routeParams', 'gettextCatalog', 'pagination_labels', '$pusher', '$rootScope', '$location', '$mdDialog', 'showToast', function(Logs, Location, Box, $routeParams, gettextCatalog, pagination_labels, $pusher, $rootScope, $location, $mdDialog, showToast) {

  var link = function(scope,element,attrs,controller) {

    scope.loading  = true;
    var ap_mac = $routeParams.ap_mac;

    scope.pagination_labels = pagination_labels;
    scope.query = {
      // order:   '-timestamp',
      query:   $routeParams.q,
      ap_mac:  $routeParams.ap_mac,
      limit:   $routeParams.per || 100,
      page:    $routeParams.page || 1,
      options: [5,10,25,50,100],
    };

    $routeParams.start || $routeParams.end ? scope.date_range = true : scope.date_range = false;

    var ap_descriptions = {};
    var ap_slugs = {};
    var location;

    var fetchBoxes = function() {
      Box.get({location_id: $routeParams.id}).$promise.then(function(results) {
        for (var i = 0, len = results.boxes.length; i < len; i++) {
          ap_descriptions[results.boxes[i].calledstationid] = results.boxes[i].description;
          ap_slugs[results.boxes[i].calledstationid] = results.boxes[i].slug;
        }
      });
    };

    var setApAttributes = function() {
      for (var i = 0, len = scope.logs.length; i < len; i++) {
        scope.logs[i].ap_name = ap_descriptions[scope.logs[i].ap_mac];
        scope.logs[i].ap_slug = ap_slugs[scope.logs[i].ap_mac];
      }
    };

    scope.expandRow = function(log) {
      if (log.show_detail && log.show_detail === true) {
        log.show_detail = false;
      } else {
        log.show_detail = true;
      }
    };

    scope.updatePage = function(page) {
      var hash  = {};
      hash.start  = scope.start;
      hash.end    = scope.end;
      hash.page  = scope.query.page;
      hash.per    = scope.query.limit;
      hash.q      = scope.query.query;
      hash.ap_mac = scope.query.ap_mac;
      $location.search(hash);
    };

    var start_time;
    var end_time;

    if ($routeParams.start && $routeParams.end && $routeParams.end - $routeParams.start <= 3600) {
      start_time = $routeParams.start;
      end_time = $routeParams.end;
    } else {
      if ($routeParams.end - $routeParams.start > 3600) {
        showToast(gettextCatalog.getString('Period should not be longer than an hour'));
      } else if ($routeParams.start || $routeParams.end) {
        showToast(gettextCatalog.getString('Selected range period not valid'));
      }
      start_time = Math.round((new Date().getTime() - 60*60*1000) / 1000);
      end_time = Math.round((new Date().getTime()) / 1000);
    }

    function rangeCtrl($scope, startFull, endFull) {
      $scope.startFull = startFull;
      $scope.endFull = endFull;
      $scope.saveRange = function() {
        if ($scope.startFull && $scope.endFull) {
          // converting the moment picker time format - this could really do with some work:
          var startTimestamp = Math.floor(moment($scope.startFull).utc().toDate().getTime() / 1000);
          var endTimestamp = Math.floor(moment($scope.endFull).utc().toDate().getTime() / 1000);
          if (startTimestamp > endTimestamp) {
            showToast(gettextCatalog.getString('Selected range period not valid'));
          } else if ((endTimestamp - startTimestamp) < 300 || (endTimestamp - startTimestamp) > 3600) {
            // check that the selected range period is between five minutes and thirty days
            showToast(gettextCatalog.getString('Range period should be between five minutes and one hour'));
          } else {
            scope.start = startTimestamp;
            scope.end = endTimestamp;
            scope.date_range = true;
            scope.updatePage();
            $mdDialog.cancel();
          }
        }
      };

      $scope.close = function() {
        $mdDialog.cancel();
      };
    }

    scope.openMomentRange = function() {
      if ($routeParams.start && $routeParams.end) {
        scope.startFull = moment($routeParams.start * 1000).format('MM/DD/YYYY h:mm A');
        scope.endFull = moment($routeParams.end * 1000).format('MM/DD/YYYY h:mm A');
      }
      $mdDialog.show({
        templateUrl: 'components/locations/clients/_client_date_range.html',
        parent: angular.element(document.body),
        clickOutsideToClose:true,
        locals: {
          startFull: scope.startFull,
          endFull:   scope.endFull
        },
        controller: rangeCtrl
      });
    };

    scope.clearRangeFilter = function() {
      scope.start = undefined;
      scope.end = undefined;
      scope.date_range = false;
      scope.updatePage();
    };

    scope.clearQueryFilter = function() {
      scope.query.query = undefined;
      scope.updatePage();
    };

    scope.filterByMac = function(mac) {
      scope.query.ap_mac = mac;
      scope.updatePage();
    };

    scope.clearAPFilter = function() {
      scope.query.ap_mac = undefined;
      scope.updatePage();
    };

    scope.clearAllFilters = function() {
      scope.query.ap_mac = undefined;
      scope.query.query = undefined;
      scope.start = undefined;
      scope.end = undefined;
      scope.date_range = false;
      scope.updatePage();
    };

    scope.onPaginate = function (page, limit) {
      scope.query.page = page;
      scope.query.limit = limit;
      scope.updatePage();
    };

    var getLogs = function() {
      Logs.query({
        location_id: scope.location.id,
        ap_mac: ap_mac,
        page: scope.query.page,
        per: scope.query.limit,
        start_time: start_time,
        end_time: end_time,
        q: scope.query.query
      }).$promise.then(function(res) {
        scope.logs = res.data;
        setApAttributes();
        // scope._links = res._links;
        scope.loading = undefined;
      }, function() {
        scope.loading = undefined;
      });
    };

    var init = function() {
      Location.get({id: $routeParams.id}, function(data) {
        scope.location = data;
        fetchBoxes();
        getLogs();
      }, function(err){
        console.log(err);
      });
    };

    init();
  };

  return {
    link: link,
    scope: {
      loading: '='
    },
    templateUrl: 'components/locations/logging/_index.html'
  };

}]);

<|endoftext|>


<commit_before>/* eslint-disable */
'use strict';

function setLocalEndpoint(endpointUrl, appId) {
    localStorage.setItem('config.app_id', appId);
    localStorage.setItem('config.server_url', endpointUrl);
    console.log('Endpoint: ' + endpointUrl + '\nApp ID: ' + appId);
    location.reload();
}

function resetLocalEndpoint() {
    localStorage.removeItem('config.app_id');
    localStorage.removeItem('config.server_url');
    console.log('Endpoint reset to default values.');
    location.reload();
}

(function init() {
    // clean stale data in local storage
    localStorage.removeItem('assets');
    localStorage.removeItem('settings');

    var defaultConfig = {
        language: 'EN',
        theme: 'light',
        apiUrl: 'wss://frontend.binaryws.com/websockets/v3',
        oAuthUrl: 'https://oauth.binary.com/oauth2/authorize',
        accounts: []
    };

    function getDefaultAppId() {
        var defaultAppID;
        if(window.cordova) {
          defaultAppID = 1006;
        } else if(window.electron) {
          defaultAppID = 1306;
        } else if (/localhost:/g.test(window.location.href)) {
          defaultAppID = 3588;
        } else if (/arnabk.github.io:/g.test(window.location.href)) {
          defaultAppID = 3604;
        } else if (/beta/g.test(window.location.href)) {
          defaultAppID = 4343; //This is for BETA release
        } else {
          defaultAppID = 1001; //This is for PROD release
        }
        localStorage.setItem('config.default_app_id', defaultAppID);
        return defaultAppID;
    };

    function getAppId() {
      return window.localStorage.getItem('config.app_id') || getDefaultAppId();
    }

    function getSocketURL () {
        var server_url = window.localStorage.getItem('config.server_url');
        if (!server_url) {
            var server = 'frontend';
            server_url = server + '.binaryws.com';
        }
        return 'wss://' + server_url + '/websockets/v3';
    };

    function getoAuthURL(appId) {
        var server_url = window.localStorage.getItem('config.server_url');
        if (!server_url) {
          return defaultConfig.oAuthUrl;
        } else {
          return 'https://' + server_url + '/oauth2/authorize';
        }
    }

    function parseOAuthResponse(responseUrl) {
        var urlParts = responseUrl.split('?');
        if (urlParts.length !== 2) {
            throw new Error('Not a valid url');
        }

        var objURL = {};
        responseUrl.replace(
            new RegExp( "([^?=&]+)(=([^&]*))?", "g" ),
            function( $0, $1, $2, $3 ){
                objURL[ $1 ] = $3;
            }
        );

        var accounts = [];
        for(var i = 1;; i++) {
            var account = objURL['acct' + i],
                token = objURL['token' + i],
                currency = objURL['cur' + i];
            if (!account || !token) break;
            accounts.push({ account: account, token: token, currency : currency || '', });
        }

        return accounts;
    }

    function readConfig() {
        try {
            window.BinaryBoot = JSON.parse(localStorage.getItem('boot')) || defaultConfig;
        } catch (e) {
            window.BinaryBoot = defaultConfig;
            window.console.log('Error while initializing', e);
        }
    }

    function parseUrlAndStoreAccountInfo(url) {
        if (~url.indexOf('acct1')) {
            var accounts = parseOAuthResponse(url);
            window.BinaryBoot.accounts = accounts;
            try {
                localStorage.setItem('boot', JSON.stringify(window.BinaryBoot));
                localStorage.setItem('account', JSON.stringify({ token: accounts[0].token }));
            } catch (e) {
                window.console.log('Error while saving boot config', e);
            }
        }
    }

    readConfig();
    parseUrlAndStoreAccountInfo(window.location.href);
    window.BinaryBoot.parseUrl = parseOAuthResponse;
    window.BinaryBoot.isBeta = /beta/g.test(window.location.href);
    window.BinaryBoot.baseUrl = window.BinaryBoot.isBeta ? '/beta' : '/';
    var lang = window.BinaryBoot.language;

    var redirectIndex = window.location.href.indexOf('?');
    if (~redirectIndex) {
        window.location.href = window.BinaryBoot.baseUrl;
    }

    // window.BinaryBoot.oAuthUrl = defaultConfig.oAuthUrl;
    // window.BinaryBoot.apiUrl = defaultConfig.apiUrl;

    try {
        // var config = JSON.parse(testConfig) || { };
        window.BinaryBoot.appId = getAppId();
        window.BinaryBoot.apiUrl = getSocketURL();
        window.BinaryBoot.oAuthUrl = getoAuthURL(window.BinaryBoot.appId);
    } catch (e) { }

    window.BinaryBoot.connection = new WebSocket(window.BinaryBoot.apiUrl + '?app_id=' + window.BinaryBoot.appId + '&l=' + lang);
})();
<commit_msg>Change to new app id<commit_after>/* eslint-disable */
'use strict';

function setLocalEndpoint(endpointUrl, appId) {
    localStorage.setItem('config.app_id', appId);
    localStorage.setItem('config.server_url', endpointUrl);
    console.log('Endpoint: ' + endpointUrl + '\nApp ID: ' + appId);
    location.reload();
}

function resetLocalEndpoint() {
    localStorage.removeItem('config.app_id');
    localStorage.removeItem('config.server_url');
    console.log('Endpoint reset to default values.');
    location.reload();
}

(function init() {
    // clean stale data in local storage
    localStorage.removeItem('assets');
    localStorage.removeItem('settings');

    var defaultConfig = {
        language: 'EN',
        theme: 'light',
        apiUrl: 'wss://frontend.binaryws.com/websockets/v3',
        oAuthUrl: 'https://oauth.binary.com/oauth2/authorize',
        accounts: []
    };

    function getDefaultAppId() {
        var defaultAppID;
        if(window.cordova) {
          defaultAppID = 11780;
        } else if(window.electron) {
          defaultAppID = 1306;
        } else if (/localhost:/g.test(window.location.href)) {
          defaultAppID = 3588;
        } else if (/arnabk.github.io:/g.test(window.location.href)) {
          defaultAppID = 3604;
        } else if (/beta/g.test(window.location.href)) {
          defaultAppID = 4343; //This is for BETA release
        } else {
          defaultAppID = 1001; //This is for PROD release
        }
        localStorage.setItem('config.default_app_id', defaultAppID);
        return defaultAppID;
    };

    function getAppId() {
      return window.localStorage.getItem('config.app_id') || getDefaultAppId();
    }

    function getSocketURL () {
        var server_url = window.localStorage.getItem('config.server_url');
        if (!server_url) {
            var server = 'frontend';
            server_url = server + '.binaryws.com';
        }
        return 'wss://' + server_url + '/websockets/v3';
    };

    function getoAuthURL(appId) {
        var server_url = window.localStorage.getItem('config.server_url');
        if (!server_url) {
          return defaultConfig.oAuthUrl;
        } else {
          return 'https://' + server_url + '/oauth2/authorize';
        }
    }

    function parseOAuthResponse(responseUrl) {
        var urlParts = responseUrl.split('?');
        if (urlParts.length !== 2) {
            throw new Error('Not a valid url');
        }

        var objURL = {};
        responseUrl.replace(
            new RegExp( "([^?=&]+)(=([^&]*))?", "g" ),
            function( $0, $1, $2, $3 ){
                objURL[ $1 ] = $3;
            }
        );

        var accounts = [];
        for(var i = 1;; i++) {
            var account = objURL['acct' + i],
                token = objURL['token' + i],
                currency = objURL['cur' + i];
            if (!account || !token) break;
            accounts.push({ account: account, token: token, currency : currency || '', });
        }

        return accounts;
    }

    function readConfig() {
        try {
            window.BinaryBoot = JSON.parse(localStorage.getItem('boot')) || defaultConfig;
        } catch (e) {
            window.BinaryBoot = defaultConfig;
            window.console.log('Error while initializing', e);
        }
    }

    function parseUrlAndStoreAccountInfo(url) {
        if (~url.indexOf('acct1')) {
            var accounts = parseOAuthResponse(url);
            window.BinaryBoot.accounts = accounts;
            try {
                localStorage.setItem('boot', JSON.stringify(window.BinaryBoot));
                localStorage.setItem('account', JSON.stringify({ token: accounts[0].token }));
            } catch (e) {
                window.console.log('Error while saving boot config', e);
            }
        }
    }

    readConfig();
    parseUrlAndStoreAccountInfo(window.location.href);
    window.BinaryBoot.parseUrl = parseOAuthResponse;
    window.BinaryBoot.isBeta = /beta/g.test(window.location.href);
    window.BinaryBoot.baseUrl = window.BinaryBoot.isBeta ? '/beta' : '/';
    var lang = window.BinaryBoot.language;

    var redirectIndex = window.location.href.indexOf('?');
    if (~redirectIndex) {
        window.location.href = window.BinaryBoot.baseUrl;
    }

    // window.BinaryBoot.oAuthUrl = defaultConfig.oAuthUrl;
    // window.BinaryBoot.apiUrl = defaultConfig.apiUrl;

    try {
        // var config = JSON.parse(testConfig) || { };
        window.BinaryBoot.appId = getAppId();
        window.BinaryBoot.apiUrl = getSocketURL();
        window.BinaryBoot.oAuthUrl = getoAuthURL(window.BinaryBoot.appId);
    } catch (e) { }

    window.BinaryBoot.connection = new WebSocket(window.BinaryBoot.apiUrl + '?app_id=' + window.BinaryBoot.appId + '&l=' + lang);
})();
<|endoftext|>


<commit_before>var profiles = $('<select>')
$(document).ready(function() {
    $(window).trigger('hashchange')
    getMovieLists()
    getNotificationList()
    getHistory()
    $('#searchform').submit(function(e) {
        e.preventDefault()
        var search = $('#moviename').val()
        if (search) searchMovie(search)
    })
    $.get(WEBDIR + 'couchpotato/GetProfiles', function(data) {
        if (data === null) return
        $.each(data.list, function(i, item) {
            if (!item.hide) profiles.append($('<option>').val(item.id).text(item.label))
        })
    })
})

function getMovies(strStatus, pHTMLElement) {
	pHTMLElement.empty();
    $(".spinner").show();

    $.getJSON(WEBDIR + "couchpotato/GetMovieList/" + strStatus, function (pResult) {

        if (pResult === null || pResult.total === 0) {
            pHTMLElement.append($("
").html("No " + strStatus + " movies found"));
            return;
        }

        $.each(pResult.movies, function(nIndex, pMovie) {
            var strHTML = $("").attr("href", "#").click(function(pEvent) {
                pEvent.preventDefault();
                showMovie(pMovie);
            });

            if (pMovie.info.images.poster && pMovie.info.images.poster_original) {
                strHTML.append($("").attr("src", WEBDIR + "couchpotato/GetImage?w=100&h=150&url=" + pMovie.info.images.poster[0]).attr("width", "100").attr("height", "150").addClass("thumbnail"));
            }

            if (pMovie.releases.length > 0) {
                strHTML.append($("").attr("title", "Download").addClass("icon-white icon-download status"));
            }

            strHTML.append($("
").addClass("movie-title").html(shortenText(pMovie.info.original_title, 12)));
            pHTMLElement.append($("
").attr("id", pMovie.id).append(strHTML));
            $(".spinner").hide();
        });
    });

}

function getMovieLists() {
    getMovies("done", $("#library-grid"));
	getMovies("active", $("#wanted-grid"));
}

function addMovie(movieid, profile, title) {
    var data = {
        movieid: movieid,
        profile: profile,
        title: encodeURIComponent(title)
    }
    $.getJSON(WEBDIR + 'couchpotato/AddMovie', data, function (result) {
        if (result.success) {
            notify('CouchPotato', 'Added ' + title, 'info');
        } else {
            notify('CouchPotato', 'Failed to add ' + title, 'info')
        }
    })
}
function editMovie(id, profile, title) {
    $.getJSON(WEBDIR + 'couchpotato/EditMovie', {
        id: id,
        profile: profile,
        title: encodeURIComponent(title)
    }, function (result) {
        if (result.success) {
            notify('CouchPotato', 'Profile changed', 'info')
            getMovieLists();
        } else {
            notify('CouchPotato', 'An error occured.', 'error')
        }
    })
}
function deleteMovie(id, name) {
    $.getJSON(WEBDIR + 'couchpotato/DeleteMovie', {id: id}, function (result) {
        if (result.success) {
            $('#' + id).fadeOut()
            getMovieList();
            $('a[href=#wanted]').tab('show') // NEW
        } else {
            notify('CouchPotato', 'An error occured.', 'error')
        }
    })
}
function refreshMovie(id, name) {
    $.getJSON(WEBDIR + 'couchpotato/RefreshMovie', {id: id}, function (result) {
        if (result.success) {
            notify('CouchPotato', 'Refreshing: ' + name, 'info')
        } else {
            notify('CouchPotato', 'An error occured.', 'error')
        }
    })
}
function searchMovie(q) {
    var grid = $('#result-grid').empty()
    $('a[href=#result]').tab('show')
    $('.spinner').show()
    $.getJSON(WEBDIR + 'couchpotato/SearchMovie', {
        q: encodeURIComponent(q)
    }, function (result) {
        $('.spinner').hide()
        $.each(result.movies, function(i, movie) {
            var link = $('').attr('href', '#').click(function(e) {
                e.preventDefault()
                showMovie(movie)
            })
            var src = 'holder.js/100x150/text:No artwork'
            if (movie.images.poster[0]) {
                src = WEBDIR + 'couchpotato/GetImage?w=100&h=150&url=' + movie.images.poster[0]
            }
            link.append($('').attr('src', src).addClass('thumbnail'))
            var title = shortenText(movie.original_title, 12)
            link.append($('
').addClass('movie-title').html(title))
            grid.append($('
').attr('id', movie.id).append(link))
        })
        Holder.run()
    })
}
function getNotificationList() {
    $.getJSON(WEBDIR + 'couchpotato/GetNotificationList', function (result) {
        if (result == null) return
        $.each(result.notifications, function(i, item) {
            if (!item.read) notify('Notifications', item.message, 'info')
        })
    })
}
function getHistory() {
    $.getJSON(WEBDIR + 'couchpotato/GetNotificationList', function (result) {
        if (result == null) return
        $.each(result.notifications, function(i, item) {
            $('#history-grid').prepend(
                $('
').append(
                    $('').text(parseDate(item.added)),
                    $('').text(item.message)
                )
            )
        })
    })
}

function showMovie(movie) {
    var plot;
    var info;
    var year;
    var modalButtons;

    //If true, it was not called from a search.
    if (typeof movie.plot === 'undefined') {
        plot = movie.info.plot;
        info = movie.info;
        year = movie.info.year;

    } else {
        info = movie;
        plot = movie.plot;
        year = movie.year;
    }

    var src = 'holder.js/154x231/text:No artwork';
    if (info.images.poster && info.images.poster[0]) {
        src = WEBDIR + 'couchpotato/GetImage?w=154&h=231&url=' + info.images.poster[0];
    }
    var modalImg = $('').attr('src', src).addClass('thumbnail pull-left');

    var modalInfo = $('
').addClass('modal-movieinfo');
    if (info.runtime) {
        modalInfo.append($('
').html('Runtime: ' + parseSec(info.runtime)));
    }
    modalInfo.append($('
').html('Plot: ' + plot));
    if (info.directors) {
        modalInfo.append($('
').html('Director: ' + info.directors));
    }
    if (info.genres) {
        modalInfo.append($('
').html('Genre: ' + info.genres));
    }

    if (info.rating && info.rating.imdb) {
        modalInfo.append(
        $('
').raty({
            readOnly: true,
            path: WEBDIR + 'img',
            score: (info.rating.imdb[0] / 2)
        }));
    }

    var titles = $('<select>').attr('id', 'titles');

    $.each(info.titles, function (i, item) {
        titles.append($('<option>').text(item).val(item));
    });

    profiles.unbind();
    var title = info.original_title + ' (' + year + ')';
    profiles.change(function () {
        editMovie(movie._id, profiles.val(), titles.val());
    }).val(movie.profile_id);
    titles.change(function () {
        editMovie(movie._id, profiles.val(), titles.val());
    });

    // If showmovie isnt called from a search
    if (typeof movie.in_wanted === 'undefined' || typeof movie.in_library === 'undefined') {
        modalButtons = {
            'Delete': function () {
                if (confirm('Do you want to delete: ' + title)) {
                    deleteMovie(movie._id, title);
                    hideModal();
                }
            },
                'Refresh': function () {
                refreshMovie(movie._id, title);
                hideModal();
            }
        };
    } else {
        // Was called from search
        modalButtons = {
            'Add': function () {
                addMovie(movie.imdb, profiles.val(), titles.val());
                hideModal();
            }
        };
    }
    if (info.imdb) {
        $.extend(modalButtons,{
            'IMDb' : function() {
                window.open('http://www.imdb.com/title/'+info.imdb,'IMDb')
            }
        })
    }

    //Loop all with movies with releases. Dont add button if its done (#wanted tab)
    $.each(movie.releases, function (nIndex, rr) {
        if (movie.releases && movie.releases.length > 0 && rr.status !== 'done') {
            $.extend(modalButtons, {
                'Releases': function () {
                    $('.modal-body').html(strTable);
                }
            });
        }

    });

    modalInfo.append(titles, profiles);
    if (movie.releases && movie.releases.length > 0 && movie.releases.status !== 'done') {
        var strTable = $("
").addClass("table table-striped table-hover").append(
        $("
").append("Action	").append("Name	").append("Score	").append("Size	"));

        $.getJSON(WEBDIR + "couchpotato/GetReleases/" + movie._id, function (pResult) {

            $.each(movie.releases, function (nIndex, pRelease) {
                if (pRelease.info === undefined || pRelease.info.id === undefined) {
                    return;
                }

                strTable.append(
                $("
").append(
                $("").append(
                $("").attr("href", "#").append(
                $("").attr("title", "Download").addClass("icon-download")).click(function (pEvent) {
                    pEvent.preventDefault();
                    hideModal();
                    $.getJSON("DownloadRelease/?id=" + pRelease._id);
                }),
                $("").attr("href", "#").append(
                $("").attr("title", "Ignore").addClass("icon-remove-sign")).click(function (pEvent) {
                    pEvent.preventDefault();
                    $(this).closest("tr").toggleClass("ignore");
                    $.getJSON("IgnoreRelease/?id=" + pRelease._id);
                })),
                $("").append(
                $("").attr("href", "#").text(pRelease.info.name).click(function (pEvent) {
                    pEvent.preventDefault();
                    window.open(pRelease.info.detail_url);
                })),
                $("").append(pRelease.info.score),
                $("").html(bytesToSize(pRelease.info.size * 1000000))).toggleClass("ignore", pRelease.status_id == 3));

            });
        });
    }

    if (info.images.backdrop && info.images.backdrop.length > 0) {
        var backdrop = WEBDIR + 'couchpotato/GetImage?w=675&h=400&o=10&url=' + encodeURIComponent(info.images.backdrop);
        $('.modal-fanart').css({
            'background-image': 'url(' + backdrop + ')'
        });
    }

    var modalBody = $('
').append(modalImg, modalInfo);
    showModal(title, modalBody, modalButtons);
    Holder.run();
}<commit_msg>Damn search..<commit_after>var profiles = $('<select>')
$(document).ready(function() {
    $(window).trigger('hashchange')
    getMovieLists()
    getNotificationList()
    getHistory()
    $('#searchform').submit(function(e) {
        e.preventDefault()
        var search = $('#moviename').val()
        if (search) searchMovie(search)
    })
    $.get(WEBDIR + 'couchpotato/GetProfiles', function(data) {
        if (data === null) return
        $.each(data.list, function(i, item) {
            if (!item.hide) profiles.append($('<option>').val(item.id).text(item.label))
        })
    })
})

function getMovies(strStatus, pHTMLElement) {
	pHTMLElement.empty();
    $(".spinner").show();

    $.getJSON(WEBDIR + "couchpotato/GetMovieList/" + strStatus, function (pResult) {

        if (pResult === null || pResult.total === 0) {
            pHTMLElement.append($("
").html("No " + strStatus + " movies found"));
            return;
        }

        $.each(pResult.movies, function(nIndex, pMovie) {
            var strHTML = $("").attr("href", "#").click(function(pEvent) {
                pEvent.preventDefault();
                showMovie(pMovie);
            });

            if (pMovie.info.images.poster && pMovie.info.images.poster_original) {
                strHTML.append($("").attr("src", WEBDIR + "couchpotato/GetImage?w=100&h=150&url=" + pMovie.info.images.poster[0]).attr("width", "100").attr("height", "150").addClass("thumbnail"));
            }

            if (pMovie.releases.length > 0) {
                strHTML.append($("").attr("title", "Download").addClass("icon-white icon-download status"));
            }

            strHTML.append($("
").addClass("movie-title").html(shortenText(pMovie.info.original_title, 12)));
            pHTMLElement.append($("
").attr("id", pMovie.id).append(strHTML));
            $(".spinner").hide();
        });
    });

}

function getMovieLists() {
    getMovies("done", $("#library-grid"));
	getMovies("active", $("#wanted-grid"));
}

function addMovie(movieid, profile, title) {
    var data = {
        movieid: movieid,
        profile: profile,
        title: encodeURIComponent(title)
    }
    $.getJSON(WEBDIR + 'couchpotato/AddMovie', data, function (result) {
        if (result.success) {
            notify('CouchPotato', 'Added ' + title, 'info');
        } else {
            notify('CouchPotato', 'Failed to add ' + title, 'info')
        }
    })
}
function editMovie(id, profile, title) {
    $.getJSON(WEBDIR + 'couchpotato/EditMovie', {
        id: id,
        profile: profile,
        title: encodeURIComponent(title)
    }, function (result) {
        if (result.success) {
            notify('CouchPotato', 'Profile changed', 'info')
            getMovieLists();
        } else {
            notify('CouchPotato', 'An error occured.', 'error')
        }
    })
}
function deleteMovie(id, name) {
    $.getJSON(WEBDIR + 'couchpotato/DeleteMovie', {id: id}, function (result) {
        if (result.success) {
            $('#' + id).fadeOut()
            getMovieList();
            $('a[href=#wanted]').tab('show') // NEW
        } else {
            notify('CouchPotato', 'An error occured.', 'error')
        }
    })
}
function refreshMovie(id, name) {
    $.getJSON(WEBDIR + 'couchpotato/RefreshMovie', {id: id}, function (result) {
        if (result.success) {
            notify('CouchPotato', 'Refreshing: ' + name, 'info')
        } else {
            notify('CouchPotato', 'An error occured.', 'error')
        }
    })
}
function searchMovie(q) {
    var grid = $('#result-grid').empty()
    $('a[href=#result]').tab('show')
    $('.spinner').show()
    $.getJSON(WEBDIR + 'couchpotato/SearchMovie', {
        q: encodeURIComponent(q)
    }, function (result) {
        $('.spinner').hide()
        $.each(result.movies, function(i, movie) {
            var link = $('').attr('href', '#').click(function(e) {
                e.preventDefault()
                showMovie(movie)
            })
            var src = 'holder.js/100x150/text:No artwork'
            if (movie.images.poster[0]) {
                src = WEBDIR + 'couchpotato/GetImage?w=100&h=150&url=' + movie.images.poster[0]
            }
            link.append($('').attr('src', src).addClass('thumbnail'))
            var title = shortenText(movie.original_title, 12)
            link.append($('
').addClass('movie-title').html(title))
            grid.append($('
').attr('id', movie.id).append(link))
        })
        Holder.run()
    })
}
function getNotificationList() {
    $.getJSON(WEBDIR + 'couchpotato/GetNotificationList', function (result) {
        if (result == null) return
        $.each(result.notifications, function(i, item) {
            if (!item.read) notify('Notifications', item.message, 'info')
        })
    })
}
function getHistory() {
    $.getJSON(WEBDIR + 'couchpotato/GetNotificationList', function (result) {
        if (result == null) return
        $.each(result.notifications, function(i, item) {
            $('#history-grid').prepend(
                $('
').append(
                    $('').text(parseDate(item.added)),
                    $('').text(item.message)
                )
            )
        })
    })
}

function showMovie(movie) {
    var plot;
    var info;
    var year;
    var modalButtons;

    //If true, it was not called from a search.
    if (typeof movie.plot === 'undefined') {
        plot = movie.info.plot;
        info = movie.info;
        year = movie.info.year;

    } else {
        info = movie;
        plot = movie.plot;
        year = movie.year;
    }

    var src = 'holder.js/154x231/text:No artwork';
    if (info.images.poster && info.images.poster[0]) {
        src = WEBDIR + 'couchpotato/GetImage?w=154&h=231&url=' + info.images.poster[0];
    }
    var modalImg = $('').attr('src', src).addClass('thumbnail pull-left');

    var modalInfo = $('
').addClass('modal-movieinfo');
    if (info.runtime) {
        modalInfo.append($('
').html('Runtime: ' + parseSec(info.runtime)));
    }
    modalInfo.append($('
').html('Plot: ' + plot));
    if (info.directors) {
        modalInfo.append($('
').html('Director: ' + info.directors));
    }
    if (info.genres) {
        modalInfo.append($('
').html('Genre: ' + info.genres));
    }

    if (info.rating && info.rating.imdb) {
        modalInfo.append(
        $('
').raty({
            readOnly: true,
            path: WEBDIR + 'img',
            score: (info.rating.imdb[0] / 2)
        }));
    }

    var titles = $('<select>').attr('id', 'titles');

    $.each(info.titles, function (i, item) {
        titles.append($('<option>').text(item).val(item));
    });

    profiles.unbind();
    var title = info.original_title + ' (' + year + ')';
    profiles.change(function () {
        editMovie(movie._id, profiles.val(), titles.val());
    }).val(movie.profile_id);
    titles.change(function () {
        editMovie(movie._id, profiles.val(), titles.val());
    });

    // If showmovie isnt called from a search
    if (typeof movie.in_wanted === 'undefined' || typeof movie.in_library === 'undefined') {
        modalButtons = {
            'Delete': function () {
                if (confirm('Do you want to delete: ' + title)) {
                    deleteMovie(movie._id, title);
                    hideModal();
                }
            },
                'Refresh': function () {
                refreshMovie(movie._id, title);
                hideModal();
            }
        };
    } else {
        // Was called from search
        modalButtons = {
            'Add': function () {
                addMovie(movie.imdb, profiles.val(), titles.val());
                hideModal();
            }
        };
    }
    if (info.imdb) {
        $.extend(modalButtons,{
            'IMDb' : function() {
                window.open('http://www.imdb.com/title/'+info.imdb,'IMDb')
            }
        })
    }

    //Make sure that this isnt a search call...
    if (typeof movie.plot === 'undefined') {
        //Loop all with movies with releases. Dont add button if its done (#wanted tab)
        if (movie.releases && movie.releases.length > 0) {
            $.each(movie.releases, function (nIndex, rr) {
                if (rr.status !== 'done') {
                    $.extend(modalButtons, {
                        'Releases': function () {
                            $('.modal-body').html(strTable);
                            }
                        });
                    }
                });
        }
    }

    modalInfo.append(titles, profiles);
    if (movie.releases && movie.releases.length > 0 && movie.releases.status !== 'done') {
        var strTable = $("
").addClass("table table-striped table-hover").append(
        $("
").append("Action	").append("Name	").append("Score	").append("Size	"));

        $.getJSON(WEBDIR + "couchpotato/GetReleases/" + movie._id, function (pResult) {

            $.each(movie.releases, function (nIndex, pRelease) {
                if (pRelease.info === undefined || pRelease.info.id === undefined) {
                    return;
                }

                strTable.append(
                $("
").append(
                $("").append(
                $("").attr("href", "#").append(
                $("").attr("title", "Download").addClass("icon-download")).click(function (pEvent) {
                    pEvent.preventDefault();
                    hideModal();
                    $.getJSON("DownloadRelease/?id=" + pRelease._id);
                }),
                $("").attr("href", "#").append(
                $("").attr("title", "Ignore").addClass("icon-remove-sign")).click(function (pEvent) {
                    pEvent.preventDefault();
                    $(this).closest("tr").toggleClass("ignore");
                    $.getJSON("IgnoreRelease/?id=" + pRelease._id);
                })),
                $("").append(
                $("").attr("href", "#").text(pRelease.info.name).click(function (pEvent) {
                    pEvent.preventDefault();
                    window.open(pRelease.info.detail_url);
                })),
                $("").append(pRelease.info.score),
                $("").html(bytesToSize(pRelease.info.size * 1000000))).toggleClass("ignore", pRelease.status_id == 3));

            });
        });
    }

    if (info.images.backdrop && info.images.backdrop.length > 0) {
        var backdrop = WEBDIR + 'couchpotato/GetImage?w=675&h=400&o=10&url=' + encodeURIComponent(info.images.backdrop);
        $('.modal-fanart').css({
            'background-image': 'url(' + backdrop + ')'
        });
    }

    var modalBody = $('
').append(modalImg, modalInfo);
    showModal(title, modalBody, modalButtons);
    Holder.run();
}<|endoftext|>


<commit_before>
/**
 * Base URL of the REST API.
 *
 * @type {string}
 */
const API_BASE = "https://www.googleapis.com/"

/**
 * Separator of query parameter pairs to use when serializing query parameters
 * into a query string.
 *
 * @type {string}
 */
const QUERY_PARAMETERS_SEPARATOR = "&"

/**
 * Private field symbols.
 *
 * @type {Object<string, symbol>}
 */
const PRIVATE = Object.freeze({
  // fields
  baseUrl: Symbol("baseUrl"),
  tokenProvider: Symbol("tokenProvider"),
  loadTimeout: Symbol("loadTimeout"),

  // methods
  encodeQueryData: Symbol("encodeQueryData"),
  sendRequest: Symbol("sendRequest"),
  processRequest: Symbol("processRequest")
})

/**
 * Google REST API client.
 */
export default class ApiClient {
  /**
   * Initializes the Google REST API client.
   *
   * @param {string} service The service to access, for example "youtube".
   * @param {number} version The version of the REST API to access, for example
   *        3.
   * @param {AbstractOAuthTokenGenerator} tokenProvider Generator of OAuth2.0
   *        tokens to use.
   * @param {number=} loadTimeout The REST API request timeout in milliseconds,
   *        defaults to 15 seconds.
   */
  constructor(service, version, tokenProvider, loadTimeout = 15000) {
    /**
     * The base URL of the service within the Google REST API to access.
     *
     * @type {string}
     */
    this[PRIVATE.baseUrl] = `${API_BASE}${service}/v${version}/`

    /**
     * Generator of OAuth2.0 tokens to use.
     *
     * @type {AbstractOAuthTokenGenerator}
     */
    this[PRIVATE.tokenProvider] = tokenProvider

    /**
     * The REST API request timeout in milliseconds, defaults to 15 seconds.
     *
     * @type {number}
     */
    this[PRIVATE.loadTimeout] = loadTimeout

    Object.freeze(this)
  }

  /**
   * Retrieves an entity or entities at the specified location matching the
   * specified criteria.
   *
   * @param {string} path The path within the REST API denoting the resource or
   *        entity to query.
   * @param {Object<string, ?(boolean|number|string)>} parameters Parameters
   *        restricting the result to return.
   * @return {Promise<Object>} A promise that will resolve to the response body
   *         parsed as JSON.
   */
  list(path, parameters) {
    return this[PRIVATE.sendRequest]("GET", path, parameters)
  }

  /**
   * Creates a new entity at the specified path.
   *
   * @param {string} path The path within the REST API denoting the location
   *        where the entity should be created.
   * @param {Object<string, *>} data The data representing the entity.
   * @return {Promise<Object>} A promise that will resolve to the response body
   *         parsed as JSON.
   */
  insert(path, data) {
    return this[PRIVATE.sendRequest]("POST", path, data)
  }

  /**
   * Updates the entity denoted by the provided path.
   *
   * @param {string} path The path within the REST API denoting the entity.
   * @param {Object<string, *>} data Data representing the updated the entity
   *        and related metadata.
   * @return {Promise<Object>} A promise that will resolve to the response
   *         body parsed as JSON.
   */
  update(path, data) {
    return this[PRIVATE.sendRequest]("PUT", path, data)
  }

  /**
   * Deletes the entity denoted by the provided path.
   *
   * @param {string} path The path within the REST API denoting the entity.
   * @param {Object<string, *>} parameters The operations parameters to send to
   *        the server. There will be send as JSON-encoded request body.
   * @return {Promise<Object>} A promise that will resolve to the response
   *         body parsed as JSON.
   */
  delete(path, parameters) {
    return this[PRIVATE.sendRequest]("DELETE", path, parameters)
  }

  /**
   * Sends the specified request to the Google API.
   *
   * @param {("GET"|"POST"|"PUT"|"DELETE")} method The HTTP method to use.
   * @param {string} path The URI path within the REST API to send the request
   *        to.
   * @param {Object<string, *>} data The data to send to the server. These will
   *        be URI-encoded and sent as query string when the {@code GET} HTTP
   *        method is used, otherwise they will be JSON-encoded and sent as the
   *        request body.
   * @return {Promise<Object>} A promise that will resolve to the response
   *         body parsed as JSON.
   */
  [PRIVATE.sendRequest](method, path, data) {
    return this[PRIVATE.tokenProvider].generate().then((token) => {
      let xhr = new XMLHttpRequest()
      let url = this[PRIVATE.baseUrl] + path
      if (method === "GET") {
        url += "?" + this[PRIVATE.encodeQueryData](data)
      }
      xhr.open(method, url)
      xhr.timeout = this[PRIVATE.loadTimeout]

      xhr.setRequestHeader("Authorization", `Bearer ${token}`)
      if (method !== "GET") {
        xhr.setRequestHeader("Content-Type", "application/json")
      }

      return new Promise((resolve, reject) => {
        xhr.addEventListener("load", () => {
          this[PRIVATE.processRequest](xhr, resolve, reject)
        })

        xhr.addEventListener("abort", () => {
          let rejectionError = new Error("The request has been aborted")
          rejectionError.xhr = xhr
          reject(rejectionError)
        })

        xhr.addEventListener("error", (event) => {
          let rejectionError = new Error("A network error has occurred")
          rejectionError.xhr = xhr
          rejectionError.errorEvent = event
          reject(rejectionError)
        })

        xhr.addEventListener("timeout", () => {
          let rejectionError = new Error("The request has timed out, the " +
              `timeout is set to ${this[PRIVATE.loadTimeout]} ms`)
          rejectionError.xhr = xhr
          reject(rejectionError)
        })

        if (method !== "GET") {
          xhr.send(JSON.stringify(data))
        } else {
          xhr.send()
        }
      })
    })
  }

  /**
   * Processes a request that has had its response fully loaded.
   *
   * @param {XMLHttpRequest} xhr The XML HTTP request that was just loaded.
   * @param {function(Object)} resolve Callback to execute if the request was
   *        successful. The callback should receive the response body parsed as
   *        JSON as its argument.
   * @param {function(Error)} reject Callback to execute if the request has
   *        failed.
   */
  [PRIVATE.processRequest](xhr, resolve, reject) {
    if (xhr.status !== 200) {
      let rejectionError = new Error("The Google API rejected the " +
        `request with ${xhr.status} (${xhr.statusText}) code`)
      rejectionError.xhr = xhr
      reject(rejectionError)
      return
    }

    let response = xhr.responseText
    try {
      resolve(JSON.parse(response))
    } catch (parseError) {
      let rejectionError = new Error(`Cannot parse response body: ${response}`)
      rejectionError.xhr = xhr
      rejectionError.parseError = parseError
      reject(rejectionError)
    }
  }

  /**
   * Returns a query string generated from the provided data, without a
   * starting question mark (?).
   *
   * @param {Object<string, ?(boolean|number|string)>} data URI-compatible data
   *        to encode.
   * @return {string} URI-encoded query string generated from the provided
   *         data.
   */
  [PRIVATE.encodeQueryData](data) {
    return Object.keys(data).map((parameterName) => {
      return [parameterName, data[parameterName]]
    }).map((pair) => {
      return pair.map(encodeURIComponent).join("=")
    }).join(QUERY_PARAMETERS_SEPARATOR)
  }
}
<commit_msg>added names to errors thrown by the API client<commit_after>
/**
 * Base URL of the REST API.
 *
 * @type {string}
 */
const API_BASE = "https://www.googleapis.com/"

/**
 * Separator of query parameter pairs to use when serializing query parameters
 * into a query string.
 *
 * @type {string}
 */
const QUERY_PARAMETERS_SEPARATOR = "&"

/**
 * Private field symbols.
 *
 * @type {Object<string, symbol>}
 */
const PRIVATE = Object.freeze({
  // fields
  baseUrl: Symbol("baseUrl"),
  tokenProvider: Symbol("tokenProvider"),
  loadTimeout: Symbol("loadTimeout"),

  // methods
  encodeQueryData: Symbol("encodeQueryData"),
  sendRequest: Symbol("sendRequest"),
  processRequest: Symbol("processRequest")
})

/**
 * Google REST API client.
 */
export default class ApiClient {
  /**
   * Initializes the Google REST API client.
   *
   * @param {string} service The service to access, for example "youtube".
   * @param {number} version The version of the REST API to access, for example
   *        3.
   * @param {AbstractOAuthTokenGenerator} tokenProvider Generator of OAuth2.0
   *        tokens to use.
   * @param {number=} loadTimeout The REST API request timeout in milliseconds,
   *        defaults to 15 seconds.
   */
  constructor(service, version, tokenProvider, loadTimeout = 15000) {
    /**
     * The base URL of the service within the Google REST API to access.
     *
     * @type {string}
     */
    this[PRIVATE.baseUrl] = `${API_BASE}${service}/v${version}/`

    /**
     * Generator of OAuth2.0 tokens to use.
     *
     * @type {AbstractOAuthTokenGenerator}
     */
    this[PRIVATE.tokenProvider] = tokenProvider

    /**
     * The REST API request timeout in milliseconds, defaults to 15 seconds.
     *
     * @type {number}
     */
    this[PRIVATE.loadTimeout] = loadTimeout

    Object.freeze(this)
  }

  /**
   * Retrieves an entity or entities at the specified location matching the
   * specified criteria.
   *
   * @param {string} path The path within the REST API denoting the resource or
   *        entity to query.
   * @param {Object<string, ?(boolean|number|string)>} parameters Parameters
   *        restricting the result to return.
   * @return {Promise<Object>} A promise that will resolve to the response body
   *         parsed as JSON.
   */
  list(path, parameters) {
    return this[PRIVATE.sendRequest]("GET", path, parameters)
  }

  /**
   * Creates a new entity at the specified path.
   *
   * @param {string} path The path within the REST API denoting the location
   *        where the entity should be created.
   * @param {Object<string, *>} data The data representing the entity.
   * @return {Promise<Object>} A promise that will resolve to the response body
   *         parsed as JSON.
   */
  insert(path, data) {
    return this[PRIVATE.sendRequest]("POST", path, data)
  }

  /**
   * Updates the entity denoted by the provided path.
   *
   * @param {string} path The path within the REST API denoting the entity.
   * @param {Object<string, *>} data Data representing the updated the entity
   *        and related metadata.
   * @return {Promise<Object>} A promise that will resolve to the response
   *         body parsed as JSON.
   */
  update(path, data) {
    return this[PRIVATE.sendRequest]("PUT", path, data)
  }

  /**
   * Deletes the entity denoted by the provided path.
   *
   * @param {string} path The path within the REST API denoting the entity.
   * @param {Object<string, *>} parameters The operations parameters to send to
   *        the server. There will be send as JSON-encoded request body.
   * @return {Promise<Object>} A promise that will resolve to the response
   *         body parsed as JSON.
   */
  delete(path, parameters) {
    return this[PRIVATE.sendRequest]("DELETE", path, parameters)
  }

  /**
   * Sends the specified request to the Google API.
   *
   * @param {("GET"|"POST"|"PUT"|"DELETE")} method The HTTP method to use.
   * @param {string} path The URI path within the REST API to send the request
   *        to.
   * @param {Object<string, *>} data The data to send to the server. These will
   *        be URI-encoded and sent as query string when the {@code GET} HTTP
   *        method is used, otherwise they will be JSON-encoded and sent as the
   *        request body.
   * @return {Promise<Object>} A promise that will resolve to the response
   *         body parsed as JSON.
   */
  [PRIVATE.sendRequest](method, path, data) {
    return this[PRIVATE.tokenProvider].generate().then((token) => {
      let xhr = new XMLHttpRequest()
      let url = this[PRIVATE.baseUrl] + path
      if (method === "GET") {
        url += "?" + this[PRIVATE.encodeQueryData](data)
      }
      xhr.open(method, url)
      xhr.timeout = this[PRIVATE.loadTimeout]

      xhr.setRequestHeader("Authorization", `Bearer ${token}`)
      if (method !== "GET") {
        xhr.setRequestHeader("Content-Type", "application/json")
      }

      return new Promise((resolve, reject) => {
        xhr.addEventListener("load", () => {
          this[PRIVATE.processRequest](xhr, resolve, reject)
        })

        xhr.addEventListener("abort", () => {
          let rejectionError = new Error("The request has been aborted")
          rejectionError.name = "HttpAbortError"
          rejectionError.xhr = xhr
          reject(rejectionError)
        })

        xhr.addEventListener("error", (event) => {
          let rejectionError = new Error("A network error has occurred")
          rejectionError.name = "HttpError"
          rejectionError.xhr = xhr
          rejectionError.errorEvent = event
          reject(rejectionError)
        })

        xhr.addEventListener("timeout", () => {
          let rejectionError = new Error("The request has timed out, the " +
              `timeout is set to ${this[PRIVATE.loadTimeout]} ms`)
          rejectionError.name = "HttpTimeoutError"
          rejectionError.xhr = xhr
          reject(rejectionError)
        })

        if (method !== "GET") {
          xhr.send(JSON.stringify(data))
        } else {
          xhr.send()
        }
      })
    })
  }

  /**
   * Processes a request that has had its response fully loaded.
   *
   * @param {XMLHttpRequest} xhr The XML HTTP request that was just loaded.
   * @param {function(Object)} resolve Callback to execute if the request was
   *        successful. The callback should receive the response body parsed as
   *        JSON as its argument.
   * @param {function(Error)} reject Callback to execute if the request has
   *        failed.
   */
  [PRIVATE.processRequest](xhr, resolve, reject) {
    if (xhr.status !== 200) {
      let rejectionError = new Error("The Google API rejected the " +
        `request with ${xhr.status} (${xhr.statusText}) code`)
      rejectionError.xhr = xhr
      reject(rejectionError)
      return
    }

    let response = xhr.responseText
    try {
      resolve(JSON.parse(response))
    } catch (parseError) {
      let rejectionError = new Error(`Cannot parse response body: ${response}`)
      rejectionError.name = "HttpBodyParseError"
      rejectionError.xhr = xhr
      rejectionError.parseError = parseError
      reject(rejectionError)
    }
  }

  /**
   * Returns a query string generated from the provided data, without a
   * starting question mark (?).
   *
   * @param {Object<string, ?(boolean|number|string)>} data URI-compatible data
   *        to encode.
   * @return {string} URI-encoded query string generated from the provided
   *         data.
   */
  [PRIVATE.encodeQueryData](data) {
    return Object.keys(data).map((parameterName) => {
      return [parameterName, data[parameterName]]
    }).map((pair) => {
      return pair.map(encodeURIComponent).join("=")
    }).join(QUERY_PARAMETERS_SEPARATOR)
  }
}
<|endoftext|>


<commit_before>var _ = require('lodash');
var httpRequest = require('request');
var msb = require('msb');
var helpers = require('./helpers');
var debug = require('debug')('bus2http');
var router = exports;
var routes;

router.load = function load(newRoutes) {
  if (routes) {
    // Remove listeners for previously loaded routes
    routes.forEach(function(route) {
      if (!route.server) return;

      route.server.close();
      delete(route.server);
    });
  }

  routes = newRoutes;
  routes.forEach(function(route) {
    route.server = msb.Responder.createServer(route.bus)
    .use(function(request, response, next) {
      var options = {
        method: request.method,
        headers: _.omit(request.headers, 'host', 'connection', 'accept-encoding'),
        url: route.http.baseUrl + request.url
      };

      httpRequest(options, function(err, res, body) {
        if (err) return next(err);

        response.writeHead(res.statusCode, _.omit(res.headers, 'content-encoding'));

        var textType = helpers.contentTypeIsText(res.headers['content-type'] || '');

        if (textType === 'json') {
          body = JSON.parse(body);
        } else if (textType) {
          body = body.toString();
        }

        response.end(body);
      }).encoding = null;
    })
    .listen();
  });
};
<commit_msg>Tolerate invalid JSON from remote servers<commit_after>var _ = require('lodash');
var httpRequest = require('request');
var msb = require('msb');
var helpers = require('./helpers');
var debug = require('debug')('bus2http');
var router = exports;
var routes;

router.load = function load(newRoutes) {
  if (routes) {
    // Remove listeners for previously loaded routes
    routes.forEach(function(route) {
      if (!route.server) return;

      route.server.close();
      delete(route.server);
    });
  }

  routes = newRoutes;
  routes.forEach(function(route) {
    route.server = msb.Responder.createServer(route.bus)
    .use(function(request, response, next) {
      var options = {
        method: request.method,
        headers: _.omit(request.headers, 'host', 'connection', 'accept-encoding'),
        url: route.http.baseUrl + request.url
      };

      httpRequest(options, function(err, res, body) {
        if (err) return next(err);

        response.writeHead(res.statusCode, _.omit(res.headers, 'content-encoding'));

        var textType = helpers.contentTypeIsText(res.headers['content-type'] || '');

        if (textType === 'json') {
          try {
            body = JSON.parse(body);
          } catch (e) {
            // If it fails to parse, it will just be sent as a blob
          }
        } else if (textType) {
          body = body.toString();
        }

        response.end(body);
      }).encoding = null;
    })
    .listen();
  });
};
<|endoftext|>


<commit_before>define(["dojo/_base/declare"],
    function(declare) {

      var IdNotFoundException = declare("be.ppwcode.vernacular.persistence.IdNotFoundException", [], {

        // MUDO

        constructor: function(/*Object*/ props) {
        }

      });

      return IdNotFoundException;
    }
);
<commit_msg>IdNotFoundException now is a SemanticException<commit_after>define(["dojo/_base/declare", "ppwcode/exceptions/SemanticException"],
    function(declare, SemanticException) {

      var IdNotFoundException = declare("be.ppwcode.vernacular.persistence.IdNotFoundException", [SemanticException], {

        // MUDO

        constructor: function(/*Object*/ props) {
        }

      });

      return IdNotFoundException;
    }
);
<|endoftext|>


<commit_before>define(function(require) {

var Block = require("chunks/block");

var common = require("chunks/chunkCommon");
var CHUNK = common.CHUNK;

return function simpleMesh(blocks, cc, manager) {
	var cw = CHUNK.WIDTH;
	var cd = CHUNK.DEPTH;
	var ch = CHUNK.HEIGHT;

	var ccX = cc.x;
	var ccY = cc.y;
	var ccZ = cc.z;

	// Neighbouring chunks (for blockTypeAt)
	var nxc, pxc, nyc, pyc, nzc, pzc;

	var verts = [];
	var index = [];
	var uvs = [];

	var blocks = blocks;

	updateNeighbours();

	for (var ocX = 0; ocX < cw; ocX++) {
		for (var ocY = 0; ocY < ch; ocY++) {
			for (var ocZ = 0; ocZ < cd; ocZ++) {
				addBlockGeometry(verts, index, uvs, ocX, ocY, ocZ);
			}
		}
	}

	/**
	 * This function copies JavaScript floats into a typed array.
	 */
	function copy(src, dst) {
		for (var i = 0; i < src.length; i++) {
			dst[i] = src[i];
		}
	}

	var vertsa = new Float32Array(verts.length);
	copy(verts, vertsa);

	var indexa = new Uint16Array(index.length);
	copy(index, indexa);

	var uvsa = new Float32Array(uvs.length);
	copy(uvs, uvsa);

	//See the readme for documentation.
	var attributes = {
		position: {
			itemSize: 3,
			array: vertsa,
			numItems: verts.length,
		},
		index: {
			itemSize: 1,
			array: indexa,
			numItems: index.length,
		},
		uv: {
			itemSize: 2,
			array: uvsa,
			numItems: uvsa.length,
		},
	};
	var offsets = [{
		start: 0,
		count: index.length,
		index: 0,
	}];

	return {
		attributes: attributes,
		offsets: offsets,
		transferables: [vertsa.buffer, indexa.buffer, uvsa.buffer],
	};

	//Everything after here is just helper functions.

	//Can get blocks from up to 1 chunk away from out current chunk
	function blockTypeAt(ocX, ocY, ocZ) {
		if (ocX < 0) {
			return nxc ? nxc.block(cw - 1, ocY, ocZ) : null;
		} else if (ocX >= cw) {
			return pxc ? pxc.block(0, ocY, ocZ) : null;
		} else if (ocY < 0) {
			return nyc ? nyc.block(ocX, ch - 1, ocZ) : null;
		} else if (ocY >= ch) {
			return pyc ? pyc.block(ocX, 0, ocZ) : null;
		} else if (ocZ < 0) {
			return nzc ? nzc.block(ocX, ocY, cd - 1) : null;
		} else if (ocZ >= cd) {
			return pzc ? pzc.block(ocX, ocY, 0) : null;
		} else {
			return blocks[ocX*cw*ch + ocY*cw + ocZ];
		}
	}

	function addBlockGeometry(verts, index, uvs, ocX, ocY, ocZ) {
		if (empty(ocX, ocY, ocZ)) return;

		var blockType = blockTypeAt(ocX, ocY, ocZ);
		if (blockType < 0) return;

		//We only draw faces when there is no cube blocking it.
		var shown = [
			empty(ocX + 1, ocY,     ocZ    ),
			empty(ocX - 1, ocY,     ocZ    ),
			empty(ocX,     ocY + 1, ocZ    ),
			empty(ocX,     ocY - 1, ocZ    ),
			empty(ocX,     ocY,     ocZ + 1),
			empty(ocX,     ocY,     ocZ - 1)
		];

		function empty(ocX, ocY, ocZ) {
			return Block.isInvisible(blockTypeAt(ocX, ocY, ocZ));
		}
		var positions = [
			[ [ 1, 0, 0 ], [ 1, 1, 0 ], [ 1, 1, 1 ], [ 1, 0, 1 ], [   1, 0.5, 0.5 ] ],
			[ [ 0, 0, 1 ], [ 0, 1, 1 ], [ 0, 1, 0 ], [ 0, 0, 0 ], [   0, 0.5, 0.5 ] ],
			[ [ 0, 1, 1 ], [ 1, 1, 1 ], [ 1, 1, 0 ], [ 0, 1, 0 ], [ 0.5,   1, 0.5 ] ],
			[ [ 0, 0, 0 ], [ 1, 0, 0 ], [ 1, 0, 1 ], [ 0, 0, 1 ], [ 0.5,   0, 0.5 ] ],
			[ [ 0, 0, 1 ], [ 1, 0, 1 ], [ 1, 1, 1 ], [ 0, 1, 1 ], [ 0.5, 0.5,   1 ] ],
			[ [ 0, 1, 0 ], [ 1, 1, 0 ], [ 1, 0, 0 ], [ 0, 0, 0 ], [ 0.5, 0.5,   0 ] ],
		];

		var uvWind = [
			[ [ 1, 0 ], [ 1, 1 ], [ 0, 1 ], [ 0, 0 ], [ 0.5, 0.5 ] ],
			[ [ 1, 0 ], [ 1, 1 ], [ 0, 1 ], [ 0, 0 ], [ 0.5, 0.5 ] ],
			[ [ 0, 0 ], [ 1, 0 ], [ 1, 1 ], [ 0, 1 ], [ 0.5, 0.5 ] ],
			[ [ 1, 1 ], [ 0, 1 ], [ 0, 0 ], [ 1, 0 ], [ 0.5, 0.5 ] ],
			[ [ 0, 0 ], [ 1, 0 ], [ 1, 1 ], [ 0, 1 ], [ 0.5, 0.5 ] ],
			[ [ 1, 1 ], [ 0, 1 ], [ 0, 0 ], [ 1, 0 ], [ 0.5, 0.5 ] ],
		];

		var worldCords = [ocX + ccX*cw, ocY + ccY*ch, ocZ + ccZ*cd];

		for (var face = 0; face < 6; face++) {
			if (!shown[face]) continue;

			var tileOffset = Block.getTileOffset(blockType, face);

			for (var vert = 0; vert < 5; vert++) {
				// Each of x, y and z
				for (var comp = 0; comp < 3; comp++) {
					verts.push(worldCords[comp] + positions[face][vert][comp]);
				}
				buildUv(tileOffset, uvWind[face][vert]);
			}
			buildFace(face)
		}

		function buildFace(face) {
			var l = verts.length / 3;
			// Each face is made up of four triangles
			index.push(l-5, l-4, l-1);
			index.push(l-4, l-3, l-1);
			index.push(l-3, l-2, l-1);
			index.push(l-2, l-5, l-1);
		}

		function buildUv(tileOffset, uvWind) {
			var u = tileOffset[0] * Block.UV_UNIT;
			var v = 1 - Block.UV_UNIT - (tileOffset[1] * Block.UV_UNIT);
			u += uvWind[0] * Block.UV_UNIT;
			v += uvWind[1] * Block.UV_UNIT;
			// Add a 12.5% texel inset at the edges, to prevent rounding artifacts.
			u += (uvWind[0] === 1 ? -1 : 1) / (Block.ATLAS_SIZE * 8);
			v += (uvWind[1] === 1 ? -1 : 1) / (Block.ATLAS_SIZE * 8);
			uvs.push(u, v);
		}
	}

	function updateNeighbours() {
		pxc = manager.chunkAt(ccX + 1, ccY, ccZ);
		nxc = manager.chunkAt(ccX - 1, ccY, ccZ);
		pyc = manager.chunkAt(ccX, ccY + 1, ccZ);
		nyc = manager.chunkAt(ccX, ccY - 1, ccZ);
		pzc = manager.chunkAt(ccX, ccY, ccZ + 1);
		nzc = manager.chunkAt(ccX, ccY, ccZ - 1);
	}
}
});
<commit_msg>Update the commend on copying JavaScript numbers to typed arrays.<commit_after>define(function(require) {

var Block = require("chunks/block");

var common = require("chunks/chunkCommon");
var CHUNK = common.CHUNK;

return function simpleMesh(blocks, cc, manager) {
	var cw = CHUNK.WIDTH;
	var cd = CHUNK.DEPTH;
	var ch = CHUNK.HEIGHT;

	var ccX = cc.x;
	var ccY = cc.y;
	var ccZ = cc.z;

	// Neighbouring chunks (for blockTypeAt)
	var nxc, pxc, nyc, pyc, nzc, pzc;

	var verts = [];
	var index = [];
	var uvs = [];

	var blocks = blocks;

	updateNeighbours();

	for (var ocX = 0; ocX < cw; ocX++) {
		for (var ocY = 0; ocY < ch; ocY++) {
			for (var ocZ = 0; ocZ < cd; ocZ++) {
				addBlockGeometry(verts, index, uvs, ocX, ocY, ocZ);
			}
		}
	}

	function copy(src, dst) {
		for (var i = 0; i < src.length; i++) {
			dst[i] = src[i];
		}
	}

	// Here we're just copying the native JavaScript numbers into a typed Float32 array.
	var vertsa = new Float32Array(verts.length);
	copy(verts, vertsa);

	var indexa = new Uint16Array(index.length);
	copy(index, indexa);

	var uvsa = new Float32Array(uvs.length);
	copy(uvs, uvsa);

	//See the readme for documentation.
	var attributes = {
		position: {
			itemSize: 3,
			array: vertsa,
			numItems: verts.length,
		},
		index: {
			itemSize: 1,
			array: indexa,
			numItems: index.length,
		},
		uv: {
			itemSize: 2,
			array: uvsa,
			numItems: uvsa.length,
		},
	};
	var offsets = [{
		start: 0,
		count: index.length,
		index: 0,
	}];

	return {
		attributes: attributes,
		offsets: offsets,
		transferables: [vertsa.buffer, indexa.buffer, uvsa.buffer],
	};

	//Everything after here is just helper functions.

	//Can get blocks from up to 1 chunk away from out current chunk
	function blockTypeAt(ocX, ocY, ocZ) {
		if (ocX < 0) {
			return nxc ? nxc.block(cw - 1, ocY, ocZ) : null;
		} else if (ocX >= cw) {
			return pxc ? pxc.block(0, ocY, ocZ) : null;
		} else if (ocY < 0) {
			return nyc ? nyc.block(ocX, ch - 1, ocZ) : null;
		} else if (ocY >= ch) {
			return pyc ? pyc.block(ocX, 0, ocZ) : null;
		} else if (ocZ < 0) {
			return nzc ? nzc.block(ocX, ocY, cd - 1) : null;
		} else if (ocZ >= cd) {
			return pzc ? pzc.block(ocX, ocY, 0) : null;
		} else {
			return blocks[ocX*cw*ch + ocY*cw + ocZ];
		}
	}

	function addBlockGeometry(verts, index, uvs, ocX, ocY, ocZ) {
		if (empty(ocX, ocY, ocZ)) return;

		var blockType = blockTypeAt(ocX, ocY, ocZ);
		if (blockType < 0) return;

		//We only draw faces when there is no cube blocking it.
		var shown = [
			empty(ocX + 1, ocY,     ocZ    ),
			empty(ocX - 1, ocY,     ocZ    ),
			empty(ocX,     ocY + 1, ocZ    ),
			empty(ocX,     ocY - 1, ocZ    ),
			empty(ocX,     ocY,     ocZ + 1),
			empty(ocX,     ocY,     ocZ - 1)
		];

		function empty(ocX, ocY, ocZ) {
			return Block.isInvisible(blockTypeAt(ocX, ocY, ocZ));
		}
		var positions = [
			[ [ 1, 0, 0 ], [ 1, 1, 0 ], [ 1, 1, 1 ], [ 1, 0, 1 ], [   1, 0.5, 0.5 ] ],
			[ [ 0, 0, 1 ], [ 0, 1, 1 ], [ 0, 1, 0 ], [ 0, 0, 0 ], [   0, 0.5, 0.5 ] ],
			[ [ 0, 1, 1 ], [ 1, 1, 1 ], [ 1, 1, 0 ], [ 0, 1, 0 ], [ 0.5,   1, 0.5 ] ],
			[ [ 0, 0, 0 ], [ 1, 0, 0 ], [ 1, 0, 1 ], [ 0, 0, 1 ], [ 0.5,   0, 0.5 ] ],
			[ [ 0, 0, 1 ], [ 1, 0, 1 ], [ 1, 1, 1 ], [ 0, 1, 1 ], [ 0.5, 0.5,   1 ] ],
			[ [ 0, 1, 0 ], [ 1, 1, 0 ], [ 1, 0, 0 ], [ 0, 0, 0 ], [ 0.5, 0.5,   0 ] ],
		];

		var uvWind = [
			[ [ 1, 0 ], [ 1, 1 ], [ 0, 1 ], [ 0, 0 ], [ 0.5, 0.5 ] ],
			[ [ 1, 0 ], [ 1, 1 ], [ 0, 1 ], [ 0, 0 ], [ 0.5, 0.5 ] ],
			[ [ 0, 0 ], [ 1, 0 ], [ 1, 1 ], [ 0, 1 ], [ 0.5, 0.5 ] ],
			[ [ 1, 1 ], [ 0, 1 ], [ 0, 0 ], [ 1, 0 ], [ 0.5, 0.5 ] ],
			[ [ 0, 0 ], [ 1, 0 ], [ 1, 1 ], [ 0, 1 ], [ 0.5, 0.5 ] ],
			[ [ 1, 1 ], [ 0, 1 ], [ 0, 0 ], [ 1, 0 ], [ 0.5, 0.5 ] ],
		];

		var worldCords = [ocX + ccX*cw, ocY + ccY*ch, ocZ + ccZ*cd];

		for (var face = 0; face < 6; face++) {
			if (!shown[face]) continue;

			var tileOffset = Block.getTileOffset(blockType, face);

			for (var vert = 0; vert < 5; vert++) {
				// Each of x, y and z
				for (var comp = 0; comp < 3; comp++) {
					verts.push(worldCords[comp] + positions[face][vert][comp]);
				}
				buildUv(tileOffset, uvWind[face][vert]);
			}
			buildFace(face)
		}

		function buildFace(face) {
			var l = verts.length / 3;
			// Each face is made up of four triangles
			index.push(l-5, l-4, l-1);
			index.push(l-4, l-3, l-1);
			index.push(l-3, l-2, l-1);
			index.push(l-2, l-5, l-1);
		}

		function buildUv(tileOffset, uvWind) {
			var u = tileOffset[0] * Block.UV_UNIT;
			var v = 1 - Block.UV_UNIT - (tileOffset[1] * Block.UV_UNIT);
			u += uvWind[0] * Block.UV_UNIT;
			v += uvWind[1] * Block.UV_UNIT;
			// Add a 12.5% texel inset at the edges, to prevent rounding artifacts.
			u += (uvWind[0] === 1 ? -1 : 1) / (Block.ATLAS_SIZE * 8);
			v += (uvWind[1] === 1 ? -1 : 1) / (Block.ATLAS_SIZE * 8);
			uvs.push(u, v);
		}
	}

	function updateNeighbours() {
		pxc = manager.chunkAt(ccX + 1, ccY, ccZ);
		nxc = manager.chunkAt(ccX - 1, ccY, ccZ);
		pyc = manager.chunkAt(ccX, ccY + 1, ccZ);
		nyc = manager.chunkAt(ccX, ccY - 1, ccZ);
		pzc = manager.chunkAt(ccX, ccY, ccZ + 1);
		nzc = manager.chunkAt(ccX, ccY, ccZ - 1);
	}
}
});
<|endoftext|>


<commit_before>// Copyright (c) 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

const fs = require('fs');
const http = require('http');
const path = require('path');
const parseURL = require('url').parse;
const {argv} = require('yargs');

const serverPort = parseInt(process.env.PORT, 10) || 8090;

const target = argv.target || 'Default';

/**
 * When you run npm run components-server we run the script as is from scripts/,
 * but when this server is run as part of a test suite it's run from
 * out/Default/gen/scripts, so we have to do a bit of path mangling to figure
 * out where we are.
 */
const isRunningInGen = __dirname.includes(path.join(path.sep, 'gen', path.sep, 'scripts'));

const pathToBuiltOutTargetDirectory = isRunningInGen ? path.resolve(path.join(__dirname), '..', '..', '..') :
                                                       path.resolve(path.join(__dirname, '..', '..', 'out', target));
const devtoolsFrontendFolder = path.resolve(path.join(pathToBuiltOutTargetDirectory, 'gen', 'front_end'));

if (!fs.existsSync(devtoolsFrontendFolder)) {
  console.error(`ERROR: Generated front_end folder (${devtoolsFrontendFolder}) does not exist.`);
  console.log(
      'The components server works from the built Ninja output; you may need to run Ninja to update your built DevTools.');
  console.log('If you build to a target other than default, you need to pass --target=X as an argument');
  process.exit(1);
}

const server = http.createServer(requestHandler);
server.listen(serverPort);
server.once('listening', () => {
  if (process.send) {
    process.send(serverPort);
  }
  console.log(`Started components server at http://localhost:${serverPort}\n`);
});

server.once('error', error => {
  if (process.send) {
    process.send('ERROR');
  }
  throw error;
});

function createComponentIndexFile(componentPath, componentExamples) {
  const componentName = componentPath.replace('/', '').replace(/_/g, ' ');
  // clang-format off
  return `<!DOCTYPE html>

      <meta charset="UTF-8" />
      <meta name="viewport" content="width=device-width" />
      <title>DevTools component: ${componentName}</title>

${componentName}

      ${componentExamples.map(example => {
        const fullPath = path.join('component_docs', componentPath, example);
        return `

${example}

`;
      }).join('\n')}

  `;
  // clang-format on
}

function createServerIndexFile(componentNames) {
  // clang-format off
  return `<!DOCTYPE html>

      <meta charset="UTF-8" />
      <meta name="viewport" content="width=device-width" />
      <title>DevTools components</title>

DevTools components

        ${componentNames.map(name => {
          const niceName = name.replace(/_/g, ' ');
          return `<li><a href='/${name}'>${niceName}</a></li>`;
        }).join('\n')}

  `;
  // clang-format on
}

async function getExamplesForPath(filePath) {
  const componentDirectory = path.join(devtoolsFrontendFolder, 'component_docs', filePath);
  const allFiles = await fs.promises.readdir(componentDirectory);
  const htmlExampleFiles = allFiles.filter(file => {
    return path.extname(file) === '.html';
  });

  return createComponentIndexFile(filePath, htmlExampleFiles);
}

function respondWithHtml(response, html) {
  response.setHeader('Content-Type', 'text/html; charset=utf-8');
  response.writeHead(200);
  response.write(html, 'utf8');
  response.end();
}

function send404(response, message) {
  response.writeHead(404);
  response.write(message, 'utf8');
  response.end();
}

async function checkFileExists(filePath) {
  try {
    const errorsAccessingFile = await fs.promises.access(filePath, fs.constants.R_OK);
    return !errorsAccessingFile;
  } catch (e) {
    return false;
  }
}

/**
 * In Devtools-Frontend we load images without a leading slash, e.g.
 * url(Images/checker.png). This works within devtools, but breaks this component
 * server as the path ends up as /component_docs/my_component/Images/checker.png.
 * So we check if the path ends in Images/*.* and if so, remove anything before
 * it. Then it will be resolved correctly.
 */
function normalizeImagePathIfRequired(filePath) {
  const imagePathRegex = /\/Images\/(\S+)\.(\w{3})/;
  const match = imagePathRegex.exec(filePath);
  if (!match) {
    return filePath;
  }

  const [, imageName, imageExt] = match;
  const normalizedPath = path.join('Images', `${imageName}.${imageExt}`);
  return normalizedPath;
}

async function requestHandler(request, response) {
  const filePath = parseURL(request.url).pathname;
  if (filePath === '/favicon.ico') {
    send404(response, '404, no favicon');
    return;
  }

  if (filePath === '/' || filePath === '/index.html') {
    const components = await fs.promises.readdir(path.join(devtoolsFrontendFolder, 'component_docs'));
    const html = createServerIndexFile(components.filter(filePath => {
      const stats = fs.lstatSync(path.join(devtoolsFrontendFolder, 'component_docs', filePath));
      // Filter out some build config files (tsconfig, d.ts, etc), and just list the directories.
      return stats.isDirectory();
    }));
    respondWithHtml(response, html);
  } else if (path.extname(filePath) === '') {
    // This means it's a component path like /breadcrumbs.
    const componentHtml = await getExamplesForPath(filePath);
    respondWithHtml(response, componentHtml);
    return;
  } else if (/component_docs\/(.+)\/(.+)\.html/.test(filePath)) {
    /** This conditional checks if we are viewing an individual example's HTML
     *  file. e.g. localhost:8090/component_docs/data_grid/basic.html For each
     *  example we inject themeColors.css into the page so all CSS variables
     *  that components use are available.
     */
    const fileContents = await fs.promises.readFile(path.join(devtoolsFrontendFolder, filePath), {encoding: 'utf8'});
    const themeColoursLink = '<link rel="stylesheet" href="/ui/themeColors.css" type="text/css" />';
    const toggleDarkModeScript = '';
    const newFileContents = fileContents.replace('


<commit_before><commit_msg>macosx-left-menu-fix<commit_after><|endoftext|>


<commit_before>;(function($, window, undefined) {
    "use strict";

    /** @object keyMap Maps key codes to the associated function */
    var keyMap = {
        'UP': 38,
        'DOWN': 40,
        'ENTER': 13
    };

    /**
     * Shopware Live Search Plugin.
     *
     * The plugin fires the ajax search request, render the results inside the modal
     * and controlling the keyboard navigation inside the search results.
     *
     * @example
     *  <input type="search" data-live-search="true">
     */
    $.plugin('liveSearch', {

        /** @object Plugin default configuration */
        defaults: {
            /** @int minLength minimum term characters which will be needed before the ajax request is triggered */
            minLength: 3,

            /** @int searchDelay time in milliseconds before ajax requests is triggered after last key down  */
            searchDelay: 250,

            /** @string activeCls Class which will be added when the drop down was triggered */
            activeCls: 'js--is-active',

            /** @string resultsSelector Selector which will contain the search results */
            resultsSelector: '.main-search--results',

            /** @string loadingIndicatorSelector Selector of the ajax loading indicator */
            loadingIndicatorSelector: '.form--ajax-loader',

            /** @string requestUrl The endpoint which will be triggered by the live search */
            requestUrl: '',

            /** @boolean Truthy to enable keyboard navigation, if falsy the keyboard navigation will be disabled */
            enableKeyboardNavigation: true,

            /** @string|integer animationSpeed The speed of the fading animation */
            animationSpeed: 'fast'
        },

        /**
         * Initializes the plugin
         *
         * @returns {Plugin}
         */
        init: function () {
            var me = this;

            me.opts.requestUrl = $.controller.ajax_search || '';

            if (!me.opts.requestUrl.length) {
                throw new Error('Parameter "requestUrl" needs to be set.');
                return false;
            }

            me._lastSearchTerm = '';
            me.$parent = me.$el.parent('form');
            me.$results = me.$parent.next(me.opts.resultsSelector).hide();
            me.$loader = me.$parent.find(me.opts.loadingIndicatorSelector);

            me._on(me.$el, 'keyup', $.proxy(me.onKeyUp, me));
            me._on(me.$el, 'keydown', $.proxy(me.onKeyDown, me));
            me._on(me.$el, 'blur', $.proxy(me.onBlur, me));
        },

        onBlur: function (event) {
            var me = this,
                target = event.target || event.currentTarget;

            if($.contains(me.$results[0], target)) {
                return;
            }
            me.closeResult();
        },

        onKeyUp: function (event) {
            var me = this,
                term = me.$el.val();

            if (me._keyupTimeout) {
                window.clearTimeout(me._keyupTimeout);
            }

            if (me.opts.minLength && term.length < me.opts.minLength) {
                me._lastSearchTerm = '';
                me.closeResult();
                return;
            }

            if(term === me._lastSearchTerm) {
                return;
            }

            me._keyupTimeout = window.setTimeout($.proxy(me.triggerSearchRequest, me, term), me.opts.searchDelay);
        },

        /**
         * Event handler method which will be fired when the user presses a key when
         * focusing the field.
         *
         * @param event {jQuery.Event}
         */
        onKeyDown: function (event) {
            var me = this,
                keyCode = event.which,
                shouldPrevent = me.opts.enableKeyboardNavigation && (keyCode === keyMap.UP || keyCode === keyMap.DOWN || keyCode === keyMap.ENTER);

            if (shouldPrevent) {
                event.preventDefault();
            }

            if(me.$results.hasClass(me.opts.activeCls)) {
                me.onKeyboardNavigation(keyCode);
            }

            return !shouldPrevent;
        },

        /**
         * Triggers an AJAX request with the given {@param searchTerm}.
         *
         * @param {String} searchTerm
         */
        triggerSearchRequest: function (searchTerm) {
            var me = this;

            searchTerm = me._sanitizeSearchTerm(searchTerm);

            me.$loader.fadeIn('fast');

            me._lastSearchTerm = searchTerm;

            $.ajax({
                'url': me.opts.requestUrl,
                'data': {
                    'sSearch': searchTerm
                },
                'success': $.proxy(me.showResult, me)
            });
        },

        /**
         * Shows the {@param response} of the AJAX request and slides down the results container.
         *
         * @param {Object} response
         */
        showResult: function (response) {
            var me = this;

            me.$loader.fadeOut('fast');
            me.$results.empty().html(response).slideDown().addClass(me.opts.activeCls);
        },

        /**
         * Closes the results container.
         *
         * @returns {Void}
         */
        closeResult: function() {
            var me = this;

            me.$results.removeClass(me.opts.activeCls).fadeOut(me.opts.animationSpeed, function() {
                me.$results.empty();
            });
        },

        /**
         * Adds support to navigate using the keyboard.
         *
         * @param {Number} key - Keycode number
         */
        onKeyboardNavigation: function(key) {
            var me = this,
                selected = me.$results.find('.' + me.defaults.activeCls);

            if (key === keyMap.DOWN) {
                if(!selected.length) {
                    me.$results.find('li').first().addClass(me.defaults.activeCls);
                    return;
                }

                me.$results.find('li').removeClass(me.defaults.activeCls);
                if(selected.next().length != 0) {
                    selected.next().addClass(me.defaults.activeCls);
                    return;
                }

                selected.siblings().first().addClass(me.defaults.activeCls);
                return;
            }

            if (key === keyMap.UP) {
                if(!selected.length) {
                    me.$results.find('li').last().addClass(me.defaults.activeCls);
                    return;
                }

                me.$results.find('li').removeClass(me.defaults.activeCls);
                if (selected.prev().length != 0) {
                    selected.prev().addClass(me.defaults.activeCls);
                    return;
                }

                selected.siblings().last().addClass(me.defaults.activeCls);
                return;
            }

            if(key == keyMap.ENTER) {

                if (selected.length) {

                    window.location.href = selected.find('a').attr('href');
                }
            }
        },

        /**
         * Sanitize the search result like trimming the search term.
         *
         * @param {String} term - String which should be sanitized
         * @returns {String} Sanitized string
         * @private
         */
        _sanitizeSearchTerm: function (term) {
            return $.trim(term);
        },

        /**
         * Destroys the plugin.
         *
         * @returns {Void}
         */
        destroy: function () {
            this._destroy();
        }
    });
})(jQuery, window);<commit_msg>SW-9898 - fixed enter key on search results<commit_after>;(function($, window, undefined) {
    "use strict";

    /** @object keyMap Maps key codes to the associated function */
    var keyMap = {
        'UP': 38,
        'DOWN': 40,
        'ENTER': 13
    };

    /**
     * Shopware Live Search Plugin.
     *
     * The plugin fires the ajax search request, render the results inside the modal
     * and controlling the keyboard navigation inside the search results.
     *
     * @example
     *  <input type="search" data-live-search="true">
     */
    $.plugin('liveSearch', {

        /** @object Plugin default configuration */
        defaults: {
            /** @int minLength minimum term characters which will be needed before the ajax request is triggered */
            minLength: 3,

            /** @int searchDelay time in milliseconds before ajax requests is triggered after last key down  */
            searchDelay: 250,

            /** @string activeCls Class which will be added when the drop down was triggered */
            activeCls: 'js--is-active',

            /** @string resultsSelector Selector which will contain the search results */
            resultsSelector: '.main-search--results',

            /** @string loadingIndicatorSelector Selector of the ajax loading indicator */
            loadingIndicatorSelector: '.form--ajax-loader',

            /** @string requestUrl The endpoint which will be triggered by the live search */
            requestUrl: '',

            /** @boolean Truthy to enable keyboard navigation, if falsy the keyboard navigation will be disabled */
            enableKeyboardNavigation: true,

            /** @string|integer animationSpeed The speed of the fading animation */
            animationSpeed: 'fast'
        },

        /**
         * Initializes the plugin
         *
         * @returns {Plugin}
         */
        init: function () {
            var me = this;

            me.opts.requestUrl = $.controller.ajax_search || '';

            if (!me.opts.requestUrl.length) {
                throw new Error('Parameter "requestUrl" needs to be set.');
                return false;
            }

            me._lastSearchTerm = '';
            me.$parent = me.$el.parent('form');
            me.$results = me.$parent.next(me.opts.resultsSelector).hide();
            me.$loader = me.$parent.find(me.opts.loadingIndicatorSelector);

            me._on(me.$el, 'keyup', $.proxy(me.onKeyUp, me));
            me._on(me.$el, 'keydown', $.proxy(me.onKeyDown, me));
            me._on(me.$el, 'blur', $.proxy(me.onBlur, me));
        },

        onBlur: function (event) {
            var me = this,
                target = event.target || event.currentTarget;

            if($.contains(me.$results[0], target)) {
                return;
            }
            me.closeResult();
        },

        onKeyUp: function (event) {
            var me = this,
                term = me.$el.val();

            if (me._keyupTimeout) {
                window.clearTimeout(me._keyupTimeout);
            }

            if (me.opts.minLength && term.length < me.opts.minLength) {
                me._lastSearchTerm = '';
                me.closeResult();
                return;
            }

            if(term === me._lastSearchTerm) {
                return;
            }

            me._keyupTimeout = window.setTimeout($.proxy(me.triggerSearchRequest, me, term), me.opts.searchDelay);
        },

        /**
         * Event handler method which will be fired when the user presses a key when
         * focusing the field.
         *
         * @param event {jQuery.Event}
         */
        onKeyDown: function (event) {
            var me = this,
                keyCode = event.which,
                shouldPrevent = me.opts.enableKeyboardNavigation && (keyCode === keyMap.UP || keyCode === keyMap.DOWN || keyCode === keyMap.ENTER);

            if (shouldPrevent) {
                event.preventDefault();
            }

            if(me.$results.hasClass(me.opts.activeCls)) {
                me.onKeyboardNavigation(keyCode);
            }

            return !shouldPrevent;
        },

        /**
         * Triggers an AJAX request with the given {@param searchTerm}.
         *
         * @param {String} searchTerm
         */
        triggerSearchRequest: function (searchTerm) {
            var me = this;

            searchTerm = me._sanitizeSearchTerm(searchTerm);

            me.$loader.fadeIn('fast');

            me._lastSearchTerm = searchTerm;

            $.ajax({
                'url': me.opts.requestUrl,
                'data': {
                    'sSearch': searchTerm
                },
                'success': $.proxy(me.showResult, me)
            });
        },

        /**
         * Shows the {@param response} of the AJAX request and slides down the results container.
         *
         * @param {Object} response
         */
        showResult: function (response) {
            var me = this;

            me.$loader.fadeOut('fast');
            me.$results.empty().html(response).slideDown().addClass(me.opts.activeCls);
        },

        /**
         * Closes the results container.
         *
         * @returns {Void}
         */
        closeResult: function() {
            var me = this;

            me.$results.removeClass(me.opts.activeCls).fadeOut(me.opts.animationSpeed, function() {
                me.$results.empty();
            });
        },

        /**
         * Adds support to navigate using the keyboard.
         *
         * @param {Number} key - Keycode number
         */
        onKeyboardNavigation: function(key) {
            var me = this,
                selected = me.$results.find('.' + me.defaults.activeCls),
                form = me.$el.closest('form');

            if (key === keyMap.DOWN) {
                if(!selected.length) {
                    me.$results.find('li').first().addClass(me.defaults.activeCls);
                    return;
                }

                me.$results.find('li').removeClass(me.defaults.activeCls);
                if(selected.next().length != 0) {
                    selected.next().addClass(me.defaults.activeCls);
                    return;
                }

                selected.siblings().first().addClass(me.defaults.activeCls);
                return;
            }

            if (key === keyMap.UP) {
                if(!selected.length) {
                    me.$results.find('li').last().addClass(me.defaults.activeCls);
                    return;
                }

                me.$results.find('li').removeClass(me.defaults.activeCls);
                if (selected.prev().length != 0) {
                    selected.prev().addClass(me.defaults.activeCls);
                    return;
                }

                selected.siblings().last().addClass(me.defaults.activeCls);
                return;
            }

            if(key == keyMap.ENTER) {

                if (selected.length) {

                    window.location.href = selected.find('a').attr('href');
                    return;
                }

                form.submit();
            }
        },

        /**
         * Sanitize the search result like trimming the search term.
         *
         * @param {String} term - String which should be sanitized
         * @returns {String} Sanitized string
         * @private
         */
        _sanitizeSearchTerm: function (term) {
            return $.trim(term);
        },

        /**
         * Destroys the plugin.
         *
         * @returns {Void}
         */
        destroy: function () {
            this._destroy();
        }
    });
})(jQuery, window);<|endoftext|>


<commit_before>// This file contains a package.json for the dependencies of the command-line
// tool.

// We put this in a JS file so that it can contain comments. It is processed
// into a package.json file by generate-dev-bundle.sh.

var packageJson = {
  name: "meteor-dev-bundle-tool",
  private: true,
  dependencies: {
    // Explicit dependency because we are replacing it with a bundled version
    // and we want to make sure there are no dependencies on a higher version
    npm: "6.4.1",
    pacote: "https://github.com/meteor/pacote/tarball/a3285854a68e4969d6c8abaa74407f7d113b178f",
    "node-gyp": "3.7.0",
    "node-pre-gyp": "0.10.3",
    "meteor-babel": "7.1.3",
    "meteor-promise": "0.8.6",
    promise: "8.0.1",
    reify: "0.17.3",
    fibers: "2.0.0",
    // So that Babel can emit require("@babel/runtime/helpers/...") calls.
    "@babel/runtime": "7.1.2",
    // For backwards compatibility with isopackets that still depend on
    // babel-runtime rather than @babel/runtime.
    "babel-runtime": "7.0.0-beta.3",
    // Not yet upgrading Underscore from 1.5.2 to 1.7.0 (which should be done
    // in the package too) because we should consider using lodash instead
    // (and there are backwards-incompatible changes either way).
    underscore: "1.5.2",
    "source-map-support": "https://github.com/meteor/node-source-map-support/tarball/1912478769d76e5df4c365e147f25896aee6375e",
    semver: "5.4.1",
    request: "2.88.0",
    uuid: "3.3.2",
    fstream: "https://github.com/meteor/fstream/tarball/cf4ea6c175355cec7bee38311e170d08c4078a5d",
    tar: "2.2.1",
    kexec: "3.0.0",
    "source-map": "0.5.3",
    chalk: "0.5.1",
    sqlite3: "3.1.8",
    netroute: "1.0.2",
    "http-proxy": "1.16.2",
    "wordwrap": "0.0.2",
    "moment": "2.20.1",
    "rimraf": "2.6.2",
    "glob": "7.1.2",
    ignore: "3.3.7",
    // XXX: When we update this, see if it fixes this Github issue:
    // https://github.com/jgm/CommonMark/issues/276 . If it does, remove the
    // workaround from the tool.
    "commonmark": "0.15.0",
    escope: "3.6.0",
    split2: "2.2.0",
    multipipe: "2.0.1",
    pathwatcher: "7.1.1",
    optimism: "0.6.3",
    'lru-cache': '4.1.3'
  }
};

if (process.platform === 'win32') {
  // Remove dependencies that do not work on Windows
  delete packageJson.dependencies.netroute;
  delete packageJson.dependencies.kexec;
}

process.stdout.write(JSON.stringify(packageJson, null, 2) + '\n');
<commit_msg>Update optimism npm package to version 0.6.8.<commit_after>// This file contains a package.json for the dependencies of the command-line
// tool.

// We put this in a JS file so that it can contain comments. It is processed
// into a package.json file by generate-dev-bundle.sh.

var packageJson = {
  name: "meteor-dev-bundle-tool",
  private: true,
  dependencies: {
    // Explicit dependency because we are replacing it with a bundled version
    // and we want to make sure there are no dependencies on a higher version
    npm: "6.4.1",
    pacote: "https://github.com/meteor/pacote/tarball/a3285854a68e4969d6c8abaa74407f7d113b178f",
    "node-gyp": "3.7.0",
    "node-pre-gyp": "0.10.3",
    "meteor-babel": "7.1.3",
    "meteor-promise": "0.8.6",
    promise: "8.0.1",
    reify: "0.17.3",
    fibers: "2.0.0",
    // So that Babel can emit require("@babel/runtime/helpers/...") calls.
    "@babel/runtime": "7.1.2",
    // For backwards compatibility with isopackets that still depend on
    // babel-runtime rather than @babel/runtime.
    "babel-runtime": "7.0.0-beta.3",
    // Not yet upgrading Underscore from 1.5.2 to 1.7.0 (which should be done
    // in the package too) because we should consider using lodash instead
    // (and there are backwards-incompatible changes either way).
    underscore: "1.5.2",
    "source-map-support": "https://github.com/meteor/node-source-map-support/tarball/1912478769d76e5df4c365e147f25896aee6375e",
    semver: "5.4.1",
    request: "2.88.0",
    uuid: "3.3.2",
    fstream: "https://github.com/meteor/fstream/tarball/cf4ea6c175355cec7bee38311e170d08c4078a5d",
    tar: "2.2.1",
    kexec: "3.0.0",
    "source-map": "0.5.3",
    chalk: "0.5.1",
    sqlite3: "3.1.8",
    netroute: "1.0.2",
    "http-proxy": "1.16.2",
    "wordwrap": "0.0.2",
    "moment": "2.20.1",
    "rimraf": "2.6.2",
    "glob": "7.1.2",
    ignore: "3.3.7",
    // XXX: When we update this, see if it fixes this Github issue:
    // https://github.com/jgm/CommonMark/issues/276 . If it does, remove the
    // workaround from the tool.
    "commonmark": "0.15.0",
    escope: "3.6.0",
    split2: "2.2.0",
    multipipe: "2.0.1",
    pathwatcher: "7.1.1",
    optimism: "0.6.8",
    'lru-cache': '4.1.3'
  }
};

if (process.platform === 'win32') {
  // Remove dependencies that do not work on Windows
  delete packageJson.dependencies.netroute;
  delete packageJson.dependencies.kexec;
}

process.stdout.write(JSON.stringify(packageJson, null, 2) + '\n');
<|endoftext|>