<commit_before>/* object contains jQuery-contextMenu
 * https://github.com/medialize/jQuery-contextMenu
 */
var CloudCmd, Util, DOM, CloudFunc, $;

(function(CloudCmd, Util, DOM, CloudFunc) {
    'use strict';

    CloudCmd.Menu = MenuProto;

    function MenuProto(pPosition) {
        var Name                        = 'Menu',
            Info                        = DOM.CurrentInfo,
            Loading                     = false,
            Key                         = CloudCmd.Key,
            Events                      = DOM.Events,
            MenuSeted                   = false,
            Menu                        = this,
            Position,
            Images                      = DOM.Images,
            UploadToItemNames;

        this.ENABLED                    = false;

        function init() {
            Loading  = true;

            Position = pPosition;

            Util.loadOnLoad([
                DOM.jqueryLoad,
                load,
                setUploadToItemNames,
                Menu.show
            ]);

            Events.addKey(listener);
        }

        this.show        = function() {
            Key.unsetBind();

            if (!Loading) {
                set();
                Images.hideLoad();

                if (Position && !Position.x )
                    Position = undefined;

                $('li').contextMenu(Position);
            }
        };

        /* function read data from modules.json
         * and build array of menu items of "upload to"
         * menu
         */
        function setUploadToItemNames(pCallBack) {
            CloudCmd.getModules(function(pModules) {
                var lStorageObj         = Util.findObjByNameInArr( pModules, 'storage' );
                    UploadToItemNames   = Util.getNamesFromObjArray( lStorageObj ) || [];

                Util.exec(pCallBack);
            });
        }

        /**
         * function get menu item object for Upload To
         */
        function getUploadToItems(pObjectName) {
            var i, n, lStr, lObj        = {};
            if (Util.isArray(pObjectName)) {

                n = pObjectName.length;
                for (i = 0; i < n; i++) {
                    lStr        = pObjectName[i];
                    lObj[lStr]  =  getUploadToItems( lStr );
                }
            }
            else if (Util.isString(pObjectName)) {
                lObj = function(key, opt) {
                    Info.getData(function(data) {
                        var name = Info.name;
                        CloudCmd.execFromModule(pObjectName, 'uploadFile', {
                            name: name,
                            data: data
                        });
                    });

                    Util.log('Uploading to ' + pObjectName+ '...');
                };
            }

            return lObj;
        }

        /**
         * get menu item
         */
        function getItem(pName, pCallBack) {
            var lRet,
                name    = pName,
                icon    = name && name.toLowerCase();

            icon        = Util.removeStr(icon, ['(', ')']);
            icon        = Util.replaceStr(icon, ' ', '-');

            lRet        = {
                name : pName,
                icon : icon
            };

            if (Util.isFunction(pCallBack))
                lRet.callback       = pCallBack;

            else if (Util.isObject(pCallBack))
                if (pCallBack.name)
                    lRet.items      = pCallBack;
                else
                    lRet.items      = getAllItems(pCallBack);

            return lRet;
        }

        /**
         * get all menu items
         * pItems = [{pName, pFunc}]
         */
         function getAllItems(pItems) {
            var lRet = {},
                lName,
                lFunc;

            if (pItems)
                for (lName in pItems) {
                    lFunc       = pItems[lName];
                    lRet[lName] = getItem(lName, lFunc);
                }

            return lRet;
         }

        /**
         * download menu item callback
         */
        function downloadFromMenu(key, opt) {
            Images.showLoad();

            var TIME    = 1000,
                lPath   = Info.path,
                lId     = DOM.getIdBySrc(lPath),
                lDir    = Info.isDir ? '&&json' : '';

            Util.log('downloading file ' + lPath +'...');

            lPath = CloudFunc.FS + lPath + '?download' + lDir;

            if (!DOM.getById(lId)) {
                var lDownload = DOM.anyload({
                    name        : 'iframe',
                    async       : false,
                    className   : 'hidden',
                    src         : lPath,
                    func        : Images.hideLoad
                });

                Images.hideLoad();
                setTimeout(function() {
                    document.body.removeChild(lDownload);
                }, TIME);
            }
            else
                Images.showError({
                    responseText: 'Error: You trying to' +
                        'download same file to often'});
        }

        /**
         * function return configureation for menu
         */
        function getConfig () {
            var lRet,
                lMenuItems = {
                    'View'          : show.bind(null, 'View'),
                    'Edit'          : show.bind(null, 'Edit'),
                    'Rename'        : function() {
                        setTimeout(DOM.renameCurrent, 100);
                    },
                    'Delete'        : DOM.promptDelete,
                    '(Un)Select All': DOM.toggleAllSelectedFiles,
                    'Zip file'      : DOM.zipFile,
                    'Unzip file'    : DOM.unzipFile
                };

            if (UploadToItemNames.length)
                lMenuItems['Upload to'] = getUploadToItems(UploadToItemNames);

            lMenuItems.Download = downloadFromMenu;

            lMenuItems.New = {
                'File'              : DOM.promptNewFile,
                'Directory'         : DOM.promptNewDir,

                'From FilePicker'   : function() {
                    Images.showLoad({
                        top: true
                    });

                    CloudCmd.execFromModule('FilePicker', 'saveFile', function(pName, pData) {
                        var lPath = DOM.getCurrentDirPath() + pName;

                        DOM.RESTful.write(lPath,  pData, CloudCmd.refresh);
                    });
                }
            };

            lRet = {
                // define which elements trigger this menu
                selector: 'li',

                // define the elements of the menu
                items   : getAllItems(lMenuItems),
                events  :{
                    hide: function() {
                        var event = window.event;

                        if (!event || !event.keyCode)
                            clickProcessing();
                        else if (event.keyCode)
                            listener(event);
                    }
                }
            };

            return lRet;

            function show(pName) {
                var lEditor = CloudCmd[pName],
                    lResult = Util.exec(lEditor);

                if (!lResult)
                    Util.exec(lEditor.show);
            }
        }

        /** function loads css and js of Menu
         * @param pCallBack
         */
        function load(pCallBack) {
            Util.time(Name + ' load');

            var dir     = CloudCmd.LIBDIRCLIENT + 'menu/',
                files   = [
                    dir + 'contextMenu.js',
                    dir + 'contextMenu.css'
                ];

            DOM.anyLoadInParallel(files, function() {
                setCSS();
                Util.timeEnd(Name + ' load');
                Loading = false;
                Util.exec(pCallBack);
            });
        }

        function setCSS() {
            DOM.cssSet({
                id   : 'menu-css',
                inner: '.context-menu-item.icon-edit {'     +
                            'background-image: none;'       +
                        '}'                                 +
                        '.context-menu-item.icon-delete {'  +
                            'background-image: none;'       +
                        '}'
                });
        }

        /*
         * Menu works in some crazy way so need a
         * little hack to get every thing work out.
         * When menu shows up, it draws invisible
         * layer wich hides all elements of
         * Cloud Commander so it could not handle
         * onclick events. To get every thing work
         * how expected we hide invisible layer
         * so for observer it is nothing special
         * is not going on. All magic happening in
         * DOM tree.
         */
        function clickProcessing() {
            var lLayer = DOM.getById('context-menu-layer');

            if (lLayer && Position) {
                DOM.hide(lLayer);

                var lElement    = document.elementFromPoint(Position.x, Position.y),
                    lTag        = lElement.tagName;

                if (Util.strCmp(lTag, ['A', 'SPAN'])) {
                    switch (lTag) {
                        case 'A':
                            lElement = lElement.parentElement.parentElement;
                            break;

                        case 'SPAN':
                            lElement = lElement.parentElement;
                            break;
                    }

                    if (lElement.className === '')
                        DOM.setCurrentFile(lElement);
                }

                DOM.show(lLayer);
                Key.setBind();
            }
        }

        function set() {
            if (!MenuSeted) {
                $.contextMenu(getConfig());
                MenuSeted = true;
                DOM.Events.add('mousemove', function(pEvent) {
                    Position = {
                        x : pEvent.clientX,
                        y : pEvent.clientY
                    };
                });
            }
        }

        function listener(event) {
            var current,
                F9          = Key.F9,
                ESC         = Key.ESC,
                key         = event.keyCode,
                isBind      = Key.isBind();

            if (isBind && key === F9) {
                current = DOM.getCurrentFile();
                $(current).contextmenu();

                DOM.preventDefault(event);
            }  else if (key === ESC)
                    Key.setBind();
        }

        init();
    }
})(CloudCmd, Util, DOM, CloudFunc);
<commit_msg>chore(menu) rm "\n"<commit_after>/* object contains jQuery-contextMenu
 * https://github.com/medialize/jQuery-contextMenu
 */
var CloudCmd, Util, DOM, CloudFunc, $;

(function(CloudCmd, Util, DOM, CloudFunc) {
    'use strict';

    CloudCmd.Menu = MenuProto;

    function MenuProto(pPosition) {
        var Name                        = 'Menu',
            Info                        = DOM.CurrentInfo,
            Loading                     = false,
            Key                         = CloudCmd.Key,
            Events                      = DOM.Events,
            MenuSeted                   = false,
            Menu                        = this,
            Position,
            Images                      = DOM.Images,
            UploadToItemNames;

        this.ENABLED                    = false;

        function init() {
            Loading  = true;

            Position = pPosition;

            Util.loadOnLoad([
                DOM.jqueryLoad,
                load,
                setUploadToItemNames,
                Menu.show
            ]);

            Events.addKey(listener);
        }

        this.show        = function() {
            Key.unsetBind();

            if (!Loading) {
                set();
                Images.hideLoad();

                if (Position && !Position.x )
                    Position = undefined;

                $('li').contextMenu(Position);
            }
        };

        /* function read data from modules.json
         * and build array of menu items of "upload to"
         * menu
         */
        function setUploadToItemNames(pCallBack) {
            CloudCmd.getModules(function(pModules) {
                var lStorageObj         = Util.findObjByNameInArr( pModules, 'storage' );
                    UploadToItemNames   = Util.getNamesFromObjArray( lStorageObj ) || [];

                Util.exec(pCallBack);
            });
        }

        /**
         * function get menu item object for Upload To
         */
        function getUploadToItems(pObjectName) {
            var i, n, lStr, lObj        = {};
            if (Util.isArray(pObjectName)) {

                n = pObjectName.length;
                for (i = 0; i < n; i++) {
                    lStr        = pObjectName[i];
                    lObj[lStr]  =  getUploadToItems( lStr );
                }
            }
            else if (Util.isString(pObjectName)) {
                lObj = function(key, opt) {
                    Info.getData(function(data) {
                        var name = Info.name;
                        CloudCmd.execFromModule(pObjectName, 'uploadFile', {
                            name: name,
                            data: data
                        });
                    });

                    Util.log('Uploading to ' + pObjectName+ '...');
                };
            }

            return lObj;
        }

        /**
         * get menu item
         */
        function getItem(pName, pCallBack) {
            var lRet,
                name    = pName,
                icon    = name && name.toLowerCase();

            icon        = Util.removeStr(icon, ['(', ')']);
            icon        = Util.replaceStr(icon, ' ', '-');

            lRet        = {
                name : pName,
                icon : icon
            };

            if (Util.isFunction(pCallBack))
                lRet.callback       = pCallBack;

            else if (Util.isObject(pCallBack))
                if (pCallBack.name)
                    lRet.items      = pCallBack;
                else
                    lRet.items      = getAllItems(pCallBack);

            return lRet;
        }

        /**
         * get all menu items
         * pItems = [{pName, pFunc}]
         */
         function getAllItems(pItems) {
            var lRet = {},
                lName,
                lFunc;

            if (pItems)
                for (lName in pItems) {
                    lFunc       = pItems[lName];
                    lRet[lName] = getItem(lName, lFunc);
                }

            return lRet;
         }

        /**
         * download menu item callback
         */
        function downloadFromMenu(key, opt) {
            Images.showLoad();

            var TIME    = 1000,
                lPath   = Info.path,
                lId     = DOM.getIdBySrc(lPath),
                lDir    = Info.isDir ? '&&json' : '';

            Util.log('downloading file ' + lPath +'...');

            lPath = CloudFunc.FS + lPath + '?download' + lDir;

            if (!DOM.getById(lId)) {
                var lDownload = DOM.anyload({
                    name        : 'iframe',
                    async       : false,
                    className   : 'hidden',
                    src         : lPath,
                    func        : Images.hideLoad
                });

                Images.hideLoad();
                setTimeout(function() {
                    document.body.removeChild(lDownload);
                }, TIME);
            }
            else
                Images.showError({
                    responseText: 'Error: You trying to' +
                        'download same file to often'});
        }

        /**
         * function return configureation for menu
         */
        function getConfig () {
            var lRet,
                lMenuItems = {
                    'View'          : show.bind(null, 'View'),
                    'Edit'          : show.bind(null, 'Edit'),
                    'Rename'        : function() {
                        setTimeout(DOM.renameCurrent, 100);
                    },
                    'Delete'        : DOM.promptDelete,
                    '(Un)Select All': DOM.toggleAllSelectedFiles,
                    'Zip file'      : DOM.zipFile,
                    'Unzip file'    : DOM.unzipFile
                };

            if (UploadToItemNames.length)
                lMenuItems['Upload to'] = getUploadToItems(UploadToItemNames);

            lMenuItems.Download = downloadFromMenu;

            lMenuItems.New = {
                'File'              : DOM.promptNewFile,
                'Directory'         : DOM.promptNewDir,

                'From FilePicker'   : function() {
                    Images.showLoad({
                        top: true
                    });

                    CloudCmd.execFromModule('FilePicker', 'saveFile', function(pName, pData) {
                        var lPath = DOM.getCurrentDirPath() + pName;

                        DOM.RESTful.write(lPath,  pData, CloudCmd.refresh);
                    });
                }
            };

            lRet = {
                // define which elements trigger this menu
                selector: 'li',

                // define the elements of the menu
                items   : getAllItems(lMenuItems),
                events  :{
                    hide: function() {
                        var event = window.event;

                        if (!event || !event.keyCode)
                            clickProcessing();
                        else if (event.keyCode)
                            listener(event);
                    }
                }
            };

            return lRet;

            function show(pName) {
                var lEditor = CloudCmd[pName],
                    lResult = Util.exec(lEditor);

                if (!lResult)
                    Util.exec(lEditor.show);
            }
        }

        /** function loads css and js of Menu
         * @param pCallBack
         */
        function load(pCallBack) {
            Util.time(Name + ' load');

            var dir     = CloudCmd.LIBDIRCLIENT + 'menu/',
                files   = [
                    dir + 'contextMenu.js',
                    dir + 'contextMenu.css'
                ];

            DOM.anyLoadInParallel(files, function() {
                setCSS();
                Util.timeEnd(Name + ' load');
                Loading = false;
                Util.exec(pCallBack);
            });
        }

        function setCSS() {
            DOM.cssSet({
                id   : 'menu-css',
                inner: '.context-menu-item.icon-edit {'     +
                            'background-image: none;'       +
                        '}'                                 +
                        '.context-menu-item.icon-delete {'  +
                            'background-image: none;'       +
                        '}'
                });
        }

        /*
         * Menu works in some crazy way so need a
         * little hack to get every thing work out.
         * When menu shows up, it draws invisible
         * layer wich hides all elements of
         * Cloud Commander so it could not handle
         * onclick events. To get every thing work
         * how expected we hide invisible layer
         * so for observer it is nothing special
         * is not going on. All magic happening in
         * DOM tree.
         */
        function clickProcessing() {
            var lLayer = DOM.getById('context-menu-layer');

            if (lLayer && Position) {
                DOM.hide(lLayer);

                var lElement    = document.elementFromPoint(Position.x, Position.y),
                    lTag        = lElement.tagName;

                if (Util.strCmp(lTag, ['A', 'SPAN'])) {
                    switch (lTag) {
                        case 'A':
                            lElement = lElement.parentElement.parentElement;
                            break;

                        case 'SPAN':
                            lElement = lElement.parentElement;
                            break;
                    }

                    if (lElement.className === '')
                        DOM.setCurrentFile(lElement);
                }

                DOM.show(lLayer);
                Key.setBind();
            }
        }

        function set() {
            if (!MenuSeted) {
                $.contextMenu(getConfig());
                MenuSeted = true;
                DOM.Events.add('mousemove', function(pEvent) {
                    Position = {
                        x : pEvent.clientX,
                        y : pEvent.clientY
                    };
                });
            }
        }

        function listener(event) {
            var current,
                F9          = Key.F9,
                ESC         = Key.ESC,
                key         = event.keyCode,
                isBind      = Key.isBind();

            if (isBind && key === F9) {
                current = DOM.getCurrentFile();
                $(current).contextmenu();

                DOM.preventDefault(event);
            }  else if (key === ESC)
                    Key.setBind();
        }

        init();
    }
})(CloudCmd, Util, DOM, CloudFunc);
<|endoftext|>


<commit_before>import {NotFoundError, NetworkError, BadRequestError} from "./errors";

let URI = require("URIjs");
let URITemplate = require("URIjs").URITemplate;

let axios = require("axios");
var EventSource = (typeof window === 'undefined') ? require('eventsource') : window.EventSource;
let toBluebird = require("bluebird").resolve;
let _ = require('lodash');

/**
* @class Builder
*/
export class CallBuilder {

  /*
  * @constructor
  */
  constructor(url) {
    this.url = url;
    this.filter = [];
  }

  checkFilter() {
    if (this.filter.length >= 2) {
      throw new BadRequestError("Too many filters specified", this.filter);
    }
    if (this.filter.length === 1) {
      this.url.segment(this.filter[0]);
    }
  }

  /*
  * Triggers a HTTP request using this builder's current configuration.
  * Returns a Promise that resolves to the server's response.
  */
  call() {
    this.checkFilter();
    var promise = this._sendNormalRequest(this.url)
      .then(r => this._parseResponse(r));
    return promise;
  }

  /*
  * Creates an Eventsource that listens for incoming messages from the server.
  * URL based on builder's current configuration.
  */
  stream(options) {
    this.checkFilter();
    var es = new EventSource(this.url.toString());
    es.onmessage = (message) => {
      var result = message.data ? this._parseRecord(JSON.parse(message.data)) : message;
      options.onmessage(result);
    };
    es.onerror = options.onerror;
    return es;
  }

  _requestFnForLink(link) {
    return opts => {
      let uri;

      if (link.template) {
        let template = URITemplate(link.href);
        uri = URI(template.expand(opts));
      } else {
        uri = URI(link.href);
      }

      uri = uri
        .authority(this.url.authority())
        .protocol(this.url.protocol());

      return this._sendNormalRequest(uri).then(r => this._parseRecord(r));
    };
  }

  /**
  * Convert each link into a function on the response object.
  */
  _parseRecord(json) {
    if (!json._links) {
      return json;
    }
    _.forEach(json._links, (n, key) => {json[key] = this._requestFnForLink(n);});
    return json;
  }

  _sendNormalRequest(url) {
    url.addQuery('c', Math.random());
    var promise = axios.get(url.toString())
      .then(response => response.data)
      .catch(this._handleNetworkError);
    return toBluebird(promise);
  }

  _parseResponse(json) {
    if (json._embedded && json._embedded.records) {
      return this._toCollectionPage(json);
    } else {
      return this._parseRecord(json);
    }
  }

  _toCollectionPage(json) {
    for (var i = 0; i < json._embedded.records.length; i++) {
      json._embedded.records[i] = this._parseRecord(json._embedded.records[i]);
    }
    return {
      records: json._embedded.records,
      next: () => {
        return this._sendNormalRequest(URI(json._links.next.href)
          .authority(this.url.authority())
          .protocol(this.url.protocol())
          )
          .then(r => this._toCollectionPage(r));
      },
      prev: () => {
        return this._sendNormalRequest(URI(json._links.prev.href)
          .authority(this.url.authority())
          .protocol(this.url.protocol())
          )
          .then(r => this._toCollectionPage(r));
      }
    };
  }

  _handleNetworkError(response) {
    if (response instanceof Error) {
      return Promise.reject(response);
    } else {
      switch (response.status) {
        case 404:
          return Promise.reject(new NotFoundError(response.data, response));
        default:
          return Promise.reject(new NetworkError(response.status, response));
      }
    }
  }

  cursor(token) {
    this.url.addQuery("cursor", token);
    return this;
  }

  limit(number) {
    this.url.addQuery("limit", number);
    return this;
  }

  order(direction) {
    this.url.addQuery("order", direction);
    return this;
  }

}
<commit_msg>Error when EventSource is not supported<commit_after>import {NotFoundError, NetworkError, BadRequestError} from "./errors";

let URI = require("URIjs");
let URITemplate = require("URIjs").URITemplate;

let axios = require("axios");
var EventSource = (typeof window === 'undefined') ? require('eventsource') : window.EventSource;
let toBluebird = require("bluebird").resolve;
let _ = require('lodash');

/**
* @class Builder
*/
export class CallBuilder {

  /*
  * @constructor
  */
  constructor(url) {
    this.url = url;
    this.filter = [];
  }

  checkFilter() {
    if (this.filter.length >= 2) {
      throw new BadRequestError("Too many filters specified", this.filter);
    }
    if (this.filter.length === 1) {
      this.url.segment(this.filter[0]);
    }
  }

  /*
  * Triggers a HTTP request using this builder's current configuration.
  * Returns a Promise that resolves to the server's response.
  */
  call() {
    this.checkFilter();
    var promise = this._sendNormalRequest(this.url)
      .then(r => this._parseResponse(r));
    return promise;
  }

  /*
  * Creates an Eventsource that listens for incoming messages from the server.
  * URL based on builder's current configuration.
  */
  stream(options) {
    this.checkFilter();
    try {
      var es = new EventSource(this.url.toString());
      es.onmessage = (message) => {
        var result = message.data ? this._parseRecord(JSON.parse(message.data)) : message;
        options.onmessage(result);
      };
      es.onerror = options.onerror;
      return es;
    } catch (err) {
      if (options.onerror) {
        options.onerror('EventSource not supported');
      }
      return false;
    }
  }

  _requestFnForLink(link) {
    return opts => {
      let uri;

      if (link.template) {
        let template = URITemplate(link.href);
        uri = URI(template.expand(opts));
      } else {
        uri = URI(link.href);
      }

      uri = uri
        .authority(this.url.authority())
        .protocol(this.url.protocol());

      return this._sendNormalRequest(uri).then(r => this._parseRecord(r));
    };
  }

  /**
  * Convert each link into a function on the response object.
  */
  _parseRecord(json) {
    if (!json._links) {
      return json;
    }
    _.forEach(json._links, (n, key) => {json[key] = this._requestFnForLink(n);});
    return json;
  }

  _sendNormalRequest(url) {
    url.addQuery('c', Math.random());
    var promise = axios.get(url.toString())
      .then(response => response.data)
      .catch(this._handleNetworkError);
    return toBluebird(promise);
  }

  _parseResponse(json) {
    if (json._embedded && json._embedded.records) {
      return this._toCollectionPage(json);
    } else {
      return this._parseRecord(json);
    }
  }

  _toCollectionPage(json) {
    for (var i = 0; i < json._embedded.records.length; i++) {
      json._embedded.records[i] = this._parseRecord(json._embedded.records[i]);
    }
    return {
      records: json._embedded.records,
      next: () => {
        return this._sendNormalRequest(URI(json._links.next.href)
          .authority(this.url.authority())
          .protocol(this.url.protocol())
          )
          .then(r => this._toCollectionPage(r));
      },
      prev: () => {
        return this._sendNormalRequest(URI(json._links.prev.href)
          .authority(this.url.authority())
          .protocol(this.url.protocol())
          )
          .then(r => this._toCollectionPage(r));
      }
    };
  }

  _handleNetworkError(response) {
    if (response instanceof Error) {
      return Promise.reject(response);
    } else {
      switch (response.status) {
        case 404:
          return Promise.reject(new NotFoundError(response.data, response));
        default:
          return Promise.reject(new NetworkError(response.status, response));
      }
    }
  }

  cursor(token) {
    this.url.addQuery("cursor", token);
    return this;
  }

  limit(number) {
    this.url.addQuery("limit", number);
    return this;
  }

  order(direction) {
    this.url.addQuery("order", direction);
    return this;
  }

}
<|endoftext|>


<commit_before>// Document view

'use strict';

var renderDocument = require('../_business-process-document-preview')
  , renderDocumentHistory = require('../_business-process-revision-document-history')
  , reactiveSibling = require('../../utils/reactive-sibling')
  , _                = require('mano').i18n.bind('User Submitted')
  , _d = _;

module.exports = function () {
	var reqUploads = this.processingStep.requirementUploads.applicable;
	var nextReqUpload = reactiveSibling.next(reqUploads, this.document);
	var nextReqUploadUrl = nextReqUpload.map(function (nextReqUpload) {
		if (!nextReqUpload) return null;
		return nextReqUpload.docUrl;
	});
	var prevReqUpload = reactiveSibling.previous(reqUploads, this.document);
	var prevReqUploadUrl = prevReqUpload.map(function (nextReqUpload) {
		if (!prevReqUpload) return null;
		return prevReqUpload.docUrl;
	});

	return [div({ id: 'submitted-box', class: 'business-process-submitted-box' },
		div({ class: 'business-process-submitted-box-header' },
			div({ class: 'business-process-submitted-box-header-document-title' },
				_d(this.document.label,
					this.document.getTranslations())),
			div({ class: 'business-process-revision-box-controls' },
				_if(prevReqUpload,
					a({ href: prevReqUploadUrl,
						class: 'hint-optional hint-optional-left',
						'data-hint': _('Previous document') },
						i({ class: 'fa fa-angle-left' }))),
				_if(nextReqUpload,
					a({ href: nextReqUploadUrl,
						class: 'hint-optional hint-optional-left', 'data-hint': _('Next document') },
						i({ class: 'fa fa-angle-right' })))
					))),
		div({ id: 'user-document', class: 'business-process-submitted-selected-document' },
			div({ class: 'submitted-preview' },
				div({ id: 'document-preview', class: 'submitted-preview-document' },
					renderDocument(this.document)),
				div({ class: 'submitted-preview-user-data  entity-data-section-side' },
					renderDocumentHistory(this.document))))];
};
<commit_msg>replace processingStep by businessProcess<commit_after>// Document view

'use strict';

var renderDocument = require('../_business-process-document-preview')
  , renderDocumentHistory = require('../_business-process-revision-document-history')
  , reactiveSibling = require('../../utils/reactive-sibling')
  , _                = require('mano').i18n.bind('User Submitted')
  , _d = _;

module.exports = function () {
	var reqUploads = this.businessProcess.requirementUploads.applicable;
	var nextReqUpload = reactiveSibling.next(reqUploads, this.document);
	var nextReqUploadUrl = nextReqUpload.map(function (nextReqUpload) {
		if (!nextReqUpload) return null;
		return nextReqUpload.docUrl;
	});
	var prevReqUpload = reactiveSibling.previous(reqUploads, this.document);
	var prevReqUploadUrl = prevReqUpload.map(function (nextReqUpload) {
		if (!prevReqUpload) return null;
		return prevReqUpload.docUrl;
	});

	return [div({ id: 'submitted-box', class: 'business-process-submitted-box' },
		div({ class: 'business-process-submitted-box-header' },
			div({ class: 'business-process-submitted-box-header-document-title' },
				_d(this.document.label,
					this.document.getTranslations())),
			div({ class: 'business-process-revision-box-controls' },
				_if(prevReqUpload,
					a({ href: prevReqUploadUrl,
						class: 'hint-optional hint-optional-left',
						'data-hint': _('Previous document') },
						i({ class: 'fa fa-angle-left' }))),
				_if(nextReqUpload,
					a({ href: nextReqUploadUrl,
						class: 'hint-optional hint-optional-left', 'data-hint': _('Next document') },
						i({ class: 'fa fa-angle-right' })))
					))),
		div({ id: 'user-document', class: 'business-process-submitted-selected-document' },
			div({ class: 'submitted-preview' },
				div({ id: 'document-preview', class: 'submitted-preview-document' },
					renderDocument(this.document)),
				div({ class: 'submitted-preview-user-data  entity-data-section-side' },
					renderDocumentHistory(this.document))))];
};
<|endoftext|>


<commit_before>import React, {PropTypes} from 'react'
import FormBuilderPropTypes from '../../FormBuilderPropTypes'
import InInputButton from 'part:@sanity/components/buttons/in-input'
import InInputStyles from 'part:@sanity/components/buttons/in-input-style'
import DefaultFormField from 'part:@sanity/components/formfields/default'
import DefaultTextInput from 'part:@sanity/components/textinputs/default'
import {uniqueId, debounce} from 'lodash'
import Spinner from 'part:@sanity/components/loading/spinner'

// Fallback slugify function if not defined in factory function
// or in the type definition's options
function defaultSlugify(type, text) {
  return (text || '').toString().toLowerCase()
    .replace(/\s+/g, '-')           // Replace spaces with -
    .replace(/[^\w-]+/g, '')       // Remove all non-word chars
    .replace(/--+/g, '-')         // Replace multiple - with single -
    .substring(0, type.options.maxLength)
}

function tryPromise(fn) {
  return Promise.resolve().then(() => fn())
}

const makeCancelable = promise => {
  let hasCanceled_ = false

  const wrappedPromise = new Promise((resolve, reject) => {
    promise.then(val => {
      return hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
    })
    promise.catch(error => {
      return hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    })
  })

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled_ = true
    }
  }
}

const vanillaState = {
  validationError: null,
  inputText: undefined,
  loading: false
}

export default class SlugInput extends React.Component {
  static passDocument = true;

  static propTypes = {
    type: FormBuilderPropTypes.type.isRequired,
    level: PropTypes.number.isRequired,
    value: PropTypes.shape({
      current: PropTypes.string,
      auto: PropTypes.bool
    }),
    validation: PropTypes.shape({
      messages: PropTypes.array
    }),
    checkValidityFn: PropTypes.func,
    slugifyFn: PropTypes.func,
    document: PropTypes.object.isRequired,
    onChange: PropTypes.func
  };

  static defaultProps = {
    value: {current: undefined, auto: true},
    onChange() {},
    onEnter() {}
  };

  state = vanillaState

  constructor(props) {
    super(props)
    this.updateValueWithUniquenessCheck = debounce(
      this.updateValueWithUniquenessCheck.bind(this),
      500
    )
  }

  updateValue(value) {
    this.setState({loading: false})
    this.props.onChange({
      patch: {
        type: value ? 'set' : 'unset',
        path: [],
        value: value
      }
    })
  }

  updateValueWithUniquenessCheck(value) {
    const {type, checkValidityFn, document} = this.props
    const docId = document.getAttribute('_id').get()
    return makeCancelable(tryPromise(() => {
      if (!value.current) {
        this.updateValue(value)
        this.setState({loading: false, validationError: null})
        return Promise.resolve()
      }
      this.setState({loading: true, validationError: null})
      return checkValidityFn(type, value.current, docId)
    })).promise
      .then(validationError => {
        if (!validationError) {
          this.updateValue(value)
          this.setState({loading: false, validationError: null})
          return Promise.resolve()
        }
        const proposedNewCurrent = `${value.current}-1`
        const newVal = {current: proposedNewCurrent, auto: false}
        this.setState({
          loading: false,
          inputText: proposedNewCurrent,
          validationError: validationError.toString()
        })
        return this.updateValueWithUniquenessCheck(newVal)
      })
      .catch(err => {
        if (err.isCanceled) {
          return null
        }
        console.error(err) // eslint-disable-line no-console
        this.setState({
          loading: false,
          validationError: 'Got javascript error trying to validate the slug. '
            + 'See javascript console for more info.'
        })
        this.updateValue({current: value.current, auto: false})
        return Promise.resolve()
      })
  }

  slugify(text) {
    if (!text) {
      return text
    }
    const {type, slugifyFn} = this.props
    if (type.options.slugifyFn) {
      return type.options.slugifyFn(type, text, slugifyFn)
    }
    if (slugifyFn) {
      return this.props.slugifyFn(type, text)
    }
    return defaultSlugify(type, text)
  }

  componentWillReceiveProps(nextProps) {
    const {checkValidityFn} = this.props
    const {document, type, value} = nextProps

    // Reset state if document is changed
    const oldDocId = this.props.document.getAttribute('_id').get()
    const newDocId = document.getAttribute('_id').get()

    if (oldDocId !== newDocId) {
      this.setState(vanillaState)
    }
    let newCurrent
    if (value.auto) {
      const newFromSource = document.getAttribute(type.options.source).get()
      newCurrent = this.slugify(newFromSource)
    }
    if (!value.auto) {
      newCurrent = this.slugify(value.current)
    }

    if (newCurrent
        && (value.current !== newCurrent || value.auto !== this.props.value.auto)) {
      const newVal = {current: newCurrent, auto: value.auto}
      if (checkValidityFn) {
        this.updateValueWithUniquenessCheck(newVal)
        this.setState({inputText: newCurrent})
        return
      }
      this.updateValue(newVal)
    }
  }

  handleChange = event => {
    const {checkValidityFn, value} = this.props
    const newCurrent = event.target.value
      ? this.slugify(event.target.value.toString())
      : undefined
    this.setState({inputText: newCurrent})
    const newVal = {current: newCurrent, auto: value.auto}
    if (checkValidityFn) {
      this.updateValueWithUniquenessCheck(newVal)
      return
    }
    this.updateValue(newVal)
  }

  handleChangeButtonClick = event => {
    const {value} = this.props
    this.setState({inputText: this.state.validationError ? undefined : value.current})
    this.updateValue({current: value.current, auto: false})
  }

  handleAutoButtonClicked = event => {
    const {value} = this.props
    this.updateValue({current: value.current, auto: true})
  }

  render() {
    const {value, type, validation, level} = this.props
    const {loading, validationError, inputText} = this.state
    const formFieldProps = {
      label: type.title,
      description: type.description,
      validation: validation,
      level: level
    }

    const inputId = uniqueId('FormBuilderSlug')
    return (
      <DefaultFormField {...formFieldProps}>
        { validationError && (

{validationError}

        )}

          <DefaultTextInput
            id={inputId}
            disabled={value.auto}
            placeholder={type.placeholder}
            onChange={this.handleChange}
            value={inputText || value.current}
          />

            { loading && (
              <Spinner inline message="Loading…" />
            )}
            {
              value.auto && (
                <InInputButton onClick={this.handleChangeButtonClick}>
                  Edit
                </InInputButton>
              )
            }
            {
              !value.auto && (
                <InInputButton onClick={this.handleAutoButtonClicked}>
                  Auto
                </InInputButton>
              )
            }

      </DefaultFormField>
    )
  }
}
<commit_msg>[form-builder] Fix bug where slug field was autoupdated on mount<commit_after>import React, {PropTypes} from 'react'
import FormBuilderPropTypes from '../../FormBuilderPropTypes'
import InInputButton from 'part:@sanity/components/buttons/in-input'
import InInputStyles from 'part:@sanity/components/buttons/in-input-style'
import DefaultFormField from 'part:@sanity/components/formfields/default'
import DefaultTextInput from 'part:@sanity/components/textinputs/default'
import {isEqual, uniqueId, debounce} from 'lodash'
import Spinner from 'part:@sanity/components/loading/spinner'

// Fallback slugify function if not defined in factory function
// or in the type definition's options
function defaultSlugify(type, text) {
  return (text || '').toString().toLowerCase()
    .replace(/\s+/g, '-')           // Replace spaces with -
    .replace(/[^\w-]+/g, '')       // Remove all non-word chars
    .replace(/--+/g, '-')         // Replace multiple - with single -
    .substring(0, type.options.maxLength)
}

function tryPromise(fn) {
  return Promise.resolve().then(() => fn())
}

const makeCancelable = promise => {
  let hasCanceled_ = false

  const wrappedPromise = new Promise((resolve, reject) => {
    promise.then(val => {
      return hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
    })
    promise.catch(error => {
      return hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    })
  })

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled_ = true
    }
  }
}

const vanillaState = {
  validationError: null,
  inputText: undefined,
  loading: false
}

export default class SlugInput extends React.Component {
  static passDocument = true;

  static propTypes = {
    type: FormBuilderPropTypes.type.isRequired,
    level: PropTypes.number.isRequired,
    value: PropTypes.shape({
      current: PropTypes.string,
      auto: PropTypes.bool
    }),
    validation: PropTypes.shape({
      messages: PropTypes.array
    }),
    checkValidityFn: PropTypes.func,
    slugifyFn: PropTypes.func,
    document: PropTypes.object.isRequired,
    onChange: PropTypes.func
  };

  static defaultProps = {
    value: {current: undefined, auto: true},
    onChange() {},
    onEnter() {}
  };

  state = vanillaState

  constructor(props) {
    super(props)
    this.updateValueWithUniquenessCheck = debounce(
      this.updateValueWithUniquenessCheck.bind(this),
      500
    )
  }

  updateValue(value) {
    this.setState({loading: false})
    this.props.onChange({
      patch: {
        type: value ? 'set' : 'unset',
        path: [],
        value: value
      }
    })
  }

  updateValueWithUniquenessCheck(value) {
    const {type, checkValidityFn, document} = this.props
    const docId = document.getAttribute('_id').get()
    return makeCancelable(tryPromise(() => {
      if (!value.current) {
        this.updateValue(value)
        this.setState({loading: false, validationError: null})
        return Promise.resolve()
      }
      this.setState({loading: true, validationError: null})
      return checkValidityFn(type, value.current, docId)
    })).promise
      .then(validationError => {
        if (!validationError) {
          this.updateValue(value)
          this.setState({loading: false, validationError: null})
          return Promise.resolve()
        }
        const proposedNewCurrent = `${value.current}-1`
        const newVal = {current: proposedNewCurrent, auto: false}
        this.setState({
          loading: false,
          inputText: proposedNewCurrent,
          validationError: validationError.toString()
        })
        return this.updateValueWithUniquenessCheck(newVal)
      })
      .catch(err => {
        if (err.isCanceled) {
          return null
        }
        console.error(err) // eslint-disable-line no-console
        this.setState({
          loading: false,
          validationError: 'Got javascript error trying to validate the slug. '
            + 'See javascript console for more info.'
        })
        this.updateValue({current: value.current, auto: false})
        return Promise.resolve()
      })
  }

  slugify(text) {
    if (!text) {
      return text
    }
    const {type, slugifyFn} = this.props
    if (type.options.slugifyFn) {
      return type.options.slugifyFn(type, text, slugifyFn)
    }
    if (slugifyFn) {
      return this.props.slugifyFn(type, text)
    }
    return defaultSlugify(type, text)
  }

  componentWillReceiveProps(nextProps) {
    const {checkValidityFn} = this.props
    const {document, type, value} = nextProps

    // Reset state if document is changed
    const oldDocId = this.props.document.getAttribute('_id').get()
    const newDocId = document.getAttribute('_id').get()
    if (oldDocId !== newDocId) {
      this.setState(vanillaState)
      return
    }

    // If slug is set to auto and the source field has changed,
    // verify and set the new slug if it is different from the current one
    let newCurrent
    if (value.auto) {
      const newFromSource = document.getAttribute(type.options.source).get()
      newCurrent = this.slugify(newFromSource)
    }

    if (newCurrent && newCurrent !== value.current) {
      const newVal = {current: newCurrent, auto: value.auto}
      if (checkValidityFn) {
        this.updateValueWithUniquenessCheck(newVal)
        this.setState({inputText: newCurrent})
        return
      }
      this.updateValue(newVal)
    }
  }

  handleChange = event => {
    const {checkValidityFn, value} = this.props
    const newCurrent = event.target.value
      ? this.slugify(event.target.value.toString())
      : undefined
    this.setState({inputText: newCurrent})
    const newVal = {current: newCurrent, auto: value.auto}
    if (checkValidityFn) {
      this.updateValueWithUniquenessCheck(newVal)
      return
    }
    this.updateValue(newVal)
  }

  handleChangeButtonClick = event => {
    const {value} = this.props
    this.setState({inputText: this.state.validationError ? undefined : value.current})
    this.updateValue({current: value.current, auto: false})
  }

  handleAutoButtonClicked = event => {
    const {value} = this.props
    this.updateValue({current: value.current, auto: true})
  }

  render() {
    const {value, type, validation, level} = this.props
    const {loading, validationError, inputText} = this.state
    const formFieldProps = {
      label: type.title,
      description: type.description,
      validation: validation,
      level: level
    }

    const inputId = uniqueId('FormBuilderSlug')
    return (
      <DefaultFormField {...formFieldProps}>
        { validationError && (

{validationError}

        )}

          <DefaultTextInput
            id={inputId}
            disabled={value.auto}
            placeholder={type.placeholder}
            onChange={this.handleChange}
            value={inputText || value.current}
          />

            { loading && (
              <Spinner inline message="Loading…" />
            )}
            {
              value.auto && (
                <InInputButton onClick={this.handleChangeButtonClick}>
                  Edit
                </InInputButton>
              )
            }
            {
              !value.auto && (
                <InInputButton onClick={this.handleAutoButtonClicked}>
                  Auto
                </InInputButton>
              )
            }

      </DefaultFormField>
    )
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2016 Teppo Kurki <teppo.kurki@iki.fi>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

const _ = require('lodash')
const Bacon = require('baconjs');

const debug = require('debug')('signalk-server:subscriptionmanager')

function SubscriptionManager(app) {
  this.streambundle = app.streambundle
  this.selfContext = app.selfContext
}

SubscriptionManager.prototype.subscribe = function(command, unsubscribes, errorCallback, callback) {
  const contextFilter = contextMatcher(this.selfContext, command)
  if (Array.isArray(command.subscribe)) {
    handleSubscribeRows(command.subscribe, unsubscribes, this.streambundle.buses, contextFilter, callback, errorCallback)
    //listen to new keys and then use the same logic to check if we
    //want to subscribe, passing in a map with just that single bus
    unsubscribes.push(this.streambundle.keys.onValue(key => {
      var buses = {}
      buses[key] = this.streambundle.getBus(key)
      handleSubscribeRows(command.subscribe, unsubscribes, buses, contextFilter, callback, errorCallback)
    }))
  }
}

function handleSubscribeRows(rows, unsubscribes, buses, filter, callback, errorCallback) {
  rows.reduce((acc, subscribeRow) => {
    if (subscribeRow.path) {
      handleSubscribeRow(subscribeRow, unsubscribes, buses, filter, callback, errorCallback);
    }
    return acc
  }, unsubscribes)
}

function handleSubscribeRow(subscribeRow, unsubscribes, buses, filter, callback, errorCallback) {
  var matcher = pathMatcher(subscribeRow.path);
  //iterate over all the buses, checking if we want to subscribe to its values
  _.forOwn(buses, (bus, key) => {
    if (matcher(key)) {
      debug("Subscribing to key " + key)
      var filteredBus = bus.filter(filter)
      if (subscribeRow.minPeriod) {
        if (subscribeRow.policy && subscribeRow.policy != 'instant') {
          errorCallback("minPeriod assumes policy 'instant', ignoring " + subscribeRow.policy)
        }
        debug("minPeriod:" + subscribeRow.minPeriod)
        filteredBus = filteredBus.debounceImmediate(subscribeRow.minPeriod)
      } else if (subscribeRow.period || (subscribeRow.policy && subscribeRow.policy === 'fixed')) {
        if (subscribeRow.policy && subscribeRow.policy != 'fixed') {
          errorCallback("period assumes policy 'fixed', ignoring " + subscribeRow.policy)
        } else {
          const interval = subscribeRow.period || 1000
          //this may leak setIntervals underneath the hood
          filteredBus = filteredBus.sampledBy(Bacon.interval(interval, true))
        }
      }
      if (subscribeRow.format && subscribeRow.format != 'delta') {
        errorCallback("Only delta format supported, using it")
      }
      if (subscribeRow.policy && !['instant', 'fixed'].some(s => s === subscribeRow.policy)) {
        errorCallback("Only 'instant' and 'fixed' policies supported, ignoring " + subscribeRow.policy)
      }
      unsubscribes.push(filteredBus.map(toDelta).onValue(callback))
    }
  })}

function pathMatcher(path) {
  var pattern = path.replace('.', '\\.').replace('*', '.*')
  var matcher = new RegExp('^' + pattern + '$')
  return aPath => matcher.test(aPath)
}

function contextMatcher(selfContext, subscribeCommand) {
  debug("subscribeCommand:" + JSON.stringify(subscribeCommand))
  var pattern = subscribeCommand.context.replace('.', '\\.').replace('*', '.*')
  var matcher = new RegExp('^' + pattern + '$')
  if (subscribeCommand.context) {
    return normalizedDeltaData => matcher.test(normalizedDeltaData.context) || ((subscribeCommand.context === 'vessels.self' || subscribeCommand.context === 'self') && normalizedDeltaData.context === selfContext)
  }
  return x => true
}

function toDelta(normalizedDeltaData) {
  return {
    context: normalizedDeltaData.context,
    updates: [
      {
        source: normalizedDeltaData.source,
        '$source': normalizedDeltaData['$source'],
        timestamp: normalizedDeltaData.timestamp,
        values: [
          {
            path: normalizedDeltaData.path,
            value: normalizedDeltaData.value
          }
        ]
      }
    ]
  }
}

module.exports = SubscriptionManager;
<commit_msg>Fix: do not lose values when subscribing to * context with fixed period<commit_after>/*
 * Copyright 2016 Teppo Kurki <teppo.kurki@iki.fi>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

const _ = require('lodash')
const Bacon = require('baconjs');

const debug = require('debug')('signalk-server:subscriptionmanager')

function SubscriptionManager(app) {
  this.streambundle = app.streambundle
  this.selfContext = app.selfContext
}

SubscriptionManager.prototype.subscribe = function(command, unsubscribes, errorCallback, callback) {
  const contextFilter = contextMatcher(this.selfContext, command)
  if (Array.isArray(command.subscribe)) {
    handleSubscribeRows(command.subscribe, unsubscribes, this.streambundle.buses, contextFilter, callback, errorCallback)
    //listen to new keys and then use the same logic to check if we
    //want to subscribe, passing in a map with just that single bus
    unsubscribes.push(this.streambundle.keys.onValue(key => {
      var buses = {}
      buses[key] = this.streambundle.getBus(key)
      handleSubscribeRows(command.subscribe, unsubscribes, buses, contextFilter, callback, errorCallback)
    }))
  }
}

function handleSubscribeRows(rows, unsubscribes, buses, filter, callback, errorCallback) {
  rows.reduce((acc, subscribeRow) => {
    if (subscribeRow.path) {
      handleSubscribeRow(subscribeRow, unsubscribes, buses, filter, callback, errorCallback);
    }
    return acc
  }, unsubscribes)
}

function handleSubscribeRow(subscribeRow, unsubscribes, buses, filter, callback, errorCallback) {
  var matcher = pathMatcher(subscribeRow.path);
  //iterate over all the buses, checking if we want to subscribe to its values
  _.forOwn(buses, (bus, key) => {
    if (matcher(key)) {
      debug("Subscribing to key " + key)
      var filteredBus = bus.filter(filter)
      if (subscribeRow.minPeriod) {
        if (subscribeRow.policy && subscribeRow.policy != 'instant') {
          errorCallback("minPeriod assumes policy 'instant', ignoring policy " + subscribeRow.policy)
        }
        debug("minPeriod:" + subscribeRow.minPeriod)
        filteredBus = filteredBus.debounceImmediate(subscribeRow.minPeriod)
      } else if (subscribeRow.period || (subscribeRow.policy && subscribeRow.policy === 'fixed')) {
        if (subscribeRow.policy && subscribeRow.policy != 'fixed') {
          errorCallback("period assumes policy 'fixed', ignoring policy " + subscribeRow.policy)
        } else {
          const interval = subscribeRow.period || 1000
          filteredBus = filteredBus.bufferWithTime(interval)
            .flatMapLatest(bufferedValues => {
              const uniqueValues = _(bufferedValues)
                .reverse()
                .uniqBy(value => value.context + ":" + value.$source + ":" + value.path)
                .value()
              return Bacon.fromArray(uniqueValues)
            })
        }
      }
      if (subscribeRow.format && subscribeRow.format != 'delta') {
        errorCallback("Only delta format supported, using it")
      }
      if (subscribeRow.policy && !['instant', 'fixed'].some(s => s === subscribeRow.policy)) {
        errorCallback("Only 'instant' and 'fixed' policies supported, ignoring policy " + subscribeRow.policy)
      }
      unsubscribes.push(filteredBus.map(toDelta).onValue(callback))
    }
  })}

function pathMatcher(path) {
  var pattern = path.replace('.', '\\.').replace('*', '.*')
  var matcher = new RegExp('^' + pattern + '$')
  return aPath => matcher.test(aPath)
}

function contextMatcher(selfContext, subscribeCommand) {
  debug("subscribeCommand:" + JSON.stringify(subscribeCommand))
  var pattern = subscribeCommand.context.replace('.', '\\.').replace('*', '.*')
  var matcher = new RegExp('^' + pattern + '$')
  if (subscribeCommand.context) {
    return normalizedDeltaData => matcher.test(normalizedDeltaData.context) || ((subscribeCommand.context === 'vessels.self' || subscribeCommand.context === 'self') && normalizedDeltaData.context === selfContext)
  }
  return x => true
}

function toDelta(normalizedDeltaData) {
  return {
    context: normalizedDeltaData.context,
    updates: [
      {
        source: normalizedDeltaData.source,
        '$source': normalizedDeltaData['$source'],
        timestamp: normalizedDeltaData.timestamp,
        values: [
          {
            path: normalizedDeltaData.path,
            value: normalizedDeltaData.value
          }
        ]
      }
    ]
  }
}

module.exports = SubscriptionManager;
<|endoftext|>


<commit_before>// @flow

import { type MediaInfo, mediaInfoPropType } from 'lib/types/media-types';
import type { ImageStyle } from '../types/styles';
import {
  type Navigate,
  MultimediaModalRouteName,
  MultimediaTooltipModalRouteName,
} from '../navigation/route-names';
import {
  type VerticalBounds,
  verticalBoundsPropType,
} from '../types/lightbox-types';
import {
  type PendingMultimediaUpload,
  pendingMultimediaUploadPropType,
} from './chat-input-state';

import * as React from 'react';
import PropTypes from 'prop-types';
import { View, StyleSheet } from 'react-native';
import Animated from 'react-native-reanimated';
import { KeyboardUtils } from 'react-native-keyboard-input';
import invariant from 'invariant';

import InlineMultimedia from './inline-multimedia.react';
import { saveImage } from '../media/save-image';

type Props = {|
  mediaInfo: MediaInfo,
  navigate: Navigate,
  verticalBounds: ?VerticalBounds,
  verticalOffset: number,
  style: ImageStyle,
  modalsClosed: bool,
  lightboxPosition: ?Animated.Value,
  postInProgress: bool,
  pendingUpload: ?PendingMultimediaUpload,
  keyboardShowing: bool,
  messageFocused: bool,
  toggleMessageFocus: (messageKey: string) => void,
|};
type State = {|
  hidden: bool,
  opacity: ?Animated.Value,
|};
class MultimediaMessageMultimedia extends React.PureComponent<Props, State> {

  static propTypes = {
    mediaInfo: mediaInfoPropType.isRequired,
    navigate: PropTypes.func.isRequired,
    verticalBounds: verticalBoundsPropType,
    verticalOffset: PropTypes.number.isRequired,
    modalsClosed: PropTypes.bool.isRequired,
    lightboxPosition: PropTypes.instanceOf(Animated.Value),
    postInProgress: PropTypes.bool.isRequired,
    pendingUpload: pendingMultimediaUploadPropType,
    keyboardShowing: PropTypes.bool.isRequired,
    messageFocused: PropTypes.bool.isRequired,
    toggleMessageFocus: PropTypes.func.isRequired,
  };
  view: ?View;
  clickable = true;

  constructor(props: Props) {
    super(props);
    this.state = {
      hidden: false,
      opacity: this.getOpacity(),
    };
  }

  static getDerivedStateFromProps(props: Props, state: State) {
    if (props.modalsClosed && state.hidden) {
      return { hidden: false };
    }
    return null;
  }

  getOpacity() {
    const { lightboxPosition } = this.props;
    if (!lightboxPosition) {
      return null;
    }
    return Animated.interpolate(
      this.props.lightboxPosition,
      {
        inputRange: [ 0.1, 0.11 ],
        outputRange: [ 1, 0 ],
        extrapolate: Animated.Extrapolate.CLAMP,
      },
    );
  }

  componentDidUpdate(prevProps: Props) {
    if (this.props.lightboxPosition !== prevProps.lightboxPosition) {
      this.setState({ opacity: this.getOpacity() });
    }

    if (this.props.modalsClosed && !prevProps.modalsClosed) {
      this.clickable = true;
    }
  }

  render() {
    const wrapperStyles = [ styles.expand ];
    if (this.state.hidden && this.state.opacity) {
      wrapperStyles.push({ opacity: this.state.opacity });
    }

    const { mediaInfo, pendingUpload, style, postInProgress } = this.props;
    return (
      <Animated.View style={wrapperStyles}>
        <View style={styles.expand} ref={this.viewRef}>
          <InlineMultimedia
            mediaInfo={this.props.mediaInfo}
            style={this.props.style}
            onPress={this.onPress}
            onLongPress={this.onLongPress}
            postInProgress={this.props.postInProgress}
            pendingUpload={this.props.pendingUpload}
          />
        </View>
      </Animated.View>
    );
  }

  viewRef = (view: ?View) => {
    this.view = view;
  }

  onPress = () => {
    const { view, props: { verticalBounds } } = this;
    if (!view || !verticalBounds) {
      return;
    }

    if (!this.clickable) {
      return;
    }
    this.clickable = false;

    view.measure((x, y, width, height, pageX, pageY) => {
      const coordinates = { x: pageX, y: pageY, width, height };
      const { mediaInfo, navigate } = this.props;
      navigate({
        routeName: MultimediaModalRouteName,
        params: { mediaInfo, initialCoordinates: coordinates, verticalBounds },
      });
      this.setState({ hidden: true });
    });
  }

  onLongPress = () => {
    if (this.props.keyboardShowing) {
      KeyboardUtils.dismiss();
      return;
    }

    const { view, props: { verticalBounds, verticalOffset } } = this;
    if (!view || !verticalBounds) {
      return;
    }

    if (!this.clickable) {
      return;
    }
    this.clickable = false;

    if (!this.props.messageFocused) {
      this.props.toggleMessageFocus(this.props.mediaInfo.messageKey);
    }

    view.measure((x, y, width, height, pageX, pageY) => {
      const coordinates = { x: pageX, y: pageY, width, height };
      const { mediaInfo, navigate } = this.props;
      navigate({
        routeName: MultimediaTooltipModalRouteName,
        params: {
          mediaInfo,
          initialCoordinates: coordinates,
          verticalOffset,
          verticalBounds,
        },
      });
    });
  }

}

const styles = StyleSheet.create({
  expand: {
    flex: 1,
  },
});

export default MultimediaMessageMultimedia;
<commit_msg>[native] Fix Multimedia layout measuring on Android<commit_after>// @flow

import { type MediaInfo, mediaInfoPropType } from 'lib/types/media-types';
import type { ImageStyle } from '../types/styles';
import {
  type Navigate,
  MultimediaModalRouteName,
  MultimediaTooltipModalRouteName,
} from '../navigation/route-names';
import {
  type VerticalBounds,
  verticalBoundsPropType,
} from '../types/lightbox-types';
import {
  type PendingMultimediaUpload,
  pendingMultimediaUploadPropType,
} from './chat-input-state';

import * as React from 'react';
import PropTypes from 'prop-types';
import { View, StyleSheet } from 'react-native';
import Animated from 'react-native-reanimated';
import { KeyboardUtils } from 'react-native-keyboard-input';
import invariant from 'invariant';

import InlineMultimedia from './inline-multimedia.react';
import { saveImage } from '../media/save-image';

type Props = {|
  mediaInfo: MediaInfo,
  navigate: Navigate,
  verticalBounds: ?VerticalBounds,
  verticalOffset: number,
  style: ImageStyle,
  modalsClosed: bool,
  lightboxPosition: ?Animated.Value,
  postInProgress: bool,
  pendingUpload: ?PendingMultimediaUpload,
  keyboardShowing: bool,
  messageFocused: bool,
  toggleMessageFocus: (messageKey: string) => void,
|};
type State = {|
  hidden: bool,
  opacity: ?Animated.Value,
|};
class MultimediaMessageMultimedia extends React.PureComponent<Props, State> {

  static propTypes = {
    mediaInfo: mediaInfoPropType.isRequired,
    navigate: PropTypes.func.isRequired,
    verticalBounds: verticalBoundsPropType,
    verticalOffset: PropTypes.number.isRequired,
    modalsClosed: PropTypes.bool.isRequired,
    lightboxPosition: PropTypes.instanceOf(Animated.Value),
    postInProgress: PropTypes.bool.isRequired,
    pendingUpload: pendingMultimediaUploadPropType,
    keyboardShowing: PropTypes.bool.isRequired,
    messageFocused: PropTypes.bool.isRequired,
    toggleMessageFocus: PropTypes.func.isRequired,
  };
  view: ?View;
  clickable = true;

  constructor(props: Props) {
    super(props);
    this.state = {
      hidden: false,
      opacity: this.getOpacity(),
    };
  }

  static getDerivedStateFromProps(props: Props, state: State) {
    if (props.modalsClosed && state.hidden) {
      return { hidden: false };
    }
    return null;
  }

  getOpacity() {
    const { lightboxPosition } = this.props;
    if (!lightboxPosition) {
      return null;
    }
    return Animated.interpolate(
      this.props.lightboxPosition,
      {
        inputRange: [ 0.1, 0.11 ],
        outputRange: [ 1, 0 ],
        extrapolate: Animated.Extrapolate.CLAMP,
      },
    );
  }

  componentDidUpdate(prevProps: Props) {
    if (this.props.lightboxPosition !== prevProps.lightboxPosition) {
      this.setState({ opacity: this.getOpacity() });
    }

    if (this.props.modalsClosed && !prevProps.modalsClosed) {
      this.clickable = true;
    }
  }

  render() {
    const wrapperStyles = [ styles.expand ];
    if (this.state.hidden && this.state.opacity) {
      wrapperStyles.push({ opacity: this.state.opacity });
    }

    const { mediaInfo, pendingUpload, style, postInProgress } = this.props;
    return (
      <Animated.View style={wrapperStyles}>
        <View style={styles.expand} onLayout={this.onLayout} ref={this.viewRef}>
          <InlineMultimedia
            mediaInfo={this.props.mediaInfo}
            style={this.props.style}
            onPress={this.onPress}
            onLongPress={this.onLongPress}
            postInProgress={this.props.postInProgress}
            pendingUpload={this.props.pendingUpload}
          />
        </View>
      </Animated.View>
    );
  }

  onLayout = () => {}

  viewRef = (view: ?View) => {
    this.view = view;
  }

  onPress = () => {
    const { view, props: { verticalBounds } } = this;
    if (!view || !verticalBounds) {
      return;
    }

    if (!this.clickable) {
      return;
    }
    this.clickable = false;

    view.measure((x, y, width, height, pageX, pageY) => {
      const coordinates = { x: pageX, y: pageY, width, height };
      const { mediaInfo, navigate } = this.props;
      navigate({
        routeName: MultimediaModalRouteName,
        params: { mediaInfo, initialCoordinates: coordinates, verticalBounds },
      });
      this.setState({ hidden: true });
    });
  }

  onLongPress = () => {
    if (this.props.keyboardShowing) {
      KeyboardUtils.dismiss();
      return;
    }

    const { view, props: { verticalBounds, verticalOffset } } = this;
    if (!view || !verticalBounds) {
      return;
    }

    if (!this.clickable) {
      return;
    }
    this.clickable = false;

    if (!this.props.messageFocused) {
      this.props.toggleMessageFocus(this.props.mediaInfo.messageKey);
    }

    view.measure((x, y, width, height, pageX, pageY) => {
      const coordinates = { x: pageX, y: pageY, width, height };
      const { mediaInfo, navigate } = this.props;
      navigate({
        routeName: MultimediaTooltipModalRouteName,
        params: {
          mediaInfo,
          initialCoordinates: coordinates,
          verticalOffset,
          verticalBounds,
        },
      });
    });
  }

}

const styles = StyleSheet.create({
  expand: {
    flex: 1,
  },
});

export default MultimediaMessageMultimedia;
<|endoftext|>


<commit_before>
// default delete form
Util.Objects["formDefaultDelete"] = new function() {
	this.init = function(form) {
//		u.bug("formDefaultDelete init:" + u.nodeId(form));

		u.f.init(form);

		var bn_delete = u.qs("input.delete", form);
		if(bn_delete) {

			u.e.click(bn_delete);
			bn_delete.restore = function(event) {
				this.value = "Delete";
				u.rc(this, "confirm");
			}

			bn_delete.inputStarted = function(event) {
				u.e.kill(event);
			}

			bn_delete.clicked = function(event) {
				u.e.kill(event);

				// first click
				if(!u.hc(this, "confirm")) {
					u.ac(this, "confirm");
					this.value = "Confirm";
					this.t_confirm = u.t.setTimer(this, this.restore, 3000);
				}
				// confirm click
				else {
					u.t.resetTimer(this.t_confirm);

					this.response = function(response) {
						if(response.cms_status == "success") {
							// check for constraint error preventing row from actually being deleted
							if(response.cms_object && response.cms_object.constraint_error) {
								page.notify(response.cms_message);
								this.value = "Delete";
								u.ac(this, "disabled");
							}
							else {
								location.reload();
							}
//							location.href = this.actions["cancel"].url;
						}
						else {
							page.notify(response.cms_message);
						}
					}
					u.request(this, this.form.action, {"method":"post", "params" : u.f.getParams(this.form)});
				}
			}
		}

	}
}<commit_msg>dynamic delete names<commit_after>
// default delete form
Util.Objects["formDefaultDelete"] = new function() {
	this.init = function(form) {
//		u.bug("formDefaultDelete init:" + u.nodeId(form));

		u.f.init(form);

		var bn_delete = u.qs("input.delete", form);
		if(bn_delete) {

			bn_delete.org_value = bn_delete.value;

			u.e.click(bn_delete);
			bn_delete.restore = function(event) {
				this.value = this.org_value;
				u.rc(this, "confirm");
			}

			bn_delete.inputStarted = function(event) {
				u.e.kill(event);
			}

			bn_delete.clicked = function(event) {
				u.e.kill(event);

				// first click
				if(!u.hc(this, "confirm")) {
					u.ac(this, "confirm");
					this.value = "Confirm";
					this.t_confirm = u.t.setTimer(this, this.restore, 3000);
				}
				// confirm click
				else {
					u.t.resetTimer(this.t_confirm);

					this.response = function(response) {
						if(response.cms_status == "success") {
							// check for constraint error preventing row from actually being deleted
							if(response.cms_object && response.cms_object.constraint_error) {
								page.notify(response.cms_message);
								this.value = this.org_value;
								u.ac(this, "disabled");
							}
							else {
								location.reload();
							}
//							location.href = this.actions["cancel"].url;
						}
						else {
							page.notify(response.cms_message);
						}
					}
					u.request(this, this.form.action, {"method":"post", "params" : u.f.getParams(this.form)});
				}
			}
		}

	}
}<|endoftext|>