<commit_before>/**
    @file       app-controller.js
    @brief      Application controller and router.

    @author     Oleg Kertanov <okertanov@gmail.com>
    @date       May 2012
    @copyright  Copyright (C) 2012 Oleg Kertanov <okertanov@gmail.com>
    @license    BSD
    @see LICENSE file
*/

(function()
{

// Strict mode by default
"use strict";

var underscore = require('underscore')._,
    Db = require('./api-db.js'),
    ApiDb = Db.ApiDb,
    DbName = 'garage';

function IsEmptyObject(o)
{
    return Object.keys(o).length === 0;
}

exports.ApiController =
{
    Initialize: function()
    {
        console.log('ApiController.Initialize');

        ApiDb.Connect(DbName);
    },
    Terminate: function()
    {
        console.log('ApiController.Terminate');

        ApiDb.Disconnect();
    },
    ApplicationGet: function(req, res, next)
    {
        console.log('ApiController.ApplicationGet');

        var response = JSON.stringify({});

        this.SendJson(res, response);
    },
    CategoriesGet: function(req, res, next)
    {
        console.log('ApiController.CategoriesGet');

        var that = this;
        ApiDb.Category.find({}).asc('name').run(function(err, data){
            if ( !err )
                that.SendJson(res, data);
            else
                that.SendError(res, 'Invalid Query: ' + req.method + ' ' + req.url, 400);
        });
    },
    CategoriesCreateCategory: function(req, res, next)
    {
        console.log('ApiController.CategoriesCreateCategory');

        var that  = this,
            name  = req.params.name        || req.body.name,
            descr = req.params.description || req.body.description,
            cat   = new ApiDb.Category({name: name, description: descr, type: 'user'});

        cat.save(function(err){
            if ( !err )
                that.SendJson(res, cat);
            else
                that.SendError(res, 'Invalid Query: ' + req.method + ' ' + req.url, 400);
        });
    },
    CategoryGet: function(req, res, next)
    {
        console.log('ApiController.CategoryGet');

        var that = this,
            cat  = req.params.cat;

        ApiDb.Category.findOne({_id: cat}, function(err, data){
            if ( !err )
                that.SendJson(res, data);
            else
                that.SendError(res, 'Invalid Query: ' + req.method + ' ' + req.url, 400);
        });
    },
    CategoryEdit: function(req, res, next)
    {
        console.log('ApiController.CategoryEdit');

        var that  = this,
            cat   = req.params.cat,
            name  = req.body.name,
            descr = req.body.description,
            dirty = false;

        ApiDb.Category.findOne({_id: cat}, function(err, data){
            if ( !err )
            {
                if ( name && name.length )
                    data.name = name, dirty = true;

                if ( descr && descr.length )
                    data.description = descr, dirty = true;

                if ( dirty === true )
                {
                    data.save(function(err){
                        if ( !err )
                            that.SendJson(res, data);
                        else
                            that.SendError(res, 'Invalid Query: ' + req.method + ' ' + req.url, 400);
                    });
                }
                else
                    that.SendJson(res, data);
            }
            else
                that.SendError(res, 'Invalid Query: ' + req.method + ' ' + req.url, 400);
        });
    },
    CategoryDelete: function(req, res, next)
    {
        console.log('ApiController.CategoryDelete');

        var that = this,
            cat  = req.params.cat;

        ApiDb.Category.findOne({_id: cat}, function(err, data){
            if ( !err )
                data.remove(), that.SendJson(res, data);
            else
                that.SendError(res, 'Invalid Query: ' + req.method + ' ' + req.url, 400);
        });
    },
    ItemsGetItems: function(req, res, next)
    {
        var that = this,
            cat  = req.params.cat;

        ApiDb.Item.find({category: cat}).desc('date').run(function(err, data){
            if ( !err )
                that.SendJson(res, data);
            else
                that.SendError(res, 'Invalid Query: ' + req.method + ' ' + req.url, 400);
        });
    },
    ItemsGetItem: function(req, res, next)
    {
        console.log('ApiController.ItemsGet');

        var that = this,
            cat  = req.params.cat,
            item = req.params.id;

        ApiDb.Category.findOne({_id: item, category: cat}, function(err, data){
            if ( !err )
                that.SendJson(res, data);
            else
                that.SendError(res, 'Invalid Query: ' + req.method + ' ' + req.url, 400);
        });
    },
    ItemsCreateItem: function(req, res, next)
    {
        console.log('ApiController.ItemsCreateItem');

        var that   = this,
            name   = req.params.name        || req.body.name,
            descr  = req.params.description || req.body.description,
            user   = req.params.user        || req.body.user,
            cat    = req.params.category    || req.body.category;

        ApiDb.Category.findOne({_id: cat}, function(err, data){
            if ( !err )
            {
                console.dir(data);

                var item = new ApiDb.Item({name: name,
                                           description: descr,
                                           category: data._id,
                                           images: []});
                item.save(function(err){
                    if ( !err )
                        that.SendJson(res, item);
                    else
                        that.SendError(res, 'Invalid Query: ' + req.method + ' ' + req.url + '\nError: ' + err, 400);
                });
            }
            else
                that.SendError(res, 'Invalid Query: ' + req.method + ' ' + req.url + '\nError: ' + err, 400);
        });
    },
    ItemsEditItem: function(req, res, next)
    {
        console.log('ApiController.ItemsEdit');
    },
    ItemsDeleteItem: function(req, res, next)
    {
        console.log('ApiController.ItemsDelete');

        var that = this,
            cat  = req.params.cat,
            item = req.params.id;

        ApiDb.Category.findOne({_id: item, category: cat}, function(err, data){
            if ( !err )
                data.remove(), that.SendJson(res, data);
            else
                that.SendError(res, 'Invalid Query: ' + req.method + ' ' + req.url, 400);
        });
    },
    Admin: function(req, res, next)
    {
        console.log('ApiController.Admin');

        var response = JSON.stringify({});

        this.SendJson(res, response);
    },
    Default: function(req, res, next)
    {
        console.log('ApiController.Default', req.method, req.url);

        this.SendError(res, 'Invalid API: ' + req.method + ' ' + req.url, 501);
    },
    SendJson: function(res, content)
    {
        res.contentType('application/json').json(content);
        return this;
    },
    SendError: function(res, content, code)
    {
        res.send(content, code);
    },
    Route: function(app)
    {
        console.log('ApiController.Route');

        var that = this;

        // -- application
        app.get(    '/api/application',     function(req, res, next){ that.ApplicationGet.call(that, req, res, next); }            );

        // -- all categories in the garage (list)
        app.get(    '/api/categories',      function(req, res, next){ that.CategoriesGet.call(that, req, res, next); }             );
        app.get(    '/api/categories/:name/:description',
                                            function(req, res, next){ that.CategoriesCreateCategory.call(that, req, res, next); }  );
        app.put(    '/api/categories',      function(req, res, next){ that.CategoriesCreateCategory.call(that, req, res, next); }  );

        // -- single category (just info on the category)
        app.get(    '/api/category/:cat',   function(req, res, next){ that.CategoryGet.call(that, req, res, next); }               );
        app.put(    '/api/category/:cat',   function(req, res, next){ that.ItemsCreateItem.call(that, req, res, next); }           );
        app.post(   '/api/category/:cat',   function(req, res, next){ that.CategoryEdit.call(that, req, res, next); }              );
        app.delete( '/api/category/:cat',   function(req, res, next){ that.CategoryDelete.call(that, req, res, next); }            );

        // -- single item in category or filter (including 'all', and fuzzy queries)
        app.get(    '/api/items/:cat',      function(req, res, next){ that.ItemsGetItems.call(that, req, res, next); }             );
        app.get(    '/api/items/:cat/:id',  function(req, res, next){ that.ItemsGetItem.call(that, req, res, next); }              );
        app.put(    '/api/items/:cat',      function(req, res, next){ that.ItemsCreateItem.call(that, req, res, next); }           );
        app.post(   '/api/items/:cat/:id',  function(req, res, next){ that.ItemsEditItem.call(that, req, res, next); }             );
        app.delete( '/api/items/:cat/:id',  function(req, res, next){ that.ItemsDeleteItem.call(that, req, res, next); }           );

        // -- admin interface (with the same options)
        app.get(    '/api/admin/:obj/*',    function(req, res, next){ that.Admin.call(that, req, res, next); }                     );

        // -- Other
        app.all(    '/*',                   function(req, res, next){ that.Default.call(that, req, res, next); }                   );

        return this;
    }
};

})()

<commit_msg>user creation 10<commit_after>/**
    @file       app-controller.js
    @brief      Application controller and router.

    @author     Oleg Kertanov <okertanov@gmail.com>
    @date       May 2012
    @copyright  Copyright (C) 2012 Oleg Kertanov <okertanov@gmail.com>
    @license    BSD
    @see LICENSE file
*/

(function()
{

// Strict mode by default
"use strict";

var underscore = require('underscore')._,
    Db = require('./api-db.js'),
    ApiDb = Db.ApiDb,
    DbName = 'garage';

function IsEmptyObject(o)
{
    return Object.keys(o).length === 0;
}

exports.ApiController =
{
    Initialize: function()
    {
        console.log('ApiController.Initialize');

        ApiDb.Connect(DbName);
    },
    Terminate: function()
    {
        console.log('ApiController.Terminate');

        ApiDb.Disconnect();
    },
    ApplicationGet: function(req, res, next)
    {
        console.log('ApiController.ApplicationGet');

        var response = JSON.stringify({});

        this.SendJson(res, response);
    },
    CategoriesGet: function(req, res, next)
    {
        console.log('ApiController.CategoriesGet');

        var that = this;
        ApiDb.Category.find({}).asc('name').run(function(err, data){
            if ( !err )
                that.SendJson(res, data);
            else
                that.SendError(res, 'Invalid Query: ' + req.method + ' ' + req.url, 400);
        });
    },
    CategoriesCreateCategory: function(req, res, next)
    {
        console.log('ApiController.CategoriesCreateCategory');

        var that  = this,
            name  = req.params.name        || req.body.name,
            descr = req.params.description || req.body.description,
            cat   = new ApiDb.Category({name: name, description: descr, type: 'user'});

        cat.save(function(err){
            if ( !err )
                that.SendJson(res, cat);
            else
                that.SendError(res, 'Invalid Query: ' + req.method + ' ' + req.url, 400);
        });
    },
    CategoryGet: function(req, res, next)
    {
        console.log('ApiController.CategoryGet');

        var that = this,
            cat  = req.params.cat;

        ApiDb.Category.findOne({_id: cat}, function(err, data){
            if ( !err )
                that.SendJson(res, data);
            else
                that.SendError(res, 'Invalid Query: ' + req.method + ' ' + req.url, 400);
        });
    },
    CategoryEdit: function(req, res, next)
    {
        console.log('ApiController.CategoryEdit');

        var that  = this,
            cat   = req.params.cat,
            name  = req.body.name,
            descr = req.body.description,
            dirty = false;

        ApiDb.Category.findOne({_id: cat}, function(err, data){
            if ( !err )
            {
                if ( name && name.length )
                    data.name = name, dirty = true;

                if ( descr && descr.length )
                    data.description = descr, dirty = true;

                if ( dirty === true )
                {
                    data.save(function(err){
                        if ( !err )
                            that.SendJson(res, data);
                        else
                            that.SendError(res, 'Invalid Query: ' + req.method + ' ' + req.url, 400);
                    });
                }
                else
                    that.SendJson(res, data);
            }
            else
                that.SendError(res, 'Invalid Query: ' + req.method + ' ' + req.url, 400);
        });
    },
    CategoryDelete: function(req, res, next)
    {
        console.log('ApiController.CategoryDelete');

        var that = this,
            cat  = req.params.cat;

        ApiDb.Category.findOne({_id: cat}, function(err, data){
            if ( !err )
                data.remove(), that.SendJson(res, data);
            else
                that.SendError(res, 'Invalid Query: ' + req.method + ' ' + req.url, 400);
        });
    },
    ItemsGetItems: function(req, res, next)
    {
        var that = this,
            cat  = req.params.cat;

        ApiDb.Item.find({category: cat}).desc('date').run(function(err, data){
            if ( !err )
                that.SendJson(res, data);
            else
                that.SendError(res, 'Invalid Query: ' + req.method + ' ' + req.url, 400);
        });
    },
    ItemsGetItem: function(req, res, next)
    {
        console.log('ApiController.ItemsGet');

        var that = this,
            cat  = req.params.cat,
            item = req.params.id;

        ApiDb.Category.findOne({_id: item, category: cat}, function(err, data){
            if ( !err )
                that.SendJson(res, data);
            else
                that.SendError(res, 'Invalid Query: ' + req.method + ' ' + req.url, 400);
        });
    },
    ItemsCreateItem: function(req, res, next)
    {
        console.log('ApiController.ItemsCreateItem');

        var that   = this,
            name   = req.params.name        || req.body.name,
            descr  = req.params.description || req.body.description,
            user   = req.params.user        || req.body.user,
            cat    = req.params.category    || req.body.category;

        ApiDb.Category.findOne({_id: cat}, function(err, data){
            if ( !err )
            {
                console.dir(cat)
                var item = new ApiDb.Item({name: name,
                                           description: descr,
                                           category: data._id,
                                           images: []});
                item.save(function(err){
                    if ( !err )
                        that.SendJson(res, item);
                    else
                        that.SendError(res, 'Invalid Query: ' + req.method + ' ' + req.url + '\nError: ' + err, 400);
                });
            }
            else
                that.SendError(res, 'Invalid Query: ' + req.method + ' ' + req.url + '\nError: ' + err, 400);
        });
    },
    ItemsEditItem: function(req, res, next)
    {
        console.log('ApiController.ItemsEdit');
    },
    ItemsDeleteItem: function(req, res, next)
    {
        console.log('ApiController.ItemsDelete');

        var that = this,
            cat  = req.params.cat,
            item = req.params.id;

        ApiDb.Category.findOne({_id: item, category: cat}, function(err, data){
            if ( !err )
                data.remove(), that.SendJson(res, data);
            else
                that.SendError(res, 'Invalid Query: ' + req.method + ' ' + req.url, 400);
        });
    },
    Admin: function(req, res, next)
    {
        console.log('ApiController.Admin');

        var response = JSON.stringify({});

        this.SendJson(res, response);
    },
    Default: function(req, res, next)
    {
        console.log('ApiController.Default', req.method, req.url);

        this.SendError(res, 'Invalid API: ' + req.method + ' ' + req.url, 501);
    },
    SendJson: function(res, content)
    {
        res.contentType('application/json').json(content);
        return this;
    },
    SendError: function(res, content, code)
    {
        res.send(content, code);
    },
    Route: function(app)
    {
        console.log('ApiController.Route');

        var that = this;

        // -- application
        app.get(    '/api/application',     function(req, res, next){ that.ApplicationGet.call(that, req, res, next); }            );

        // -- all categories in the garage (list)
        app.get(    '/api/categories',      function(req, res, next){ that.CategoriesGet.call(that, req, res, next); }             );
        app.get(    '/api/categories/:name/:description',
                                            function(req, res, next){ that.CategoriesCreateCategory.call(that, req, res, next); }  );
        app.put(    '/api/categories',      function(req, res, next){ that.CategoriesCreateCategory.call(that, req, res, next); }  );

        // -- single category (just info on the category)
        app.get(    '/api/category/:cat',   function(req, res, next){ that.CategoryGet.call(that, req, res, next); }               );
        app.put(    '/api/category/:cat',   function(req, res, next){ that.ItemsCreateItem.call(that, req, res, next); }           );
        app.post(   '/api/category/:cat',   function(req, res, next){ that.CategoryEdit.call(that, req, res, next); }              );
        app.delete( '/api/category/:cat',   function(req, res, next){ that.CategoryDelete.call(that, req, res, next); }            );

        // -- single item in category or filter (including 'all', and fuzzy queries)
        app.get(    '/api/items/:cat',      function(req, res, next){ that.ItemsGetItems.call(that, req, res, next); }             );
        app.get(    '/api/items/:cat/:id',  function(req, res, next){ that.ItemsGetItem.call(that, req, res, next); }              );
        app.put(    '/api/items/:cat',      function(req, res, next){ that.ItemsCreateItem.call(that, req, res, next); }           );
        app.post(   '/api/items/:cat/:id',  function(req, res, next){ that.ItemsEditItem.call(that, req, res, next); }             );
        app.delete( '/api/items/:cat/:id',  function(req, res, next){ that.ItemsDeleteItem.call(that, req, res, next); }           );

        // -- admin interface (with the same options)
        app.get(    '/api/admin/:obj/*',    function(req, res, next){ that.Admin.call(that, req, res, next); }                     );

        // -- Other
        app.all(    '/*',                   function(req, res, next){ that.Default.call(that, req, res, next); }                   );

        return this;
    }
};

})()

<|endoftext|>


<commit_before><commit_msg>Enabling debug info by default (this should only be turned off in production).<commit_after><|endoftext|>


<commit_before>
import { isFunction, isArray, isString, isPlainObject, trim } from 'lodash'
import { Literal, SubQuery, Column, Table, Escaped } from '../expression'
import { Select } from '../query'

/**
 * A handler function for template strings
 *
 * @param {String|Array} expr
 * @param {Array} args
 * @returns {Literal}
 */
export function raw(expr, ...args) {
  // handle template strings
  if ( isArray(expr) )
    expr = expr.join('?')

  // parse named parameters
  if ( isPlainObject(args[0]) ) {
    let obj = args[0]
    args = []

    expr = expr.replace(/[:@](\w+)/gi, (match, attr) => {
      // we return the match string in case the attribute is undefined
      if ( isUndefined(obj[attr]) ) return match

      // append the value of the attribute
      args.push(obj[attr])

      // return the basic placeholder
      return '?'
    })
  }

  return new Literal(expr, args)
}

/**
 *
 * @param {Any} query
 * @returns {Expression}
 * @throws {TypeError}
 */
export function sq(query) {
  // accept a raw query string
  if ( isString(query) )
    return Literal.from(query).wrap()

  // accept a function as a parameter
  if ( isFunction(query) ) {
    let fn = query

    fn(query = new Select())
  }

  // accept a Select query instance
  if ( query instanceof Select )
    query = new SubQuery(query)

  // and finally an instance of SubQuery expression
  if ( query instanceof SubQuery ) return query

  // throws an error for invalid argument
  throw new TypeError("Invalid sub query expression")
}

/**
 *
 * @param {String} value
 * @returns {Column}
 */
export function column(value) {
  var alias = ''

  if ( value.indexOf(' as ') > 0 ) {
    [value, alias] = value.split(' as ').map(trim)
  }

  return new Column(value).as(alias)
}

/**
 *
 * @param {String} value
 * @returns {Table}
 */
export function table(value) {
  var alias = ''

  if ( value.indexOf(' as ') > 0 ) {
    [value, alias] = value.split(' as ').map(trim)
  }

  return new Table(value).as(alias)
}

/**
 *
 * @param {Any} value
 * @returns {Escaped}
 */
export function esc(value) {
  return new Escaped(value)
}

/**
 *
 * @param {Any} value
 * @param {String} type
 * @returns {Expression}
 */
export function cast(value, type) {
  return raw(`cast(? as ${type})`, value)
}<commit_msg>Add missing import<commit_after>
import { isFunction, isArray, isString, isPlainObject, isUndefined, trim } from 'lodash'
import { Literal, SubQuery, Column, Table, Escaped } from '../expression'
import { Select } from '../query'

/**
 * A handler function for template strings
 *
 * @param {String|Array} expr
 * @param {Array} args
 * @returns {Literal}
 */
export function raw(expr, ...args) {
  // handle template strings
  if ( isArray(expr) )
    expr = expr.join('?')

  // parse named parameters
  if ( isPlainObject(args[0]) ) {
    let obj = args[0]
    args = []

    expr = expr.replace(/[:@](\w+)/gi, (match, attr) => {
      // we return the match string in case the attribute is undefined
      if ( isUndefined(obj[attr]) ) return match

      // append the value of the attribute
      args.push(obj[attr])

      // return the basic placeholder
      return '?'
    })
  }

  return new Literal(expr, args)
}

/**
 *
 * @param {Any} query
 * @returns {Expression}
 * @throws {TypeError}
 */
export function sq(query) {
  // accept a raw query string
  if ( isString(query) )
    return Literal.from(query).wrap()

  // accept a function as a parameter
  if ( isFunction(query) ) {
    let fn = query

    fn(query = new Select())
  }

  // accept a Select query instance
  if ( query instanceof Select )
    query = new SubQuery(query)

  // and finally an instance of SubQuery expression
  if ( query instanceof SubQuery ) return query

  // throws an error for invalid argument
  throw new TypeError("Invalid sub query expression")
}

/**
 *
 * @param {String} value
 * @returns {Column}
 */
export function column(value) {
  var alias = ''

  if ( value.indexOf(' as ') > 0 ) {
    [value, alias] = value.split(' as ').map(trim)
  }

  return new Column(value).as(alias)
}

/**
 *
 * @param {String} value
 * @returns {Table}
 */
export function table(value) {
  var alias = ''

  if ( value.indexOf(' as ') > 0 ) {
    [value, alias] = value.split(' as ').map(trim)
  }

  return new Table(value).as(alias)
}

/**
 *
 * @param {Any} value
 * @returns {Escaped}
 */
export function esc(value) {
  return new Escaped(value)
}

/**
 *
 * @param {Any} value
 * @param {String} type
 * @returns {Expression}
 */
export function cast(value, type) {
  return raw(`cast(? as ${type})`, value)
}<|endoftext|>


<commit_before>(function(){
  'use strict';
  function CommitStreamAdminBoot(el) {
    var persistentOptions = {
      headers: { Bearer: '' }
    };

    var app = angular.module('commitStreamAdmin', ['commitStreamAdmin.config',
      'angular-hal', 'ngRoute', 'ui.bootstrap','cgPrompt']);
    app.config(function($sceProvider) {
      $sceProvider.enabled(false);
    });

    app.factory('CommitStreamApi', ['serviceUrl', 'halClient', function(serviceUrl, halClient) {
      return {
        'load' : function() {
          return halClient.$get(serviceUrl + '/api/public', persistentOptions);
        },
      };
    }]);

    app.config(['serviceUrlProvider', '$routeProvider', function(serviceUrlProvider, $routeProvider) {
      var serviceUrl = serviceUrlProvider.$get();
      $routeProvider.when('/', {templateUrl: serviceUrl + '/partials/instances.html', controller: 'InstancesController'});
      $routeProvider.when('/inboxes', {templateUrl: serviceUrl + '/partials/inboxes.html', controller: 'InboxesController'});
      $routeProvider.otherwise({redirectTo: serviceUrl + '/'});
    }]);

    app.directive('resizableOverlay', function($window) {
      return function($scope) {
        return angular.element($window).bind('resize', function() {
          $scope.adjustOverlay();
          return $scope.$apply();
        });
      };
    });

    app.directive('toggleCheckbox', function($timeout) {
      /**
       * Directive
       */
      return {
        restrict: 'A',
        transclude: true,
        replace: false,
        require: 'ngModel',
        link: function ($scope, $element, $attr, ngModel) {
          // update model from Element
          var updateModelFromElement = function() {
            // If modified
            var checked = $element.prop('checked');
            if (checked != ngModel.$viewValue) {
              // Update ngModel
              ngModel.$setViewValue(checked);
              $scope.$apply();
            }
          };

          // Update input from Model
          var updateElementFromModel = function(newValue) {
            $element.trigger('change');
          };

          // Observe: Element changes affect Model
          $element.on('change', function() {
            updateModelFromElement();
          });

          $scope.$watch(function() {
            return ngModel.$viewValue;
          }, function(newValue) {
            updateElementFromModel(newValue);
          }, true);

          // Initialise BootstrapToggle
          $element.bootstrapToggle();
        }
      };
    });

    app.controller('InstancesController',
      ['$rootScope', '$scope', '$http', '$location', 'CommitStreamApi', 'serviceUrl', 'configGetUrl', 'configSaveUrl',
      function($rootScope, $scope, $http, $location, CommitStreamApi, serviceUrl, configGetUrl, configSaveUrl) {
        var config;

        $scope.loaderUrl = serviceUrl + '/ajax-loader.gif';

        var loading = true;

        $scope.loading = function() {
          return loading;
        };

        var errorHandler = function(error) {
          loading = false;
          if (error.data && error.data.errors && error.data.errors.length) {
            $scope.error.value = error.data.errors[0];
          } else {
            $scope.error.value = 'There was an unexpected error when processing your request.';
          }
        };

        $scope.error = { value: ''};

        $scope.errorActive = function() {
          return $scope.error.value !== '';
        };

        CommitStreamApi
        .load()
        .then(function(resources) {
          $rootScope.resources = resources;
          if (!configGetUrl) return {
            data: {
              serviceUrl: serviceUrl,
              instanceId: '',
              apiKey: '',
              globalDigestId: '',
              configured: false,
              enabled: false
            }
          }
          return $http.get(configGetUrl);
        })
        .then(function(configRes) {
          // TODO handle null case?
          config = configRes.data;
          $rootScope.config = config;

          if (config.configured) {
            persistentOptions.headers.Bearer = config.apiKey;
            return $rootScope.resources.$get('instance', {instanceId: config.instanceId});
          } else {
            return $rootScope.resources.$post('instances');
          }
        })
        .then(function(instance) {
          persistentOptions.headers.Bearer = instance.apiKey; // Ensure apiKey for NEW instance
          $rootScope.instance = instance;

          if (config.configured) {
            return $rootScope.resources.$get('digest', {
              instanceId: config.instanceId,
              digestId: config.globalDigestId
            });
          }
          else {
            return instance.$post('digest-create', {}, {
              description: 'Global Repositories List'
            });
          }
        })
        .then(function(digest) {
          $rootScope.digest = digest;
          $location.path('/inboxes');
        })
        .catch(errorHandler);
      }]
    );

    app.controller('InboxesController', ['$rootScope', '$scope', '$timeout', 'serviceUrl',
      'configSaveUrl', '$http', '$q', 'prompt', '$location',
      function($rootScope, $scope, $timeout, serviceUrl,
        configSaveUrl, $http, $q, prompt, $location) {

      $scope.inboxesVisible = function() {
        // Only display when we actually have the config in $scope!
        return $rootScope.config;
      };

      if (!$rootScope.config) {
        $location.path('/');
        return;
      }

      $scope.newInbox = {
        url: '',
        name: '',
        family: 'GitHub' // Until we support other systems, hard-code this.
      };

      $scope.serviceUrl = serviceUrl;
      $scope.inboxes = [];

      $scope.enabledState = {
        enabled: $rootScope.config.enabled,
        applying: false,
        onText: 'Enabled',
        offText: 'Disabled'
      };

      $scope.message = { value: ''};

      $scope.messageActive = function() {
        return $scope.message.value !== '';
      };

      $scope.error = { value: ''};

      $scope.errorActive = function() {
        return $scope.error.value !== '';
      };

      // NOTE: this is a bit of a hack to remove errors upon network request to clear
      // out the UI prompts
      persistentOptions.transformUrl = function(url) {
        $scope.error.value = '';
        return url;
      };

      $scope.urlPattern = /^https?\:\/\/.{1,}\/.{1,}$/;

      $scope.inboxName = function() {
        if (!$scope.newInbox.url || $scope.newInbox.url.length < 1) return '...';
        var index = $scope.newInbox.url.lastIndexOf('/');
        if (index < 0) return '...';
        if (index === $scope.newInbox.url.length - 1) return '...';
        return $scope.newInbox.url.substr(index + 1);
      };

      var errorHandler = function(error) {
        if (error.data && error.data.errors && error.data.errors.length) {
          $scope.error.value = error.data.errors[0];
        } else {
          $scope.error.value = 'There was an unexpected error when processing your request.';
        }
      };

      var configSave = function(enabled) {
        $rootScope.config.enabled = enabled;

        if (!$rootScope.config.configured) {
          $rootScope.config.instanceId = $rootScope.instance.instanceId;
          $rootScope.config.globalDigestId = $rootScope.digest.digestId;
          $rootScope.config.apiKey = $rootScope.instance.apiKey;
          $rootScope.config.configured = true;
        }
        if (configSaveUrl) return $http.post(configSaveUrl, $rootScope.config);
        return $q.when(true);
      };

      var inboxConfigure = function(inbox) {
        var links = inbox.$links();
        inbox.addCommit = links['add-commit'].href + '?apiKey=' + persistentOptions.headers.Bearer;
        inbox.removeHref = links['self'].href + '?apiKey=' + persistentOptions.headers.Bearer;
      };

      var inboxesGet = function() {
        return $rootScope.digest.$get('inboxes')
          .then(function(inboxesRes) {
            return inboxesRes.$get('inboxes');
          }).then(function(inboxes) {
            $scope.inboxes.length = 0;
            inboxes.forEach(function(inbox) {
              inboxConfigure(inbox);
              $scope.inboxes.unshift(inbox);
            });
          })
          .catch(errorHandler);
      };

      var inboxesUpdate = function(enabled) {
        if (enabled) {
          $('.inbox-url').select().focus();
        }
        inboxesGet();
      }

      inboxesUpdate($rootScope.config.enabled);

      $scope.enabledChanged = function() {
        $scope.newInbox.url = '';

        var toggle = $('.commitstream-admin .enabled');

        var enabled = toggle.prop('checked');

        $scope.enabledState.applying = true;
        toggle.bootstrapToggle('disable');

        configSave(enabled).then(function(configSaveResult) {
          // TODO need to handle configSaveResult?
          inboxesUpdate(enabled);
        })
        .catch(errorHandler)
        .finally(function() {
          $scope.enabledState.applying = false;
          toggle.bootstrapToggle('enable');
        });
      };

      $scope.applying = function() {
        return $scope.enabledState.applying;
      };

      $scope.overlayVisible = function() {
        return $scope.enabledState.applying ||!$rootScope.config.enabled;
      };

      $scope.adjustOverlay = function() {
        var repolistWidth = $('.repos-list').width();
        var repolistHeight = $('.repos-list').height();
        $('.repos-section .overlay').height(repolistHeight);
        $('.repos-section .overlay').width(repolistWidth);
      };

      // A jQuery based way of handling resizing of the window to adjust the overlay
      // when CS is disabled.
      // $(window).resize(function(){
      //   $scope.adjustOverlay();
      // });

      $scope.inboxCreate = function() {
        var index = $scope.newInbox.url.lastIndexOf('/');
        $scope.newInbox.name = $scope.newInbox.url.substr(index + 1);

        $rootScope.digest.$post('inbox-create', {}, $scope.newInbox)
        .then(function(inbox) {
          inboxConfigure(inbox);
          $scope.inboxes.unshift(inbox);
          $scope.newInbox.url = '';
          $scope.inboxHighlightTop(inbox.removeHref);
        })
        .catch(errorHandler);
      };

      $scope.inboxRemove = function(inbox) {
        prompt({
          title: 'Remove Repository?',
          message: 'Are you sure you want to remove the repository ' + inbox.name + '?',
          buttons: [
            { label:'Remove',
              primary: true,
              class: 'action-remove'
            },
            {
              label:'Cancel',
              cancel: true,
              class: 'action-cancel'
            }
          ]
        }).then(function() {
          inbox.$del('self').then(function(result) {
            $scope.message.value = 'Successfully removed repository';
            var index = $scope.inboxes.indexOf(inbox);
            $scope.inboxes.splice(index, 1);
            $timeout(function() {
              $('.commitstream-admin .message').fadeOut('slow', function() {
                $scope.message.value = '';
              });
            }, 4000);
          })
          .catch(errorHandler);
        });
      };

      var inboxHighlight = function(el) {
        if ($rootScope.config.enabled) {
          el.focus();
          el.select();
        }
      };

      $scope.inboxHighlight = function(evt) {
        inboxHighlight(evt.currentTarget);
      };

      $scope.inboxHighlightTop = function() {
        $timeout(function() {
          var el = $($('.commitstream-admin .inbox')[0]);
          inboxHighlight(el);
        }, 0);
      };

    }]);

    angular.bootstrap(el, ['commitStreamAdmin']);
  };
  window.CommitStreamAdminBoot = CommitStreamAdminBoot;
}());<commit_msg>S-52318 WIP Create new instance inside of configSave if-and-only-if enabled is clicked for the first time.<commit_after>(function(){
  'use strict';
  function CommitStreamAdminBoot(el) {
    var persistentOptions = {
      headers: { Bearer: '' }
    };

    var app = angular.module('commitStreamAdmin', ['commitStreamAdmin.config',
      'angular-hal', 'ngRoute', 'ui.bootstrap','cgPrompt']);
    app.config(function($sceProvider) {
      $sceProvider.enabled(false);
    });

    app.factory('CommitStreamApi', ['serviceUrl', 'halClient', function(serviceUrl, halClient) {
      return {
        'load' : function() {
          return halClient.$get(serviceUrl + '/api/public', persistentOptions);
        },
      };
    }]);

    app.config(['serviceUrlProvider', '$routeProvider', function(serviceUrlProvider, $routeProvider) {
      var serviceUrl = serviceUrlProvider.$get();
      $routeProvider.when('/', {templateUrl: serviceUrl + '/partials/instances.html', controller: 'InstancesController'});
      $routeProvider.when('/inboxes', {templateUrl: serviceUrl + '/partials/inboxes.html', controller: 'InboxesController'});
      $routeProvider.otherwise({redirectTo: serviceUrl + '/'});
    }]);

    app.directive('resizableOverlay', function($window) {
      return function($scope) {
        return angular.element($window).bind('resize', function() {
          $scope.adjustOverlay();
          return $scope.$apply();
        });
      };
    });

    app.directive('toggleCheckbox', function($timeout) {
      /**
       * Directive
       */
      return {
        restrict: 'A',
        transclude: true,
        replace: false,
        require: 'ngModel',
        link: function ($scope, $element, $attr, ngModel) {
          // update model from Element
          var updateModelFromElement = function() {
            // If modified
            var checked = $element.prop('checked');
            if (checked != ngModel.$viewValue) {
              // Update ngModel
              ngModel.$setViewValue(checked);
              $scope.$apply();
            }
          };

          // Update input from Model
          var updateElementFromModel = function(newValue) {
            $element.trigger('change');
          };

          // Observe: Element changes affect Model
          $element.on('change', function() {
            updateModelFromElement();
          });

          $scope.$watch(function() {
            return ngModel.$viewValue;
          }, function(newValue) {
            updateElementFromModel(newValue);
          }, true);

          // Initialise BootstrapToggle
          $element.bootstrapToggle();
        }
      };
    });

    app.controller('InstancesController',
      ['$rootScope', '$scope', '$http', '$location', 'CommitStreamApi', 'serviceUrl', 'configGetUrl', 'configSaveUrl',
      function($rootScope, $scope, $http, $location, CommitStreamApi, serviceUrl, configGetUrl, configSaveUrl) {
        var config;

        $scope.loaderUrl = serviceUrl + '/ajax-loader.gif';

        var loading = true;

        $scope.loading = function() {
          return loading;
        };

        var errorHandler = function(error) {
          loading = false;
          if (error.data && error.data.errors && error.data.errors.length) {
            $scope.error.value = error.data.errors[0];
          } else {
            $scope.error.value = 'There was an unexpected error when processing your request.';
          }
        };

        $scope.error = { value: ''};

        $scope.errorActive = function() {
          return $scope.error.value !== '';
        };

        CommitStreamApi
        .load()
        .then(function(resources) {
          $rootScope.resources = resources;
          if (!configGetUrl) return {
            data: {
              serviceUrl: serviceUrl,
              instanceId: '',
              apiKey: '',
              globalDigestId: '',
              configured: false,
              enabled: false
            }
          }
          return $http.get(configGetUrl);
        })
        .then(function(configRes) {
          // TODO handle null case?
          config = configRes.data;
          $rootScope.config = config;

          if (config.configured) {
            persistentOptions.headers.Bearer = config.apiKey;
            return $rootScope.resources.$get('instance', {instanceId: config.instanceId});
          } else {
            // return $rootScope.resources.$post('instances');
            return false;
          }
        })
        .then(function(instance) {
          if (instance) {
            persistentOptions.headers.Bearer = instance.apiKey; // Ensure apiKey for NEW instance
            $rootScope.instance = instance;
            if (config.configured) {
              return $rootScope.resources.$get('digest', {
                instanceId: config.instanceId,
                digestId: config.globalDigestId
              });
            } else {
              return instance.$post('digest-create', {}, {
                description: 'Global Repositories List'
              });
            }
          } else {
            return false;
          }
        })
        .then(function(digest) {
          if (digest) {
            $rootScope.digest = digest;
          }
          $location.path('/inboxes');
        })
        .catch(errorHandler);
      }]
    );

    app.controller('InboxesController', ['$rootScope', '$scope', '$timeout', 'serviceUrl',
      'configSaveUrl', '$http', '$q', 'prompt', '$location',
      function($rootScope, $scope, $timeout, serviceUrl,
        configSaveUrl, $http, $q, prompt, $location) {

      $scope.inboxesVisible = function() {
        // Only display when we actually have the config in $scope!
        return $rootScope.config;
      };

      if (!$rootScope.config) {
        $location.path('/');
        return;
      }

      $scope.newInbox = {
        url: '',
        name: '',
        family: 'GitHub' // Until we support other systems, hard-code this.
      };

      $scope.serviceUrl = serviceUrl;
      $scope.inboxes = [];

      $scope.enabledState = {
        enabled: $rootScope.config.enabled,
        applying: false,
        onText: 'Enabled',
        offText: 'Disabled'
      };

      $scope.message = { value: ''};

      $scope.messageActive = function() {
        return $scope.message.value !== '';
      };

      $scope.error = { value: ''};

      $scope.errorActive = function() {
        return $scope.error.value !== '';
      };

      // NOTE: this is a bit of a hack to remove errors upon network request to clear
      // out the UI prompts
      persistentOptions.transformUrl = function(url) {
        $scope.error.value = '';
        return url;
      };

      $scope.urlPattern = /^https?\:\/\/.{1,}\/.{1,}$/;

      $scope.inboxName = function() {
        if (!$scope.newInbox.url || $scope.newInbox.url.length < 1) return '...';
        var index = $scope.newInbox.url.lastIndexOf('/');
        if (index < 0) return '...';
        if (index === $scope.newInbox.url.length - 1) return '...';
        return $scope.newInbox.url.substr(index + 1);
      };

      var errorHandler = function(error) {
        if (error.data && error.data.errors && error.data.errors.length) {
          $scope.error.value = error.data.errors[0];
        } else {
          $scope.error.value = 'There was an unexpected error when processing your request.';
        }
      };

      var configSave = function(enabled) {
        $rootScope.config.enabled = enabled;

        if (!$rootScope.config.configured) {
          return $rootScope.resources.$post('instances')
          .then(function(instance) {
            persistentOptions.headers.Bearer = instance.apiKey; // Ensure apiKey for NEW instance
            $rootScope.instance = instance;
            return instance.$post('digest-create', {}, {
              description: 'Global Repositories List'
            });
          })
          .then(function(digest) {
            $rootScope.digest = digest;
            $rootScope.config.instanceId = $rootScope.instance.instanceId;
            $rootScope.config.globalDigestId = digest.digestId;
            $rootScope.config.apiKey = $rootScope.instance.apiKey;
            $rootScope.config.configured = true;
            if (configSaveUrl) return $http.post(configSaveUrl, $rootScope.config);
            return $q.when(true);
          });
        } else {
          if (configSaveUrl) return $http.post(configSaveUrl, $rootScope.config);
          return $q.when(true);
        }
      };

      var inboxConfigure = function(inbox) {
        var links = inbox.$links();
        inbox.addCommit = links['add-commit'].href + '?apiKey=' + persistentOptions.headers.Bearer;
        inbox.removeHref = links['self'].href + '?apiKey=' + persistentOptions.headers.Bearer;
      };

      var inboxesGet = function() {
        return $rootScope.digest.$get('inboxes')
          .then(function(inboxesRes) {
            return inboxesRes.$get('inboxes');
          }).then(function(inboxes) {
            $scope.inboxes.length = 0;
            inboxes.forEach(function(inbox) {
              inboxConfigure(inbox);
              $scope.inboxes.unshift(inbox);
            });
          })
          .catch(errorHandler);
      };

      var inboxesUpdate = function(enabled) {
        if (enabled) {
          $('.inbox-url').select().focus();
        }
        inboxesGet();
      }

      //inboxesUpdate($rootScope.config.enabled);

      $scope.enabledChanged = function() {
        $scope.newInbox.url = '';

        var toggle = $('.commitstream-admin .enabled');

        var enabled = toggle.prop('checked');

        $scope.enabledState.applying = true;
        toggle.bootstrapToggle('disable');

        configSave(enabled).then(function(configSaveResult) {
          // TODO need to handle configSaveResult?
          inboxesUpdate(enabled);
        })
        .catch(errorHandler)
        .finally(function() {
          $scope.enabledState.applying = false;
          toggle.bootstrapToggle('enable');
        });
      };

      $scope.applying = function() {
        return $scope.enabledState.applying;
      };

      $scope.overlayVisible = function() {
        return $scope.enabledState.applying ||!$rootScope.config.enabled;
      };

      $scope.adjustOverlay = function() {
        var repolistWidth = $('.repos-list').width();
        var repolistHeight = $('.repos-list').height();
        $('.repos-section .overlay').height(repolistHeight);
        $('.repos-section .overlay').width(repolistWidth);
      };

      // A jQuery based way of handling resizing of the window to adjust the overlay
      // when CS is disabled.
      // $(window).resize(function(){
      //   $scope.adjustOverlay();
      // });

      $scope.inboxCreate = function() {
        var index = $scope.newInbox.url.lastIndexOf('/');
        $scope.newInbox.name = $scope.newInbox.url.substr(index + 1);

        $rootScope.digest.$post('inbox-create', {}, $scope.newInbox)
        .then(function(inbox) {
          inboxConfigure(inbox);
          $scope.inboxes.unshift(inbox);
          $scope.newInbox.url = '';
          $scope.inboxHighlightTop(inbox.removeHref);
        })
        .catch(errorHandler);
      };

      $scope.inboxRemove = function(inbox) {
        prompt({
          title: 'Remove Repository?',
          message: 'Are you sure you want to remove the repository ' + inbox.name + '?',
          buttons: [
            { label:'Remove',
              primary: true,
              class: 'action-remove'
            },
            {
              label:'Cancel',
              cancel: true,
              class: 'action-cancel'
            }
          ]
        }).then(function() {
          inbox.$del('self').then(function(result) {
            $scope.message.value = 'Successfully removed repository';
            var index = $scope.inboxes.indexOf(inbox);
            $scope.inboxes.splice(index, 1);
            $timeout(function() {
              $('.commitstream-admin .message').fadeOut('slow', function() {
                $scope.message.value = '';
              });
            }, 4000);
          })
          .catch(errorHandler);
        });
      };

      var inboxHighlight = function(el) {
        if ($rootScope.config.enabled) {
          el.focus();
          el.select();
        }
      };

      $scope.inboxHighlight = function(evt) {
        inboxHighlight(evt.currentTarget);
      };

      $scope.inboxHighlightTop = function() {
        $timeout(function() {
          var el = $($('.commitstream-admin .inbox')[0]);
          inboxHighlight(el);
        }, 0);
      };

    }]);

    angular.bootstrap(el, ['commitStreamAdmin']);
  };
  window.CommitStreamAdminBoot = CommitStreamAdminBoot;
}());<|endoftext|>


<commit_before><commit_msg>extjs_load: fix path for App && loading of backend classes for `nw`<commit_after><|endoftext|>


<commit_before>
import { Literal, SubQuery, Column, Table, Escaped } from '../expression'
import { isFunction, isArray, isString, trim } from 'lodash'
import { Select } from '../query'

/**
 * A handler function for template strings
 *
 * @param {String|Array} expr
 * @param {Array} args
 * @returns {Literal}
 */
export function raw(expr, ...args) {
  // handle template strings
  if ( isArray(expr) )
    expr = expr.join('?')

  return new Literal(expr, args)
}

/**
 *
 * @param {Any} query
 * @returns {Expression}
 * @throws {TypeError}
 */
export function sq(query) {
  // accept a raw query string
  if ( isString(query) )
    return Literal.from(query).wrap()

  // accept a function as a parameter
  if ( isFunction(query) ) {
    let fn = query

    fn(query = new Select())
  }

  // accept a Select query instance
  if ( query instanceof Select )
    query = new SubQuery(query)

  // and finally an instance of SubQuery expression
  if ( query instanceof SubQuery ) return query

  // throws an error for invalid argument
  throw new TypeError("Invalid sub query expression")
}

/**
 *
 * @param {String} value
 * @returns {Column}
 */
export function column(value) {
  var alias = ''

  if ( value.indexOf(' as ') > 0 ) {
    [value, alias] = value.split(' as ').map(trim)
  }

  return new Column(value).as(alias)
}

/**
 *
 * @param {String} value
 * @returns {Table}
 */
export function table(value) {
  var alias = ''

  if ( value.indexOf(' as ') > 0 ) {
    [value, alias] = value.split(' as ').map(trim)
  }

  return new Table(value).as(alias)
}

/**
 *
 * @param {Any} value
 * @returns {Escaped}
 */
export function esc(value) {
  return new Escaped(value)
}

/**
 *
 * @param {Any} value
 * @param {String} type
 * @returns {Expression}
 */
export function cast(value, type) {
  return raw(`cast(? as ${type})`, value)
}<commit_msg>Fix raw helper<commit_after>
import { isFunction, isArray, isString, isPlainObject, trim } from 'lodash'
import { Literal, SubQuery, Column, Table, Escaped } from '../expression'
import { Select } from '../query'

/**
 * A handler function for template strings
 *
 * @param {String|Array} expr
 * @param {Array} args
 * @returns {Literal}
 */
export function raw(expr, ...args) {
  // handle template strings
  if ( isArray(expr) )
    expr = expr.join('?')

  // pass a plain object instead of the arguments array,
  // when the named parameters are used
  return new Literal(expr, isPlainObject(args[0]) ? args[0] : args)
}

/**
 *
 * @param {Any} query
 * @returns {Expression}
 * @throws {TypeError}
 */
export function sq(query) {
  // accept a raw query string
  if ( isString(query) )
    return Literal.from(query).wrap()

  // accept a function as a parameter
  if ( isFunction(query) ) {
    let fn = query

    fn(query = new Select())
  }

  // accept a Select query instance
  if ( query instanceof Select )
    query = new SubQuery(query)

  // and finally an instance of SubQuery expression
  if ( query instanceof SubQuery ) return query

  // throws an error for invalid argument
  throw new TypeError("Invalid sub query expression")
}

/**
 *
 * @param {String} value
 * @returns {Column}
 */
export function column(value) {
  var alias = ''

  if ( value.indexOf(' as ') > 0 ) {
    [value, alias] = value.split(' as ').map(trim)
  }

  return new Column(value).as(alias)
}

/**
 *
 * @param {String} value
 * @returns {Table}
 */
export function table(value) {
  var alias = ''

  if ( value.indexOf(' as ') > 0 ) {
    [value, alias] = value.split(' as ').map(trim)
  }

  return new Table(value).as(alias)
}

/**
 *
 * @param {Any} value
 * @returns {Escaped}
 */
export function esc(value) {
  return new Escaped(value)
}

/**
 *
 * @param {Any} value
 * @param {String} type
 * @returns {Expression}
 */
export function cast(value, type) {
  return raw(`cast(? as ${type})`, value)
}<|endoftext|>


<commit_before><commit_msg>Typos for small version<commit_after><|endoftext|>


<commit_before>import React from "react";
import User from '../components/User';

import UserService from '../components/UserService';
import BookService from '../components/BookService';

export class Home extends React.Component {

    constructor(){
        super();
        this.state = {
            users: [],
            books: [],
            searchValue: '',
            searchType: 'name',
            activeSort: {
                direction: 'desc',
                column: ''
            }
        };
        this.addNewUser = this.addNewUser.bind(this);
        this.removeUser = this.removeUser.bind(this);
        this.updateUser = this.updateUser.bind(this);

    }

    /* Capitalize first letter */
    capitalize(str) {
        return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
    }

    /*  */
    componentDidMount(){
        UserService.getAllUsers().then(response => {
            this.setState({
                users: response.data
            });
        });

        BookService.getAllBooks().then(response => {
            this.setState({
                books: response.data
            });
        });
    }

    /* ADD USER BUTTON */
    addNewUser() {
        let newUser = {
            name: this.capitalize(this.state.newUserName),
            lastName: this.capitalize(this.state.newLastName),
            age: this.state.newUserAge
        };
        UserService.addNewUser(newUser).then(response => {
            const addedUser = response.data;
            this.setState(state => {
                state.users.push(addedUser)
            });
        });
    }

    /* ADD INPUT USER (for all inputs) */
    addInput(inputName, e){
        let stateObj = {};
        stateObj[inputName] = e.target.value;
        this.setState(stateObj);
    }

    /* REMOVE */
    removeUser(userId){
        let message = confirm("If you delete user, you also delete user book(s)!");
        if (message == true) {
            let newStateUser = this.state.users.filter((user) => {
                return user.id != userId;
            });

            this.state.books.forEach((book) => {
                if(book.userId == userId){
                    BookService.deleteBook(book.id);
                }
            });

            UserService.deleteUser(userId);
            /* updateovan state posle remove-a korisnika */
            this.setState({
                users: newStateUser
            });
        }
    }

    updateUser(updatedData){
        this.setState(state => {
            for(let i = 0; i < state.users.length; i++){
                if(state.users[i].id === updatedData.id){
                    state.users[i] = updatedData;
                }
            }
        });
    }

    /* Sorting */
    sorting(thing){
        let dir;
        if(thing == this.state.activeSort.column) {
            dir = this.state.activeSort.direction == 'asc' ? 'desc' : 'asc';
        }
        let sorts = this.state.users.sort((a,b) => {
            if(dir == 'asc') {
                return a[thing]  b[thing]
            }
        });

        this.setState({
            users: sorts,
            activeSort: {
                column: thing,
                direction: dir
            }
        })
    }

    /* Sort by age */
    sortByAge(){
        let dir;
        if('writerAge' == this.state.activeSort.column) {
            dir = this.state.activeSort.direction == 'asc' ? 'desc' : 'asc';
        }

        let getSortYear = this.state.users.sort((a, b) => {
            if(dir == 'asc') {
                return a.age + b.age
            } else {
                return a.age - b.age
            }
        });

        this.setState({
            users: getSortYear,
            activeSort: {
                column: 'writerAge',
                direction: dir
            }
        })
    }

        /* input search */
    updateSearch(e){
        this.setState({
            searchValue: e.target.value
        })
    }

    updateSelectSearch(e) {
        this.setState({
            searchType: e.target.value
        })
    }

    render() {
        const filteredUsers = this.state.users.filter((user) => {
            return user[this.state.searchType].toLowerCase().indexOf(this.state.searchValue.toLowerCase()) !== -1;
        });

        let userList = filteredUsers.map((user) => {
            return (
                <User
                    key={user.id}
                    name={user.name}
                    lastName={user.lastName}
                    age={user.age}
                    id={user.id}
                    removeUser={this.removeUser}
                    updateUser={this.updateUser}
                />
            )
        });

        return (

Description

                            This web application is used for adding a writer in list of users/writers,
                            where you can editing existing writer, remove him or adding books for specific writer.

                            Also, you can sort writers and books or searching them by typing the first few letter.

 Add new writer:

                        First name

                            <input
                                onChange={this.addInput.bind(this, 'newUserName')}
                                type="text"
                                className="form-control"
                                id="inputName"
                                placeholder="Set name"
                            />

                        Last name

                            <input
                                onChange={this.addInput.bind(this, 'newLastName')}
                                type="text"
                                className="form-control"
                                id="inputLast"
                                placeholder="Set last name"
                            />

                        Age

                            <input
                                onChange={this.addInput.bind(this, 'newUserAge')}
                                type="number"
                                className="form-control"
                                id="inputAge"
                                placeholder="Set your age"
                            />

                            <button type="submit" className="btn mainButton" onClick={this.addNewUser}>
                                Add
                            </button>

List of writers

                        Lorem ipsum dolor sit amet, consectetur adipisicing elit.
                        Consequuntur, exercitationem impedit in magni quisquam repellat tenetur.
                        Aliquam blanditiis dolore eos eveniet fugit nobis,
                        officiis omnis qui quibusdam repellendus repudiandae ullam.

 {/* SEARCH */}

                                <select
                                    className="form-control"
                                    id="selectSearch"
                                    onChange={this.updateSelectSearch.bind(this)}
                                >
                                    <option value="s">Select one</option>
                                    <option value="name">Name</option>
                                    <option value="lastName">Last name</option>
                                    <option value="age">Age</option>
                                </select>

                                <input
                                    type="text"
                                    id="searchInput"
                                    className="form-control"
                                    placeholder="Search"
                                    onChange={this.updateSearch.bind(this)}
                                />

                                First Name
                                <button
                                    className="buttonSort glyphicon glyphicon-sort btn btn-default btn-xs"
                                    onClick={this.sorting.bind(this, 'name')}>
                                </button>

                                Last Name
                                <button
                                    className="buttonSort glyphicon glyphicon-sort btn btn-default btn-xs"
                                    onClick={this.sorting.bind(this, 'lastName')}>
                                </button>

                                Books
                                <button
                                    className="buttonSort glyphicon glyphicon-sort btn btn-default btn-xs"
                                    onClick={this.sortByAge.bind(this)}>
                                </button>

                                Age
                                <button
                                    className="buttonSort glyphicon glyphicon-sort btn btn-default btn-xs"
                                    onClick={this.sortByAge.bind(this)}>
                                </button>

                            Edit
                            Remove

                            {userList}

        );
    }
}<commit_msg>Testing collab<commit_after>import React from "react";
import User from '../components/User';

import UserService from '../components/UserService';
import BookService from '../components/BookService';

export class Home extends React.Component {

    constructor(){
        super();
        this.state = {
            users: [],
            books: [],
            searchValue: '',
            searchType: 'name',
            activeSort: {
                direction: 'desc',
                column: ''
            }
        };
        this.addNewUser = this.addNewUser.bind(this);
        this.removeUser = this.removeUser.bind(this);
        this.updateUser = this.updateUser.bind(this);

    }

    /* Capitalize first letter */
    capitalize(str) {
        return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
    }

    /*  */
    componentDidMount(){
        UserService.getAllUsers().then(response => {
            this.setState({
                users: response.data
            });
        });

        BookService.getAllBooks().then(response => {
            this.setState({
                books: response.data
            });
        });
    }

    /* ADD USER BUTTON */
    addNewUser() {
        let newUser = {
            name: this.capitalize(this.state.newUserName),
            lastName: this.capitalize(this.state.newLastName),
            age: this.state.newUserAge
        };
        UserService.addNewUser(newUser).then(response => {
            const addedUser = response.data;
            this.setState(state => {
                state.users.push(addedUser)
            });
        });
    }

    /* ADD INPUT USER (for all inputs) */
    addInput(inputName, e){
        let stateObj = {};
        stateObj[inputName] = e.target.value;
        this.setState(stateObj);
    }

    /* REMOVE */
    removeUser(userId){
        let message = confirm("If you delete user, you also delete user book(s)!");
        if (message == true) {
            let newStateUser = this.state.users.filter((user) => {
                return user.id != userId;
            });

            this.state.books.forEach((book) => {
                if(book.userId == userId){
                    BookService.deleteBook(book.id);
                }
            });

            UserService.deleteUser(userId);
            /* updateovan state posle remove-a korisnika */
            this.setState({
                users: newStateUser
            });
        }
    }

    updateUser(updatedData){
        this.setState(state => {
            for(let i = 0; i < state.users.length; i++){
                if(state.users[i].id === updatedData.id){
                    state.users[i] = updatedData;
                }
            }
        });
    }

    /* Sorting */
    sorting(thing){
        let dir;
        if(thing == this.state.activeSort.column) {
            dir = this.state.activeSort.direction == 'asc' ? 'desc' : 'asc';
        }
        let sorts = this.state.users.sort((a,b) => {
            if(dir == 'asc') {
                return a[thing]  b[thing]
            }
        });

        this.setState({
            users: sorts,
            activeSort: {
                column: thing,
                direction: dir
            }
        })
    }

    /* Sort by age */
    sortByAge(){
        let dir;
        if('writerAge' == this.state.activeSort.column) {
            dir = this.state.activeSort.direction == 'asc' ? 'desc' : 'asc';
        }

        let getSortYear = this.state.users.sort((a, b) => {
            if(dir == 'asc') {
                return a.age + b.age
            } else {
                return a.age - b.age
            }
        });

        this.setState({
            users: getSortYear,
            activeSort: {
                column: 'writerAge',
                direction: dir
            }
        })
    }

        /* input search */
    updateSearch(e){
        this.setState({
            searchValue: e.target.value
        })
    }

    updateSelectSearch(e) {
        this.setState({
            searchType: e.target.value
        })
    }

    render() {
        const filteredUsers = this.state.users.filter((user) => {
            return user[this.state.searchType].toLowerCase().indexOf(this.state.searchValue.toLowerCase()) !== -1;
        });

        let userList = filteredUsers.map((user) => {
            return (
                <User
                    key={user.id}
                    name={user.name}
                    lastName={user.lastName}
                    age={user.age}
                    id={user.id}
                    removeUser={this.removeUser}
                    updateUser={this.updateUser}
                />
            )
		  });
		  console.log('test');

        return (

Description

                            This web application is used for adding a writer in list of users/writers,
                            where you can editing existing writer, remove him or adding books for specific writer.

                            Also, you can sort writers and books or searching them by typing the first few letter.

 Add new writer:

                        First name

                            <input
                                onChange={this.addInput.bind(this, 'newUserName')}
                                type="text"
                                className="form-control"
                                id="inputName"
                                placeholder="Set name"
                            />

                        Last name

                            <input
                                onChange={this.addInput.bind(this, 'newLastName')}
                                type="text"
                                className="form-control"
                                id="inputLast"
                                placeholder="Set last name"
                            />

                        Age

                            <input
                                onChange={this.addInput.bind(this, 'newUserAge')}
                                type="number"
                                className="form-control"
                                id="inputAge"
                                placeholder="Set your age"
                            />

                            <button type="submit" className="btn mainButton" onClick={this.addNewUser}>
                                Add
                            </button>

List of writers

                        Lorem ipsum dolor sit amet, consectetur adipisicing elit.
                        Consequuntur, exercitationem impedit in magni quisquam repellat tenetur.
                        Aliquam blanditiis dolore eos eveniet fugit nobis,
                        officiis omnis qui quibusdam repellendus repudiandae ullam.

 {/* SEARCH */}

                                <select
                                    className="form-control"
                                    id="selectSearch"
                                    onChange={this.updateSelectSearch.bind(this)}
                                >
                                    <option value="s">Select one</option>
                                    <option value="name">Name</option>
                                    <option value="lastName">Last name</option>
                                    <option value="age">Age</option>
                                </select>

                                <input
                                    type="text"
                                    id="searchInput"
                                    className="form-control"
                                    placeholder="Search"
                                    onChange={this.updateSearch.bind(this)}
                                />

                                First Name
                                <button
                                    className="buttonSort glyphicon glyphicon-sort btn btn-default btn-xs"
                                    onClick={this.sorting.bind(this, 'name')}>
                                </button>

                                Last Name
                                <button
                                    className="buttonSort glyphicon glyphicon-sort btn btn-default btn-xs"
                                    onClick={this.sorting.bind(this, 'lastName')}>
                                </button>

                                Books
                                <button
                                    className="buttonSort glyphicon glyphicon-sort btn btn-default btn-xs"
                                    onClick={this.sortByAge.bind(this)}>
                                </button>

                                Age
                                <button
                                    className="buttonSort glyphicon glyphicon-sort btn btn-default btn-xs"
                                    onClick={this.sortByAge.bind(this)}>
                                </button>

                            Edit
                            Remove

                            {userList}

        );
    }
}<|endoftext|>


<commit_before>const DEBUG = false;
class Debug {
    constructor() {
        this.debugPar = document.getElementById("debug");
        this.debugPar.style.display = DEBUG ? "" : "none";
        if (DEBUG) this.show();
        else this.hide();
    }
    clear() {
        if (!DEBUG) return;
        this.debugPar.innerHTML = "";
    }
    append(...html) {
        if (!DEBUG) return;
        this.debugPar.innerHTML += html.join(" ");
    }
    appendLine(...html) {
        if (!DEBUG) return;
        this.debugPar.innerHTML += "
" + html.join(" ");;
    }
    show() {
        this.debugPar.style.display = "";
    }
    hide() {
        this.debugPar.style.display = "none";
    }
}
var debug = new Debug();
class Zoom {
    constructor() {}
    reset() {
        this.zoomUpperLimit = document.getElementById("zoomUpperLimit").innerText === "true";
        this.marginPixels = 20;
        this.img = document.getElementById("image");
        this.naturalWidth = this.img.naturalWidth;
        this.naturalHeight = this.img.naturalHeight;
        this.zoom = (window.innerWidth - this.marginPixels) / this.naturalWidth * 100;
        if (this.zoom > 100) this.zoom = 100;
        this.img.style.width = "";
        this.img.style.maxWidth = "";
        document.body.style.width = "";
        if (document.body.offsetHeight < window.innerHeight) document.body.style.height = window.innerHeight - this.marginPixels + "px";
        debug.clear();
        debug.append(`
            initial zoom:${this.zoom}

            image naturalWidth:${this.naturalWidth}

            image width:${this.img.width}

            window width:${window.innerWidth}

            window height:${window.innerHeight}

            body width:${document.body.offsetWidth}

            body height:${document.body.offsetHeight}

            scroll Width:${window.innerWidth - document.body.clientWidth}

        `);
    }
    setZoom(zoom) {
        debug.appendLine("org: ", this.zoom);
        let winWidth = window.innerWidth;
        let contentWidth = winWidth - this.marginPixels
        let minWidth = contentWidth < this.naturalWidth ? contentWidth : this.naturalWidth;
        let minZoom = parseInt(minWidth / this.naturalWidth * 100);
        const maxZoom = 100;

        if (this.zoomUpperLimit && zoom > maxZoom) zoom = maxZoom;
        if (zoom < minZoom || (minZoom == maxZoom && this.zoomUpperLimit)) {
            zoom = minZoom;
            this.img.style.width = "";
            this.img.style.maxWidth = "";
            document.body.style.width = "";
            zoom = minZoom;
        } else {
            let imgWidth = parseInt(this.naturalWidth * zoom / 100);
            this.img.style.width = imgWidth + 'px';
            let body = document.body;
            let bodyWidth = imgWidth + this.marginPixels < winWidth ? winWidth : imgWidth + this.marginPixels;
            body.style.width = bodyWidth + 'px'
            if (body.offsetHeight < window.innerHeight) body.style.height = window.innerHeight - this.marginPixels + "px";
        }
        this.zoom = zoom;
        debug.appendLine("cur: ", this.zoom);
    }
    setScroll(left, top) {
        document.body.scrollLeft = left;
        document.body.scrollTop = top;
    }
    add() {
        this.reset();
        document.body.addEventListener("mousewheel", () => {
            let mouseAt = this.getMousePointer();
            if (this.zoomUpperLimit) {
                this.setZoom(this.zoom + event.wheelDelta / 12);
            } else {
                // zoom level increase / decrease by 30% for each wheel scroll
                this.setZoom(this.zoom * (event.wheelDelta / 600 + 1));
            }
            this.followMousePointer(mouseAt);
            saveStatus();
            return false;
        });
        window.onresize = () => {
            let winWidth = window.innerWidth;
            let contentWidth = winWidth - this.marginPixels
            let minWidth = contentWidth < this.naturalWidth ? contentWidth : this.naturalWidth;
            let minZoom = parseInt(minWidth / this.naturalWidth * 100);

            if (this.img.style.width == "") {
                // console.log("update zoom value due to resize");
                this.zoom = minZoom;
            } else if (this.zoom < minZoom) {
                // console.log("change zoom to fit");
                this.zoom = minZoom;
                this.img.style.width = "";
                this.img.style.maxWidth = "";
                document.body.style.width = "";
            }
        };
        window.onmousewheel = function () {
            return false
        };
    }
    followMousePointer(mouseAt) {
        let e = event || window.event;
        let imgWidth = parseInt(this.naturalWidth * this.zoom / 100);
        let imgHeight = parseInt(this.naturalHeight * this.zoom / 100);
        document.body.scrollLeft += parseInt(imgWidth * mouseAt.imageX + this.marginPixels / 2) - mouseAt.x;
        document.body.scrollTop += parseInt(imgHeight * mouseAt.imageY + this.marginPixels / 2) - mouseAt.y;
    }
    getMousePointer() {
        let imgWidth = parseInt(this.naturalWidth * this.zoom / 100);
        let imgHeight = parseInt(this.naturalHeight * this.zoom / 100);
        let e = event || window.event;
        let mouseAt = {
            x: e.clientX + document.body.scrollLeft,
            y: e.clientY + document.body.scrollTop,
            imageX: (e.clientX + document.body.scrollLeft - this.marginPixels / 2) / imgWidth,
            imageY: (e.clientY + document.body.scrollTop - this.marginPixels / 2) / imgHeight,
        }
        return mouseAt;
    }
}
class Switcher {
    constructor() {
        this.current = 0;
        this.images = [];
        this.image = document.getElementById("image");
        this.pInfo = document.getElementById("pageInfo");
        this.pInfoTpl = this.pInfo.innerText;
        for (let e of document.getElementById("images").getElementsByTagName("img")) {
            this.images.push(e.src);
        }
    }
    add() {
        if (this.images.length <= 1) {
            document.getElementById("placeholder").style.display = "none";
            document.getElementById("controls").style.display = "none";
            return;
        }
        document.getElementById("btnNext").addEventListener("click", () => {
            if (this.current == this.images.length) return;
            this.moveTo(++this.current);
            saveStatus();
        });
        document.getElementById("btnPrev").addEventListener("click", () => {
            if (this.current == 1) return;
            this.moveTo(--this.current);
            saveStatus();
        });

        this.moveTo(1);
        document.getElementById("images").remove();
        // console.log(this.images.length);
    }
    moveTo(page) {
        if (page < 1 || page > this.images.length) return;
        this.image.src = this.images[page - 1];
        this.pInfo.innerText = String.format(this.pInfoTpl, page, this.images.length);
        this.current = page;
        zoomer.reset();
    }
}
String.format = function format() {
    if (arguments.length == 0)
        return null;

    var str = arguments[0];
    for (var i = 1; i < arguments.length; i++) {
        var re = new RegExp('\\{' + (i - 1) + '\\}', 'gm');
        str = str.replace(re, arguments[i]);
    }
    return str;
}
let zoomer;
let switcher;
let sendStatus;

function saveStatus() {
    if (sendStatus) {
        let status = JSON.stringify({
            page: switcher.current,
            zoom: zoomer.zoom,
            x: document.body.scrollLeft,
            y: document.body.scrollTop
        });
        // console.log("save status: " + status);
        sendStatus.attributes["href"].value = encodeURI('command:plantuml.previewStatus?' + status);
        sendStatus.click();
    }
}
window.addEventListener("load", () => {
    sendStatus = document.getElementById("sendStatus");
    zoomer = new Zoom();
    switcher = new Switcher();
    switcher.add();
    zoomer.add();
    let jsonStatus = document.getElementById("status").innerHTML;
    if (jsonStatus) {
        let status = JSON.parse(jsonStatus);
        if (status) {
            switcher.moveTo(status.page);
            zoomer.setZoom(status.zoom);
            zoomer.setScroll(status.x, status.y);
        }
    }
    if (!document.getElementById("errtxt").innerText.trim())
        document.getElementById("error-warning").style.display = "none";

});
window.addEventListener("mouseup", () => saveStatus());<commit_msg>code optimize<commit_after>const DEBUG = false;
class Debug {
    constructor() {
        this.debugPar = document.getElementById("debug");
        this.debugPar.style.display = DEBUG ? "" : "none";
        if (DEBUG) this.show();
        else this.hide();
    }
    clear() {
        if (!DEBUG) return;
        this.debugPar.innerHTML = "";
    }
    append(...html) {
        if (!DEBUG) return;
        this.debugPar.innerHTML += html.join(" ");
    }
    appendLine(...html) {
        if (!DEBUG) return;
        this.debugPar.innerHTML += "
" + html.join(" ");;
    }
    show() {
        this.debugPar.style.display = "";
    }
    hide() {
        this.debugPar.style.display = "none";
    }
}
var debug = new Debug();
class Zoom {
    constructor() {}
    reset() {
        this.zoomUpperLimit = document.getElementById("zoomUpperLimit").innerText === "true";
        this.marginPixels = 20;
        this.img = document.getElementById("image");
        this.naturalWidth = this.img.naturalWidth;
        this.naturalHeight = this.img.naturalHeight;
        this.zoom = (window.innerWidth - this.marginPixels) / this.naturalWidth * 100;
        if (this.zoom > 100) this.zoom = 100;
        this.img.style.width = "";
        this.img.style.maxWidth = "";
        document.body.style.width = "";
        if (document.body.offsetHeight < window.innerHeight) document.body.style.height = window.innerHeight - this.marginPixels + "px";
        debug.clear();
        debug.append(`
            initial zoom:${this.zoom}

            image naturalWidth:${this.naturalWidth}

            image width:${this.img.width}

            window width:${window.innerWidth}

            window height:${window.innerHeight}

            body width:${document.body.offsetWidth}

            body height:${document.body.offsetHeight}

            scroll Width:${window.innerWidth - document.body.clientWidth}

        `);
    }
    setZoom(zoom) {
        debug.appendLine("org: ", this.zoom);
        let winWidth = window.innerWidth;
        let contentWidth = winWidth - this.marginPixels
        let minWidth = contentWidth < this.naturalWidth ? contentWidth : this.naturalWidth;
        let minZoom = parseInt(minWidth / this.naturalWidth * 100);
        const maxZoom = 100;

        if (this.zoomUpperLimit && zoom > maxZoom) zoom = maxZoom;
        if (zoom < minZoom || (minZoom == maxZoom && this.zoomUpperLimit)) {
            zoom = minZoom;
            this.img.style.width = "";
            this.img.style.maxWidth = "";
            document.body.style.width = "";
            zoom = minZoom;
        } else {
            let imgWidth = parseInt(this.naturalWidth * zoom / 100);
            this.img.style.width = imgWidth + 'px';
            let body = document.body;
            let bodyWidth = imgWidth + this.marginPixels < winWidth ? winWidth : imgWidth + this.marginPixels;
            body.style.width = bodyWidth + 'px'
            if (body.offsetHeight < window.innerHeight) body.style.height = window.innerHeight - this.marginPixels + "px";
        }
        this.zoom = zoom;
        debug.appendLine("cur: ", this.zoom);
    }
    setScroll(left, top) {
        document.body.scrollLeft = left;
        document.body.scrollTop = top;
    }
    add() {
        this.reset();
        document.body.addEventListener("mousewheel", () => {
            let mouseAt = this.getMousePointer();
            if (this.zoomUpperLimit) {
                this.setZoom(this.zoom + event.wheelDelta / 12);
            } else {
                // zoom level increase / decrease by 30% for each wheel scroll
                this.setZoom(this.zoom * (event.wheelDelta / 600 + 1));
            }
            this.followMousePointer(mouseAt);
            saveStatus();
            return false;
        });
        window.onresize = () => {
            let winWidth = window.innerWidth;
            let contentWidth = winWidth - this.marginPixels
            let minWidth = contentWidth < this.naturalWidth ? contentWidth : this.naturalWidth;
            let minZoom = parseInt(minWidth / this.naturalWidth * 100);

            if (this.img.style.width == "") {
                // console.log("update zoom value due to resize");
                this.zoom = minZoom;
            } else if (this.zoom < minZoom) {
                // console.log("change zoom to fit");
                this.zoom = minZoom;
                this.img.style.width = "";
                this.img.style.maxWidth = "";
                document.body.style.width = "";
            }
        };
        window.onmousewheel = function () {
            return false
        };
    }
    followMousePointer(mouseAt) {
        let e = event || window.event;
        let imgWidth = parseInt(this.naturalWidth * this.zoom / 100);
        let imgHeight = parseInt(this.naturalHeight * this.zoom / 100);
        document.body.scrollLeft += parseInt(imgWidth * mouseAt.imageX + this.marginPixels / 2) - mouseAt.x;
        document.body.scrollTop += parseInt(imgHeight * mouseAt.imageY + this.marginPixels / 2) - mouseAt.y;
    }
    getMousePointer() {
        let imgWidth = parseInt(this.naturalWidth * this.zoom / 100);
        let imgHeight = parseInt(this.naturalHeight * this.zoom / 100);
        let e = event || window.event;
        let mouseAt = {
            x: e.clientX + document.body.scrollLeft,
            y: e.clientY + document.body.scrollTop,
            imageX: (e.clientX + document.body.scrollLeft - this.marginPixels / 2) / imgWidth,
            imageY: (e.clientY + document.body.scrollTop - this.marginPixels / 2) / imgHeight,
        }
        return mouseAt;
    }
}
class Switcher {
    constructor() {
        this.current = 0;
        this.images = [];
        this.image = document.getElementById("image");
        this.pInfo = document.getElementById("pageInfo");
        this.pInfoTpl = this.pInfo.innerText;
        for (let e of document.getElementById("images").getElementsByTagName("img")) {
            this.images.push(e.src);
        }
    }
    add() {
        if (this.images.length <= 1) {
            document.getElementById("placeholder").style.display = "none";
            document.getElementById("controls").style.display = "none";
            return;
        }
        document.getElementById("btnNext").addEventListener("click", () => {
            if (this.current == this.images.length) return;
            this.moveTo(++this.current);
            saveStatus();
        });
        document.getElementById("btnPrev").addEventListener("click", () => {
            if (this.current == 1) return;
            this.moveTo(--this.current);
            saveStatus();
        });

        this.moveTo(1);
        document.getElementById("images").remove();
        // console.log(this.images.length);
    }
    moveTo(page) {
        if (page < 1 || page > this.images.length) return;
        this.image.src = this.images[page - 1];
        this.pInfo.innerText = String.format(this.pInfoTpl, page, this.images.length);
        this.current = page;
        zoomer.reset();
    }
}
String.format = function format() {
    if (arguments.length == 0)
        return null;

    var str = arguments[0];
    for (var i = 1; i < arguments.length; i++) {
        var re = new RegExp('\\{' + (i - 1) + '\\}', 'gm');
        str = str.replace(re, arguments[i]);
    }
    return str;
}
let zoomer;
let switcher;
let sendStatus;

function saveStatus() {
    if (sendStatus) {
        let status = JSON.stringify({
            page: switcher.current,
            zoom: zoomer.zoom,
            x: document.body.scrollLeft,
            y: document.body.scrollTop
        });
        // console.log("save status: " + status);
        sendStatus.attributes["href"].value = 'command:plantuml.previewStatus?' + encodeURIComponent(status);
        sendStatus.click();
    }
}
window.addEventListener("load", () => {
    sendStatus = document.getElementById("sendStatus");
    zoomer = new Zoom();
    switcher = new Switcher();
    switcher.add();
    zoomer.add();
    let jsonStatus = document.getElementById("status").innerHTML;
    if (jsonStatus) {
        let status = JSON.parse(jsonStatus);
        if (status) {
            switcher.moveTo(status.page);
            zoomer.setZoom(status.zoom);
            zoomer.setScroll(status.x, status.y);
        }
    }
    if (!document.getElementById("errtxt").innerText.trim())
        document.getElementById("error-warning").style.display = "none";

});
window.addEventListener("mouseup", () => saveStatus());<|endoftext|>


<commit_before><commit_msg>Fix regression introduced in previous commit<commit_after><|endoftext|>