<commit_before>var startMenu = function(game) {
    return {
        preload: function() {
            game.stage.backgroundColor = "#aaaaaa";
            this.game.scale.pageAlignHorizontally = true;
            this.game.scale.pageAlignVertically = true;
            this.game.scale.refresh();

            this.centeredText("Russian Judge Redemption", 20);
            this.loadingText = this.centeredText("Loading", game.world.centerY);
            game.load.image('floor', 'assets/floorTile/draftFloor.png');
            game.load.image('judgeResponseGood', 'assets/judgeResponseGood.jpg');
            game.load.image('judgeResponseBad', 'assets/judgeResponseBad.jpg');
            game.load.image('feedbackResultGood', 'assets/vaultBoyGood.jpg');
            game.load.image('feedbackResultBad', 'assets/vaultBoyBad.jpg');
            game.load.image('meterArrow', 'assets/meter-arrow.png');
            game.load.image('voteScoreBar', 'assets/vote-score-bar.png');
            game.load.image('russianJudge', 'assets/judge/russianJudge/normalSprite.png');
            game.load.image('draftJudge', 'assets/judge/draftJudge/normalSprite.png');
            game.load.image('judge1', 'assets/judge/1Judge/normalSprite.png');
            game.load.image('judge2', 'assets/judge/2Judge/normalSprite.png');

            game.load.image('dancer-head', 'assets/dancer/draftDancer/head.png');
            game.load.image('dancer-leftArm', 'assets/dancer/draftDancer/leftArm.png');
            game.load.image('dancer-rightArm', 'assets/dancer/draftDancer/rightArm.png');
            game.load.image('dancer-leftHand', 'assets/dancer/draftDancer/leftHand.png');
            game.load.image('dancer-rightHand', 'assets/dancer/draftDancer/rightHand.png');
            game.load.image('dancer-leftFoot', 'assets/dancer/draftDancer/leftFoot.png');
            game.load.image('dancer-rightFoot', 'assets/dancer/draftDancer/rightFoot.png');
            game.load.image('dancer-leftLeg', 'assets/dancer/draftDancer/leftLeg.png');
            game.load.image('dancer-rightLeg', 'assets/dancer/draftDancer/rightLeg.png');
            game.load.image('dancer-torso', 'assets/dancer/draftDancer/torso.png');

            game.load.onLoadComplete.add(function() {
                this.loadingText.destroy();
                var startButton = this.centeredText("Start Judging", game.world.centerY);
                startButton.inputEnabled = true;
                startButton.events.onInputDown.add(
                    function() {
                        game.state.start('dancing');
                    });
            }, this);
        },
        centeredText: function(text, y) {
            text = game.add.text(game.world.centerX, y, text);
            text.anchor.setTo(0.5, 0.5);
            return text;
        },
    };
};

game.state.add('startMenu', startMenu);
<commit_msg>convenience for devving<commit_after>var startMenu = function(game) {
    return {
        preload: function() {
            game.stage.backgroundColor = "#aaaaaa";
            this.game.scale.pageAlignHorizontally = true;
            this.game.scale.pageAlignVertically = true;
            this.game.scale.refresh();

            this.centeredText("Russian Judge Redemption", 20);
            this.loadingText = this.centeredText("Loading", game.world.centerY);
            game.load.image('floor', 'assets/floorTile/draftFloor.png');
            game.load.image('judgeResponseGood', 'assets/judgeResponseGood.jpg');
            game.load.image('judgeResponseBad', 'assets/judgeResponseBad.jpg');
            game.load.image('feedbackResultGood', 'assets/vaultBoyGood.jpg');
            game.load.image('feedbackResultBad', 'assets/vaultBoyBad.jpg');
            game.load.image('meterArrow', 'assets/meter-arrow.png');
            game.load.image('voteScoreBar', 'assets/vote-score-bar.png');
            game.load.image('russianJudge', 'assets/judge/russianJudge/normalSprite.png');
            game.load.image('draftJudge', 'assets/judge/draftJudge/normalSprite.png');
            game.load.image('judge1', 'assets/judge/1Judge/normalSprite.png');
            game.load.image('judge2', 'assets/judge/2Judge/normalSprite.png');

            game.load.image('dancer-head', 'assets/dancer/draftDancer/head.png');
            game.load.image('dancer-leftArm', 'assets/dancer/draftDancer/leftArm.png');
            game.load.image('dancer-rightArm', 'assets/dancer/draftDancer/rightArm.png');
            game.load.image('dancer-leftHand', 'assets/dancer/draftDancer/leftHand.png');
            game.load.image('dancer-rightHand', 'assets/dancer/draftDancer/rightHand.png');
            game.load.image('dancer-leftFoot', 'assets/dancer/draftDancer/leftFoot.png');
            game.load.image('dancer-rightFoot', 'assets/dancer/draftDancer/rightFoot.png');
            game.load.image('dancer-leftLeg', 'assets/dancer/draftDancer/leftLeg.png');
            game.load.image('dancer-rightLeg', 'assets/dancer/draftDancer/rightLeg.png');
            game.load.image('dancer-torso', 'assets/dancer/draftDancer/torso.png');

            game.load.onLoadComplete.add(function() {
                // TODO kill this so you have an actual start menu
                game.state.start('dancing');

                this.loadingText.destroy();
                var startButton = this.centeredText("Start Judging", game.world.centerY);
                startButton.inputEnabled = true;
                startButton.events.onInputDown.add(
                    function() {
                        game.state.start('dancing');
                    });
            }, this);
        },
        centeredText: function(text, y) {
            text = game.add.text(game.world.centerX, y, text);
            text.anchor.setTo(0.5, 0.5);
            return text;
        },
    };
};

game.state.add('startMenu', startMenu);
<|endoftext|>


<commit_before>/*
 * Table Filter (for jQuery)
 * version: 0.1.0 (Oct. 18, 2012)
 *
 * Licensed under the MIT:
 * http://www.opensource.org/licenses/mit-license.php
 *
 * Copyright 2012 Efe Amadasun [ efeamadasun@gmail.com ]
 *
 * USAGE:
 *
 * <input type="text" class="f_txt"/>
 *
...

 *
 * $(function(){
 * 	  $(".f_txt").table_filter({'table':'.f_tbl'});
 * });
 *
 * ADDITIONAL SETTINGS:
 *
 * filter_inverse (boolean) - default: False
 * True - filters out rows that match the filter text
 * False - filters out rows that do not match the filter text
 *
 * enable_space (boolean) - default: False
 * True - it uses space in filter text as delimiters. e.g. if filter text = "good boy", it
 *        will search rows for "good" and "boy" seperately
 * False - it will not use space as a delimiter. e.g. "good boy" will be treated as one word.
 *
 * cell_selector (string) - default: 'td'
 * Allows override of the cell selector, so that only certain cells will be filtered.
 * Example setting to only filter on the first column: {'cell_selector':'td:first-child'}
 *
 */

(function($){

	$.fn.table_filter = function (options) {

		//set default plugin values
		var settings = $.extend({

			'filter_inverse': false,
			'enable_space': false,
			'table': '',
			'cell_selector': 'td'

		}, options);

		//return element, to maintain chainability
		return this.each(function () {
			var $this = $(this);

			$this.bind("keyup", function () {

				//set filter text, and filterable table rows
				var txt = $this.val().toLowerCase();
				var obj = $(settings.table).find("tr:not(:has('th'))");

				$.each(obj, function () {
					//default visibilty for rows is set based on filter_inverse value
					var show_tr = (settings.filter_inverse) ? true : false;
					var inner_obj = $(this).find(settings.cell_selector);

					$.each(inner_obj, function () {
						var td_txt = $.trim($(this).text()).toLowerCase();

						//if space is enabled as a delimiter, split the TD text value
						//and check the individual values against the filter text.
						if(settings.enable_space){

							var td_array = txt.split(" ");
							$.each(td_array, function (i) {
								var td_value = td_array[i];

								if(td_txt.indexOf(td_value) != -1){
									show_tr = (settings.filter_inverse) ? false : true;
								}
							});

						}
						else{

							if(td_txt.indexOf(txt) != -1){
								show_tr = (settings.filter_inverse) ? false : true;
							}

						}

					});

					if(show_tr){
						$(this).show();
					}
					else{
						$(this).hide();
					}

				});

				//display all rows if filter text is empty
				if($.trim(txt) == ""){
					$(settings.table).find("tr").show();
				}

			});

		});

	};

})(jQuery);
<commit_msg>Refactor table filtering script to use setoddeven and generally suit us better.<commit_after>/*
 * Table Filter (for jQuery)
 * version: 0.1.0 (Oct. 18, 2012)
 *
 * Licensed under the MIT:
 * http://www.opensource.org/licenses/mit-license.php
 *
 * Copyright 2012 Efe Amadasun [ efeamadasun@gmail.com ]
 *
 * USAGE:
 *
 *

 * <input type="text" class="table-filter"/>
 *
...

 * </class>
 *
 * $(function(){
 *   $(".f_tbl").table_filter();
 * });
 *
 * ADDITIONAL SETTINGS:
 *
 * filter_inverse (boolean) - default: False
 * True - filters out rows that match the filter text
 * False - filters out rows that do not match the filter text
 *
 * enable_space (boolean) - default: False
 * True - it uses space in filter text as delimiters. e.g. if filter text = "good boy", it
 *        will search rows for "good" and "boy" seperately
 * False - it will not use space as a delimiter. e.g. "good boy" will be treated as one word.
 *
 * filter_selector (string) - default: 'input.table-filter'
 * Allows override of the filter selector.
 *
 * cell_selector (string) - default: 'td'
 * Allows override of the cell selector, so that only certain cells will be filtered.
 * Example setting to only filter on the first column: {'cell_selector':'td:first-child'}
 *
 */

(function($){

	$.fn.table_filter = function (options) {

		//set default plugin values
		var settings = $.extend({

			'filter_inverse': false,
			'enable_space': false,
			'filter_selector': 'input.table-filter',
			'cell_selector': 'td'

		}, options);

		//return element, to maintain chainability
		return this.each(function () {
			var $this = $(this);
			var $tbody = $this.find('tbody:first');
			var $input = $this.find(settings.filter_selector);

			$input.bind("keyup", function () {

				//set filter text, and filterable table rows
				var txt = $input.val().toLowerCase();
				var $rows = $tbody.children("tr");

				$rows.each(function () {
					//default visibilty for rows is set based on filter_inverse value
					var $row = $(this);
					var show_tr = (settings.filter_inverse) ? true : false;
					var $cells = $row.find(settings.cell_selector);

					$cells.each(function () {
						var $cell = $(this);
						var td_txt = $.trim($cell.text()).toLowerCase();

						//if space is enabled as a delimiter, split the TD text value
						//and check the individual values against the filter text.
						if(settings.enable_space){

							var td_array = txt.split(" ");
							$.each(td_array, function (i) {
								var td_value = td_array[i];

								if(td_txt.indexOf(td_value) != -1){
									show_tr = (settings.filter_inverse) ? false : true;
								}
							});

						}
						else{

							if(td_txt.indexOf(txt) != -1){
								show_tr = (settings.filter_inverse) ? false : true;
							}

						}

					});

					if(show_tr){
						$(this).show().removeClass('hidden');
					}
					else{
						$(this).hide().addClass('hidden');
					}

				});

				//display all rows if filter text is empty
				if($.trim(txt) === ""){
					$tbody.children("tr").show().removeClass('hidden');
				}

				$tbody.setoddeven();

			});

		});

	};

})(jQuery);
<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react';
import CheckList from './CheckList';
import marked from 'marked';

class Card extends Component {
    constructor() {
        super(...arguments);
        this.state = {
            showDetails: false
        };
    }

    toggleDetails() {
        this.setState({showDetails: !this.state.showDetails});
    }

    render() {
        let cardDetails;
        if(this.state.showDetails) {
            cardDetails = (

                    <CheckList cardId={this.props.id} tasks={this.props.tasks} />

            );
        }

        let sideColor = {
            position: 'absolute',
            zIndex: -1,
            top: 0,
            bottom: 0,
            left: 0,
            width: 7,
            backgroundColor: this.props.color
        };

        return(

                    {this.props.title}

                {cardDetails}

        );
    }
}

Card.propTypes = {
    id: PropTypes.number,
    title: PropTypes.string,
    description: PropTypes.string,
    color: PropTypes.string,
    tasks: PropTypes.arrayOf(PropTypes.object)
};

export default Card;<commit_msg>custom propType validator added<commit_after>import React, {Component, PropTypes} from 'react';
import CheckList from './CheckList';
import marked from 'marked';

let titlePropType = (props, propName, componentName) => {
    if (props[propName]) {
        let value = props[propName];
        if (typeof value !== 'string' || value.length > 80) {
            return new Error(
                `${propName} in ${componentName} is longer than 80 characters.`
            );
        }
    }
};

class Card extends Component {
    constructor() {
        super(...arguments);
        this.state = {
            showDetails: false
        };
    }

    toggleDetails() {
        this.setState({showDetails: !this.state.showDetails});
    }

    render() {
        let cardDetails;
        if(this.state.showDetails) {
            cardDetails = (

                    <CheckList cardId={this.props.id} tasks={this.props.tasks} />

            );
        }

        let sideColor = {
            position: 'absolute',
            zIndex: -1,
            top: 0,
            bottom: 0,
            left: 0,
            width: 7,
            backgroundColor: this.props.color
        };

        return(

                    {this.props.title}

                {cardDetails}

        );
    }
}

Card.propTypes = {
    id: PropTypes.number,
    title: titlePropType,
    description: PropTypes.string,
    color: PropTypes.string,
    tasks: PropTypes.arrayOf(PropTypes.object)
};

export default Card;<|endoftext|>


<commit_before>/*global jQuery,cyberDojo*/

var cyberDojo = (function(cd, $) {
  "use strict";

  cd.newDojoDialog = function(dojoParams, from) {
    $.getJSON('/image_puller/image_pulled', dojoParams, function(pulled) {
      if (!pulled.result) {
        dialog_pullImage(dojoParams, from);
      } else {
        showNewDojoDialog(dojoParams, from);
      }
    });
  };

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

  var dialog_pullImage = function(dojoParams, from) {
    var title = 'setting up a new practice session...';
    var pullingDialog = makePullingDialog(title, dojoParams);
    pullingDialog.dialog('open');
    $.getJSON('/image_puller/image_pull', dojoParams, function(pull) {
      pullingDialog.dialog('close');
      if (pull.result) {
        showNewDojoDialog(dojoParams, from);
      } else {
        makePullFailedDialog(title, dojoParams).dialog('open');
      }
    });
  };

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

  var makePullingDialog = function(title, dojoParams) {
    var html = '' +
      '
' + dojoParams.display_name + '

' +
      'First time for this test framework!
' +
      "Doing a one-time only image pull.
" +
      'It can take a minute or two.
' +
      'Please wait...';
    return $('
')
      .html(avatarGridTable(html))
      .dialog({
        title: cd.dialogTitle(title),
        closeOnEscape: true,
        close: function() { $(this).remove(); },
        autoOpen: false,
        width: 620,
        height: 230,
        modal: true,
      });
  };

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

  var makePullFailedDialog = function(title, dojoParams) {
    var html = '' +
      "We're very sorry...
" +
      'The setup failed.
';
    return $('
')
      .html(avatarGridTable(html))
      .dialog({
        title: cd.dialogTitle(title),
        closeOnEscape: true,
        close: function() { $(this).remove(); },
        autoOpen: false,
        buttons: {
          'close': function() {
            $(this).remove();
          }
        },
        width: 620,
        height: 310,
        modal: true,
      });
  };

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

  var avatarGridTable = function(rhs) {
    return '' +
      '
' +
      '
' +
      '' +
      '' +
      '	' +
      '' +
      rhs +
      '	' +
      '
' +
      '
';
  };

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

  var showNewDojoDialog = function(params, from) {
    var id = params['id'];

    if (from == 'from_setup_team') {
      window.location = '/enter/show/' + id;
      return;
    }
    if (from == 'from_setup_individual') {
      cd.startAnimal(id, 'from_setup');
      return;
    }

    var gotoPage = function(url) {
      if (from == 'from_setup') {
        window.location = url; // same tab
      }
      if (from == 'from_fork') {
        window.open(url); // new tab
      }
    };

    var html = '' +
      "
" +
        "
" +
          "" +
            id.substring(0,6) +
          '' +
        '
' +
      '
';

    $('
')
      .html(html)
      .dialog({
        title: cd.dialogTitle('setup a new practice session'),
        autoOpen: true,
        modal: true,
        width: 435,
        closeOnEscape: true,
        open: function() {
          var pane = $('.ui-dialog-buttonpane');
          pane.find('button:contains("goto home page")').addClass('new-dojo-dialog-button enter-page');
          pane.find('button:contains("start programming")').addClass('new-dojo-dialog-button start-coding');
        },
        close: function() {
          $(this).remove();
        },
        buttons: {
          'goto home page': function() {
            gotoPage('/dojo/index/' + id);
            $(this).remove();
          },
          'start programming': function() {
            cd.startAnimal(id, from);
            $(this).remove();
          }
        }
      });
  };

  return cd;

})(cyberDojo || {}, jQuery);
<commit_msg>refactoring; tweaked image-pull on setup wording<commit_after>/*global jQuery,cyberDojo*/

var cyberDojo = (function(cd, $) {
  "use strict";

  cd.newDojoDialog = function(dojoParams, from) {
    $.getJSON('/image_puller/image_pulled', dojoParams, function(pulled) {
      if (!pulled.result) {
        dialog_pullImage(dojoParams, from);
      } else {
        showNewDojoDialog(dojoParams, from);
      }
    });
  };

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

  var dialog_pullImage = function(dojoParams, from) {
    var title = 'setting up a new practice session...';
    var pullingDialog = makePullingDialog(title, dojoParams);
    pullingDialog.dialog('open');
    $.getJSON('/image_puller/image_pull', dojoParams, function(pull) {
      pullingDialog.dialog('close');
      if (pull.result) {
        showNewDojoDialog(dojoParams, from);
      } else {
        makePullFailedDialog(title, dojoParams).dialog('open');
      }
    });
  };

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

  var makePullingDialog = function(title, dojoParams) {
    var html = '' +
      '
' + dojoParams.display_name + '

' +
      'First time for this selection!
' +
      "Doing a one-time only setup.
" +
      'It can take a minute or two.
' +
      'Please wait...';
    return $('
')
      .html(avatarGridTable(html))
      .dialog({
        title: cd.dialogTitle(title),
        closeOnEscape: true,
        close: function() { $(this).remove(); },
        autoOpen: false,
        width: 620,
        height: 230,
        modal: true,
      });
  };

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

  var makePullFailedDialog = function(title, dojoParams) {
    var html = '' +
      "We're very sorry...
" +
      'The setup failed.
';
    return $('
')
      .html(avatarGridTable(html))
      .dialog({
        title: cd.dialogTitle(title),
        closeOnEscape: true,
        close: function() { $(this).remove(); },
        autoOpen: false,
        buttons: {
          'close': function() {
            $(this).remove();
          }
        },
        width: 620,
        height: 310,
        modal: true,
      });
  };

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

  var avatarGridTable = function(rhs) {
    return '' +
      '
' +
      '
' +
      '' +
      '' +
      '	' +
      '' +
      rhs +
      '	' +
      '
' +
      '
';
  };

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

  var showNewDojoDialog = function(params, from) {
    var id = params['id'];

    if (from == 'from_setup_team') {
      window.location = '/enter/show/' + id;
      return;
    }
    if (from == 'from_setup_individual') {
      cd.startAnimal(id, 'from_setup');
      return;
    }

    var gotoPage = function(url) {
      if (from == 'from_setup') {
        window.location = url; // same tab
      }
      if (from == 'from_fork') {
        window.open(url); // new tab
      }
    };

    var html = '' +
      "
" +
        "
" +
          "" +
            id.substring(0,6) +
          '' +
        '
' +
      '
';

    $('
')
      .html(html)
      .dialog({
        title: cd.dialogTitle('setup a new practice session'),
        autoOpen: true,
        modal: true,
        width: 435,
        closeOnEscape: true,
        open: function() {
          var pane = $('.ui-dialog-buttonpane');
          pane.find('button:contains("goto home page")').addClass('new-dojo-dialog-button enter-page');
          pane.find('button:contains("start programming")').addClass('new-dojo-dialog-button start-coding');
        },
        close: function() {
          $(this).remove();
        },
        buttons: {
          'goto home page': function() {
            gotoPage('/dojo/index/' + id);
            $(this).remove();
          },
          'start programming': function() {
            cd.startAnimal(id, from);
            $(this).remove();
          }
        }
      });
  };

  return cd;

})(cyberDojo || {}, jQuery);
<|endoftext|>


<commit_before>//= require jquery.js
//= require html2canvas.js
//= require component/license.txt.js
//= require component/pre.txt.js
//= require component/Core.js
//= require component/Page.js
//= require component/Send.js
//= require component/pages/Form.js
//= require component/pages/Review.js
//= require component/pages/ScreenShot.js
//= require component/send/xhr.js
//= require component/post.txt.js
<commit_msg>removido jquery<commit_after>//require jquery.js #removido jquery
//= require html2canvas.js
//= require component/license.txt.js
//= require component/pre.txt.js
//= require component/Core.js
//= require component/Page.js
//= require component/Send.js
//= require component/pages/Form.js
//= require component/pages/Review.js
//= require component/pages/ScreenShot.js
//= require component/send/xhr.js
//= require component/post.txt.js
<|endoftext|>


<commit_before>/*
 * @package info.ajaxplorer.plugins
 *
 * Copyright 2007-2009 Charles du Jeu
 * This file is part of AjaXplorer.
 * The latest code can be found at http://www.ajaxplorer.info/
 *
 * This program is published under the LGPL Gnu Lesser General Public License.
 * You should have received a copy of the license along with AjaXplorer.
 *
 * The main conditions are as follow :
 * You must conspicuously and appropriately publish on each copy distributed
 * an appropriate copyright notice and disclaimer of warranty and keep intact
 * all the notices that refer to this License and to the absence of any warranty;
 * and give any other recipients of the Program a copy of the GNU Lesser General
 * Public License along with the Program.
 *
 * If you modify your copy or copies of the library or any portion of it, you may
 * distribute the resulting library provided you do so under the GNU Lesser
 * General Public License. However, programs that link to the library may be
 * licensed under terms of your choice, so long as the library itself can be changed.
 * Any translation of the GNU Lesser General Public License must be accompanied by the
 * GNU Lesser General Public License.
 *
 * If you copy or distribute the program, you must accompany it with the complete
 * corresponding machine-readable source code or with a written offer, valid for at
 * least three years, to furnish the complete corresponding machine-readable source code.
 *
 * Any of the above conditions can be waived if you get permission from the copyright holder.
 * AjaXplorer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */
/**
 * Main BootLoader.
 * Defaults params for constructor should be {} and content.php?get_action=get_boot_conf
 */
Class.create("AjxpBootstrap", {
	/**
	 * @var $H()
	 */
	parameters : $H({}),
	/**
	 * Constructor
	 * @param startParameters Object The options
	 */
	initialize : function(startParameters){
		this.parameters = $H(startParameters);
		this.detectBaseParameters();
		if(this.parameters.get("ALERT")){
			window.setTimeout(function(){alert(this.parameters.get("ALERT"));}.bind(this),0);
		}
		Event.observe(document, 'dom:loaded', function(){
			this.insertBasicSkeleton(this.parameters.get('MAIN_ELEMENT'));
			this.loadBootConfig();
		}.bind(this));
		document.observe("ajaxplorer:before_gui_load", function(e){
			var desktop = $(this.parameters.get('MAIN_ELEMENT'));
			var options = desktop.getAttribute("ajxpOptions").evalJSON(false);
			if(options.fit && options.fit == 'height'){
				var marginBottom = 0;
				if(options.fitMarginBottom){
					try{marginBottom = parseInt(eval(options.fitMarginBottom));}catch(e){}
				}
				if(options.fitParent == 'window') options.fitParent = window;
				else options.fitParent = $(options.fitParent);
				fitHeightToBottom($(this.parameters.get('MAIN_ELEMENT')), options.fitParent, marginBottom, true);
			}
		}.bind(this));
		document.observe("ajaxplorer:actions_loaded", function(){
			if(!this.parameters.get("SELECTOR_DATA") && ajaxplorer.actionBar.actions.get("ext_select")){
				ajaxplorer.actionBar.actions.unset("ext_select");
				ajaxplorer.actionBar.fireContextChange();
				ajaxplorer.actionBar.fireSelectionChange();
			}
		}.bind(this));
		document.observe("ajaxplorer:loaded", function(e){
			this.insertAnalytics();
			if(this.parameters.get("SELECTOR_DATA")){
	    		ajaxplorer.actionBar.defaultActions.set("file", "ext_select");
	    		ajaxplorer.actionBar.selectorData = new Hash(this.parameters.get("SELECTOR_DATA"));
			}
		}.bind(this));
	},
	/**
	 * Real loading action
	 */
	loadBootConfig : function(){
		var url = this.parameters.get('BOOTER_URL')+(this.parameters.get("debugMode")?'&debug=true':'');
		if(this.parameters.get('SERVER_PREFIX_URI')){
			url += '&server_prefix_uri=' + this.parameters.get('SERVER_PREFIX_URI');
		}
		var connexion = new Connexion(url);
		connexion.onComplete = function(transport){
			if(transport.responseXML && transport.responseXML.documentElement && transport.responseXML.documentElement.nodeName == "tree"){
				var alert = XPathSelectSingleNode(transport.responseXML.documentElement, "message");
				window.alert('Exception caught by application : ' + alert.firstChild.nodeValue);
				return;
			}
			var phpError;
			try{
				var data = transport.responseText.evalJSON();
			}catch(e){
				phpError = 'Error while parsing JSON response : ' + e.message;
			}
			if(!typeof data == "object"){
				phpError = 'Exception uncaught by application : ' + transport.responseText;
			}
			if(phpError){
				document.write(phpError);
				if(phpError.indexOf('Notice')>-1 || phpError.indexOf('Strict Standards')>-1){
					window.alert('Php errors detected, it seems that Notice or Strict are detected, you may consider changing the PHP Error Reporting level!');
				}
				return;
			}
			this.parameters.update(data);
			if(this.parameters.get('SECURE_TOKEN')){
				Connexion.SECURE_TOKEN = this.parameters.get('SECURE_TOKEN');
			}
			if(this.parameters.get('SERVER_PREFIX_URI')){
				this.parameters.set('ajxpResourcesFolder', this.parameters.get('SERVER_PREFIX_URI') + this.parameters.get('ajxpResourcesFolder'));
				this.parameters.set('ajxpServerAccess', this.parameters.get('SERVER_PREFIX_URI') + this.parameters.get('ajxpServerAccess') + '?' + (Connexion.SECURE_TOKEN? 'secure_token='+Connexion.SECURE_TOKEN:''));
			}else{
				this.parameters.set('ajxpServerAccess', this.parameters.get('ajxpServerAccess') + '?' + (Connexion.SECURE_TOKEN? 'secure_token='+Connexion.SECURE_TOKEN:''));
			}
			// Refresh window variable
			window.ajxpServerAccessPath = this.parameters.get('ajxpServerAccess');
			var cssRes = this.parameters.get("cssResources");
			if(cssRes) cssRes.each(this.loadCSSResource.bind(this));
			if(this.parameters.get('ajxpResourcesFolder')){
				window.ajxpResourcesFolder = this.parameters.get('ajxpResourcesFolder');
			}
			if(this.parameters.get('additional_js_resource')){
				connexion.loadLibrary(this.parameters.get('additional_js_resource'));
			}
			this.insertLoaderProgress();
			if(!this.parameters.get("debugMode")){
				connexion.loadLibrary("ajaxplorer.js");
			}
			window.MessageHash = this.parameters.get("i18nMessages");
			for(var key in MessageHash){
				MessageHash[key] = MessageHash[key].replace("\\n", "\n");
			}
			window.zipEnabled = this.parameters.get("zipEnabled");
			window.multipleFilesDownloadEnabled = this.parameters.get("multipleFilesDownloadEnabled");
			window.flashUploaderEnabled = this.parameters.get("flashUploaderEnabled");
			document.fire("ajaxplorer:boot_loaded");
			window.ajaxplorer = new Ajaxplorer(this.parameters.get("EXT_REP")||"", this.parameters.get("usersEnabled"), this.parameters.get("loggedUser"));
			if(this.parameters.get("currentLanguage")){
				window.ajaxplorer.currentLanguage = this.parameters.get("currentLanguage");
			}
			if(this.parameters.get("htmlMultiUploaderOptions")){
				window.htmlMultiUploaderOptions = this.parameters.get("htmlMultiUploaderOptions");
			}
			$('version_span').update(' - Version '+this.parameters.get("ajxpVersion") + ' - '+ this.parameters.get("ajxpVersionDate"));
			window.ajaxplorer.init();
		}.bind(this);
		connexion.sendSync();

	},
	/**
	 * Detect the base path of the javascripts based on the script tags
	 */
	detectBaseParameters : function(){
		$$('script').each(function(scriptTag){
			if(scriptTag.src.match("/js/ajaxplorer_boot.js") || scriptTag.src.match("/js/ajaxplorer/class.AjxpBootstrap.js")){
				if(scriptTag.src.match("/js/ajaxplorer_boot.js")){
					this.parameters.set("debugMode", false);
				}else{
					this.parameters.set("debugMode", true);
				}
				this.parameters.set("ajxpResourcesFolder", scriptTag.src.replace('/js/ajaxplorer/class.AjxpBootstrap.js','').replace('/js/ajaxplorer_boot.js', ''));
				return;
			}
		}.bind(this) );
		if(this.parameters.get("ajxpResourcesFolder")){
			window.ajxpResourcesFolder = this.parameters.get("ajxpResourcesFolder");
		}else{
			alert("Cannot find resource folder");
		}
		var booterUrl = this.parameters.get("BOOTER_URL");
		if(booterUrl.indexOf("?") > -1){
			booterUrl = booterUrl.substring(0, booterUrl.indexOf("?"));
		}
		this.parameters.set('ajxpServerAccessPath', booterUrl);
		window.ajxpServerAccessPath = booterUrl;
	},
	/**
	 * Inserts a progress bar
	 */
	insertLoaderProgress : function(){
		var html = '

';
		if(this.parameters.get('customWelcomeScreen')){
			try { this.parameters.set('customWelcomeScreen', customFuncDecode(this.parameters.get('customWelcomeScreen')));
			}catch(e){
				this.parameters.set('customWelcomeScreen','');
			}
		}
		if(this.parameters.get('customWelcomeScreen')){
			html += this.parameters.get('customWelcomeScreen');
		}else{
			html+='
';
			html+='
';
			html+='
AjaXplorer
';
			html+='

The web data-browser
';
			html+='	Written by Charles du Jeu - LGPL License.
';
			if(this.parameters.get('customWelcomeMessage')){
				html+= this.parameters.get('customWelcomeMessage') + '
';
			}
			html+='
0%

Booting...
';
			html+='

';
		}
		$$('body')[0].insert({top:html});
		viewPort = document.viewport.getDimensions();
		$('progressBox').setStyle({left:Math.max((viewPort.width-305)/2,0)});
		var options = {
			animate		: true,										// Animate the progress? - default: true
			showText	: false,									// show text with percentage in next to the progressbar? - default : true
			width		: 154,										// Width of the progressbar - don't forget to adjust your image too!!!
			boxImage	: this.parameters.get("ajxpResourcesFolder")+'/images/progress_box.gif',			// boxImage : image around the progress bar
			barImage	: this.parameters.get("ajxpResourcesFolder")+'/images/progress_bar.gif',	// Image to use in the progressbar. Can be an array of images too.
			height		: 11,										// Height of the progressbar - don't forget to adjust your image too!!!
			onTick		: function(pbObj) {
				if(pbObj.getPercentage() == 100){
					new Effect.Opacity('loading_overlay', {
						from:0.1,
						to:0,
						duration:0.5,
						afterFinish:function(effect){
							$('loading_overlay').remove();
							$('progressBox').remove();
						}
					});
					return false;
				}
				return true ;
			}
		};
		window.loaderProgress = new JS_BRAMUS.jsProgressBar($('loaderProgress'), 0, options);
	},
	/**
	 * Inserts Google Analytics Code
	 */
	insertAnalytics : function(){
		if(!this.parameters.get("googleAnalyticsData")) return;
		var data = this.parameters.get("googleAnalyticsData");
		window._gaq = window._gaq || [];
		window._gaq.push(['_setAccount', data.id]);
		if(data.domain) window._gaq.push(['_setDomainName', data.domain]);
		window._gaq.push(['_trackPageview']);
		window._gaTrackEvents = data.event;
		window.setTimeout(function(){
			var src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
			var ga = new Element("script", {type:'text/javascript', async:'true',src:src});
			($$('head')[0] || $$('body')[0]).insert(ga);
		}, 200);
	},
	/**
	 * Loads a CSS file
	 * @param fileName String
	 */
	loadCSSResource : function(fileName){
		var head = $$('head')[0];
		var cssNode = new Element('link', {
			type : 'text/css',
			rel  : 'stylesheet',
			href : this.parameters.get("ajxpResourcesFolder") + '/' + fileName,
			media : 'screen'
		});
		head.insert(cssNode);
	},
	/**
	 * Inserts the all_forms and generic dialog box if not alreay present.
	 * @param desktopNode String The id of the node to attach
	 */
	insertBasicSkeleton : function(desktopNode){
		if($('all_forms')) return;
		$(desktopNode).insert({after:
			'
\

\

\

\

'});
	}
});<commit_msg>Fix "Select" as default action even if user has to log in<commit_after>/*
 * @package info.ajaxplorer.plugins
 *
 * Copyright 2007-2009 Charles du Jeu
 * This file is part of AjaXplorer.
 * The latest code can be found at http://www.ajaxplorer.info/
 *
 * This program is published under the LGPL Gnu Lesser General Public License.
 * You should have received a copy of the license along with AjaXplorer.
 *
 * The main conditions are as follow :
 * You must conspicuously and appropriately publish on each copy distributed
 * an appropriate copyright notice and disclaimer of warranty and keep intact
 * all the notices that refer to this License and to the absence of any warranty;
 * and give any other recipients of the Program a copy of the GNU Lesser General
 * Public License along with the Program.
 *
 * If you modify your copy or copies of the library or any portion of it, you may
 * distribute the resulting library provided you do so under the GNU Lesser
 * General Public License. However, programs that link to the library may be
 * licensed under terms of your choice, so long as the library itself can be changed.
 * Any translation of the GNU Lesser General Public License must be accompanied by the
 * GNU Lesser General Public License.
 *
 * If you copy or distribute the program, you must accompany it with the complete
 * corresponding machine-readable source code or with a written offer, valid for at
 * least three years, to furnish the complete corresponding machine-readable source code.
 *
 * Any of the above conditions can be waived if you get permission from the copyright holder.
 * AjaXplorer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */
/**
 * Main BootLoader.
 * Defaults params for constructor should be {} and content.php?get_action=get_boot_conf
 */
Class.create("AjxpBootstrap", {
	/**
	 * @var $H()
	 */
	parameters : $H({}),
	/**
	 * Constructor
	 * @param startParameters Object The options
	 */
	initialize : function(startParameters){
		this.parameters = $H(startParameters);
		this.detectBaseParameters();
		if(this.parameters.get("ALERT")){
			window.setTimeout(function(){alert(this.parameters.get("ALERT"));}.bind(this),0);
		}
		Event.observe(document, 'dom:loaded', function(){
			this.insertBasicSkeleton(this.parameters.get('MAIN_ELEMENT'));
			this.loadBootConfig();
		}.bind(this));
		document.observe("ajaxplorer:before_gui_load", function(e){
			var desktop = $(this.parameters.get('MAIN_ELEMENT'));
			var options = desktop.getAttribute("ajxpOptions").evalJSON(false);
			if(options.fit && options.fit == 'height'){
				var marginBottom = 0;
				if(options.fitMarginBottom){
					try{marginBottom = parseInt(eval(options.fitMarginBottom));}catch(e){}
				}
				if(options.fitParent == 'window') options.fitParent = window;
				else options.fitParent = $(options.fitParent);
				fitHeightToBottom($(this.parameters.get('MAIN_ELEMENT')), options.fitParent, marginBottom, true);
			}
		}.bind(this));
		document.observe("ajaxplorer:actions_loaded", function(){
			if(!this.parameters.get("SELECTOR_DATA") && ajaxplorer.actionBar.actions.get("ext_select")){
				ajaxplorer.actionBar.actions.unset("ext_select");
				ajaxplorer.actionBar.fireContextChange();
				ajaxplorer.actionBar.fireSelectionChange();
			}else if(this.parameters.get("SELECTOR_DATA")){
				ajaxplorer.actionBar.defaultActions.set("file", "ext_select");
			}
		}.bind(this));
		document.observe("ajaxplorer:loaded", function(e){
			this.insertAnalytics();
			if(this.parameters.get("SELECTOR_DATA")){
	    		ajaxplorer.actionBar.defaultActions.set("file", "ext_select");
	    		ajaxplorer.actionBar.selectorData = new Hash(this.parameters.get("SELECTOR_DATA"));
			}
		}.bind(this));
	},
	/**
	 * Real loading action
	 */
	loadBootConfig : function(){
		var url = this.parameters.get('BOOTER_URL')+(this.parameters.get("debugMode")?'&debug=true':'');
		if(this.parameters.get('SERVER_PREFIX_URI')){
			url += '&server_prefix_uri=' + this.parameters.get('SERVER_PREFIX_URI');
		}
		var connexion = new Connexion(url);
		connexion.onComplete = function(transport){
			if(transport.responseXML && transport.responseXML.documentElement && transport.responseXML.documentElement.nodeName == "tree"){
				var alert = XPathSelectSingleNode(transport.responseXML.documentElement, "message");
				window.alert('Exception caught by application : ' + alert.firstChild.nodeValue);
				return;
			}
			var phpError;
			try{
				var data = transport.responseText.evalJSON();
			}catch(e){
				phpError = 'Error while parsing JSON response : ' + e.message;
			}
			if(!typeof data == "object"){
				phpError = 'Exception uncaught by application : ' + transport.responseText;
			}
			if(phpError){
				document.write(phpError);
				if(phpError.indexOf('Notice')>-1 || phpError.indexOf('Strict Standards')>-1){
					window.alert('Php errors detected, it seems that Notice or Strict are detected, you may consider changing the PHP Error Reporting level!');
				}
				return;
			}
			this.parameters.update(data);
			if(this.parameters.get('SECURE_TOKEN')){
				Connexion.SECURE_TOKEN = this.parameters.get('SECURE_TOKEN');
			}
			if(this.parameters.get('SERVER_PREFIX_URI')){
				this.parameters.set('ajxpResourcesFolder', this.parameters.get('SERVER_PREFIX_URI') + this.parameters.get('ajxpResourcesFolder'));
				this.parameters.set('ajxpServerAccess', this.parameters.get('SERVER_PREFIX_URI') + this.parameters.get('ajxpServerAccess') + '?' + (Connexion.SECURE_TOKEN? 'secure_token='+Connexion.SECURE_TOKEN:''));
			}else{
				this.parameters.set('ajxpServerAccess', this.parameters.get('ajxpServerAccess') + '?' + (Connexion.SECURE_TOKEN? 'secure_token='+Connexion.SECURE_TOKEN:''));
			}
			// Refresh window variable
			window.ajxpServerAccessPath = this.parameters.get('ajxpServerAccess');
			var cssRes = this.parameters.get("cssResources");
			if(cssRes) cssRes.each(this.loadCSSResource.bind(this));
			if(this.parameters.get('ajxpResourcesFolder')){
				window.ajxpResourcesFolder = this.parameters.get('ajxpResourcesFolder');
			}
			if(this.parameters.get('additional_js_resource')){
				connexion.loadLibrary(this.parameters.get('additional_js_resource'));
			}
			this.insertLoaderProgress();
			if(!this.parameters.get("debugMode")){
				connexion.loadLibrary("ajaxplorer.js");
			}
			window.MessageHash = this.parameters.get("i18nMessages");
			for(var key in MessageHash){
				MessageHash[key] = MessageHash[key].replace("\\n", "\n");
			}
			window.zipEnabled = this.parameters.get("zipEnabled");
			window.multipleFilesDownloadEnabled = this.parameters.get("multipleFilesDownloadEnabled");
			window.flashUploaderEnabled = this.parameters.get("flashUploaderEnabled");
			document.fire("ajaxplorer:boot_loaded");
			window.ajaxplorer = new Ajaxplorer(this.parameters.get("EXT_REP")||"", this.parameters.get("usersEnabled"), this.parameters.get("loggedUser"));
			if(this.parameters.get("currentLanguage")){
				window.ajaxplorer.currentLanguage = this.parameters.get("currentLanguage");
			}
			if(this.parameters.get("htmlMultiUploaderOptions")){
				window.htmlMultiUploaderOptions = this.parameters.get("htmlMultiUploaderOptions");
			}
			$('version_span').update(' - Version '+this.parameters.get("ajxpVersion") + ' - '+ this.parameters.get("ajxpVersionDate"));
			window.ajaxplorer.init();
		}.bind(this);
		connexion.sendSync();

	},
	/**
	 * Detect the base path of the javascripts based on the script tags
	 */
	detectBaseParameters : function(){
		$$('script').each(function(scriptTag){
			if(scriptTag.src.match("/js/ajaxplorer_boot.js") || scriptTag.src.match("/js/ajaxplorer/class.AjxpBootstrap.js")){
				if(scriptTag.src.match("/js/ajaxplorer_boot.js")){
					this.parameters.set("debugMode", false);
				}else{
					this.parameters.set("debugMode", true);
				}
				this.parameters.set("ajxpResourcesFolder", scriptTag.src.replace('/js/ajaxplorer/class.AjxpBootstrap.js','').replace('/js/ajaxplorer_boot.js', ''));
				return;
			}
		}.bind(this) );
		if(this.parameters.get("ajxpResourcesFolder")){
			window.ajxpResourcesFolder = this.parameters.get("ajxpResourcesFolder");
		}else{
			alert("Cannot find resource folder");
		}
		var booterUrl = this.parameters.get("BOOTER_URL");
		if(booterUrl.indexOf("?") > -1){
			booterUrl = booterUrl.substring(0, booterUrl.indexOf("?"));
		}
		this.parameters.set('ajxpServerAccessPath', booterUrl);
		window.ajxpServerAccessPath = booterUrl;
	},
	/**
	 * Inserts a progress bar
	 */
	insertLoaderProgress : function(){
		var html = '

';
		if(this.parameters.get('customWelcomeScreen')){
			try { this.parameters.set('customWelcomeScreen', customFuncDecode(this.parameters.get('customWelcomeScreen')));
			}catch(e){
				this.parameters.set('customWelcomeScreen','');
			}
		}
		if(this.parameters.get('customWelcomeScreen')){
			html += this.parameters.get('customWelcomeScreen');
		}else{
			html+='
';
			html+='
';
			html+='
AjaXplorer
';
			html+='

The web data-browser
';
			html+='	Written by Charles du Jeu - LGPL License.
';
			if(this.parameters.get('customWelcomeMessage')){
				html+= this.parameters.get('customWelcomeMessage') + '
';
			}
			html+='
0%

Booting...
';
			html+='

';
		}
		$$('body')[0].insert({top:html});
		viewPort = document.viewport.getDimensions();
		$('progressBox').setStyle({left:Math.max((viewPort.width-305)/2,0)});
		var options = {
			animate		: true,										// Animate the progress? - default: true
			showText	: false,									// show text with percentage in next to the progressbar? - default : true
			width		: 154,										// Width of the progressbar - don't forget to adjust your image too!!!
			boxImage	: this.parameters.get("ajxpResourcesFolder")+'/images/progress_box.gif',			// boxImage : image around the progress bar
			barImage	: this.parameters.get("ajxpResourcesFolder")+'/images/progress_bar.gif',	// Image to use in the progressbar. Can be an array of images too.
			height		: 11,										// Height of the progressbar - don't forget to adjust your image too!!!
			onTick		: function(pbObj) {
				if(pbObj.getPercentage() == 100){
					new Effect.Opacity('loading_overlay', {
						from:0.1,
						to:0,
						duration:0.5,
						afterFinish:function(effect){
							$('loading_overlay').remove();
							$('progressBox').remove();
						}
					});
					return false;
				}
				return true ;
			}
		};
		window.loaderProgress = new JS_BRAMUS.jsProgressBar($('loaderProgress'), 0, options);
	},
	/**
	 * Inserts Google Analytics Code
	 */
	insertAnalytics : function(){
		if(!this.parameters.get("googleAnalyticsData")) return;
		var data = this.parameters.get("googleAnalyticsData");
		window._gaq = window._gaq || [];
		window._gaq.push(['_setAccount', data.id]);
		if(data.domain) window._gaq.push(['_setDomainName', data.domain]);
		window._gaq.push(['_trackPageview']);
		window._gaTrackEvents = data.event;
		window.setTimeout(function(){
			var src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
			var ga = new Element("script", {type:'text/javascript', async:'true',src:src});
			($$('head')[0] || $$('body')[0]).insert(ga);
		}, 200);
	},
	/**
	 * Loads a CSS file
	 * @param fileName String
	 */
	loadCSSResource : function(fileName){
		var head = $$('head')[0];
		var cssNode = new Element('link', {
			type : 'text/css',
			rel  : 'stylesheet',
			href : this.parameters.get("ajxpResourcesFolder") + '/' + fileName,
			media : 'screen'
		});
		head.insert(cssNode);
	},
	/**
	 * Inserts the all_forms and generic dialog box if not alreay present.
	 * @param desktopNode String The id of the node to attach
	 */
	insertBasicSkeleton : function(desktopNode){
		if($('all_forms')) return;
		$(desktopNode).insert({after:
			'
\

\

\

\

'});
	}
});<|endoftext|>


<commit_before>// ==========================================================================
// Project:   The M-Project - Mobile HTML5 Application Framework
// Copyright: (c) 2010 M-Way Solutions GmbH. All rights reserved.
// Creator:   Dominik
// Date:      26.10.2010
// License:   Dual licensed under the MIT or GPL Version 2 licenses.
//            http://github.com/mwaylabs/The-M-Project/blob/master/MIT-LICENSE
//            http://github.com/mwaylabs/The-M-Project/blob/master/GPL-LICENSE
// ==========================================================================

m_require('core/foundation/model.js');

/**
 * @class
 *
 * The root of all views.
 *
 */
M.View = M.Object.extend({

    /**
     * The type of this object.
     *
     * @property {String}
     */
    type: 'M.View',

    /**
     * Generic attribute for views. Some subclasses have a property that is more readable to its context than 'value'.
     * Internally these properties are mapped to 'value'.
     * e.g. in LabelView: value:this.text
     *
     * @property {Object, String}
     */
    value: null,

    /**
     * The path to a content that is bind to the view's value.
     *
     * @property {String, Object}
     */
    contentBinding: null,

    /**
     * An array specifying the view's children.
     *
     * @property {Object}
     */
    childViews: null,

    /**
     * Determines whether this view renders directly to the DOM or just returns its HTML representation.
     *
     * @property {Boolean}
     */
    renderToDOM: YES,

    /**
     * Indicates whether this view currently has the focus or not.
     *
     * @property {Boolean}
     */
    hasFocus: NO,

    /**
     * The id of the View used for the HTML attribute ID.
     */
    id: null,

    /**
     * Indicates whether the view should be displayed inline or not.
     */
    isInline: NO,

    /**
     * Indicates whether the view is visible or not.
     * Implemented via CSS's display property.
     */
    isVisible: YES,

    /*
     * Indicates whether the view is enabled or not.
     */
    isEnabled: YES,

    modelId: null,

    /**
     * This property can be used to assign a css class to the view using the jquery mobile
     * specific data-theme property. This allows you to create your custom styles.
     *
     * @property {String}
     */
    cssClass: null,

    /**
     * This property is used internally to recursively build the pages html representation.
     * It is once set within the render method and then eventually updated within the
     * renderUpdate method.
     *
     * @property {String} The pages html content.
     */
    html: '',

    /**
     * This method encapsulates the 'extend' method of M.Object for better reading of code syntax.
     * It triggers the content binding for this view,
     * gets an ID from and registers itself at the ViewManager.
     *
     * @param {Object} obj The mixed in object for the extend call.
     */
    design: function(obj) {
        var view = this.extend(obj);
        if(view.contentBinding) {
            view.attachToObservable(view.contentBinding);
        }
        view.id = M.Application.viewManager.getNextId();
        M.Application.viewManager.register(view);
        return view;
    },

     /**
     * Interface method.
     * Renders itself to HTML.
     * Render implementation is done by the special child obj of View (LabelView etc.).
     */
    render: function() {

    },

    /**
     * Interface method.
     * Updates itself in DOM.
     * Render implementation is done by the special child obj of View (LabelView etc.).
     */
    renderUpdate: function() {

    },

    /**
     * Triggers render() on all children.
     */
    renderChildViews: function() {
        if(this.childViews) {
            var childViews = $.trim(this.childViews).split(' ');
            for(var i in childViews) {
                if(this.type === 'M.PageView' && this[childViews[i]].type === 'M.TabBarView') {
                    this.hasTabBarView = YES;
                    this.tabBarView = this[childViews[i]];
                }
                this.html += this[childViews[i]].render();
            }
        }
    },

    /**
     * If the view's computedValue property is set, compute the value. This allows you to
     * apply a method to a dynamically set value. E.g. you can provide your value with an
     * toUpperCase().
     */
    computeValue: function() {
        if(this.computedValue) {
            this.value = this.computedValue.operation(this.computedValue.valuePattern ? this.value : this.computedValue.value, this);
        }
    },

    /**
     * Interface method.
     * Triggers rendering engine to style this view/apply a theme.
     */
    theme: function() {

    },

    /**
     * Triggers theme() on all children.
     */
    themeChildViews: function() {
        if(this.childViews) {
            var childViews = $.trim(this.childViews).split(' ');
            for(var i in childViews) {
                this[childViews[i]].theme();
            }
        }
    },

    /**
     * contentDidChange is called by the observable when observable's state did change.
     * It updates the view's value property.
     */
    contentDidChange: function(){
        var bindingPath = this.contentBinding.split('.');
        if(bindingPath && bindingPath.length === 3 && !(this.hasFocus && this.type === 'M.TextFieldView')) {
            this.value = eval(bindingPath[0])[bindingPath[1]][bindingPath[2]];
            this.renderUpdate();
            this.delegateValueUpdate();
        } else if(bindingPath && bindingPath.length === 3) {
            return;
        } else {
            M.Logger.log('bindingPath not valid', M.WARN);
        }
    },

    /**
     * This method attaches itself to an observable.
     *
     * @param {String} contentBinding The path to the observable property.
     */
    attachToObservable: function(contentBinding) {
        var bindingPath = contentBinding.split('.');
        if(bindingPath && bindingPath.length === 3 && eval(bindingPath[0]) && eval(bindingPath[0])[bindingPath[1]]) {
            eval(bindingPath[0])[bindingPath[1]].observable.attach(this, bindingPath[2]);
            this.isObserver = YES;
        } else {
            M.Logger.log('bindingPath not valid', M.WARN);
        }
    },

    /**
     * Interface method.
     * This method sets its value to the value it has in its DOM representation.
     */
    setValueFromDOM: function() {

    },

    /**
     * This method delegates any value changes to a controller, if the 'contentBindingReverse'
     * property is specified.
     */
    delegateValueUpdate: function() {
        /* delegate value updates to a binded controller,
           but only if the view currently is the master */
        if(this.contentBindingReverse && this.hasFocus) {
            var params = this.contentBindingReverse.split('.');
            var controller = eval(params[0]);
            controller[params[1]].set(params[2], this.value);
        }
    },

    /**
     * Interface method.
     * Method to append css styles and other style specific inline attributes to the rendered view.
     */
    style: function() {

    },

    /**
     * This method is called whenever the view gets the focus.
     */
    gotFocus: function() {
        this.hasFocus = YES;
    },

    /**
     * This method is called whenever the view lost the focus.
     */
    lostFocus: function() {
        this.hasFocus = NO;
    },

    /**
     * Secure the passed string.
     *
     * This is mainly used for securing input elements like text fields but since it is
     * part of M.View it can be used and called out of any view.
     *
     * So far we only replace '<' and '>' with their corresponding html entity. The functionality
     * of this method will be extended in the future.
     *
     * @param {String} str The string to be escaped
     */
    secure: function(str) {
        return str.replace(/</g, "<").replace(/>/g, ">");
    },

    /**
     * This method writes the view's html string into the DOM.
     */
    writeToDOM: function() {
        document.write(this.html);
    }

});<commit_msg>Refactoring: Use nl2br if newLineToBreak property is set YES<commit_after>// ==========================================================================
// Project:   The M-Project - Mobile HTML5 Application Framework
// Copyright: (c) 2010 M-Way Solutions GmbH. All rights reserved.
// Creator:   Dominik
// Date:      26.10.2010
// License:   Dual licensed under the MIT or GPL Version 2 licenses.
//            http://github.com/mwaylabs/The-M-Project/blob/master/MIT-LICENSE
//            http://github.com/mwaylabs/The-M-Project/blob/master/GPL-LICENSE
// ==========================================================================

m_require('core/foundation/model.js');

/**
 * @class
 *
 * The root of all views.
 *
 */
M.View = M.Object.extend({

    /**
     * The type of this object.
     *
     * @property {String}
     */
    type: 'M.View',

    /**
     * Generic attribute for views. Some subclasses have a property that is more readable to its context than 'value'.
     * Internally these properties are mapped to 'value'.
     * e.g. in LabelView: value:this.text
     *
     * @property {Object, String}
     */
    value: null,

    /**
     * The path to a content that is bind to the view's value.
     *
     * @property {String, Object}
     */
    contentBinding: null,

    /**
     * An array specifying the view's children.
     *
     * @property {Object}
     */
    childViews: null,

    /**
     * Determines whether this view renders directly to the DOM or just returns its HTML representation.
     *
     * @property {Boolean}
     */
    renderToDOM: YES,

    /**
     * Indicates whether this view currently has the focus or not.
     *
     * @property {Boolean}
     */
    hasFocus: NO,

    /**
     * The id of the View used for the HTML attribute ID.
     */
    id: null,

    /**
     * Indicates whether the view should be displayed inline or not.
     */
    isInline: NO,

    /**
     * Indicates whether the view is visible or not.
     * Implemented via CSS's display property.
     */
    isVisible: YES,

    /*
     * Indicates whether the view is enabled or not.
     */
    isEnabled: YES,

    modelId: null,

    /**
     * This property can be used to assign a css class to the view using the jquery mobile
     * specific data-theme property. This allows you to create your custom styles.
     *
     * @property {String}
     */
    cssClass: null,

    /**
     * This property is used internally to recursively build the pages html representation.
     * It is once set within the render method and then eventually updated within the
     * renderUpdate method.
     *
     * @property {String} The pages html content.
     */
    html: '',

    /**
     * This method encapsulates the 'extend' method of M.Object for better reading of code syntax.
     * It triggers the content binding for this view,
     * gets an ID from and registers itself at the ViewManager.
     *
     * @param {Object} obj The mixed in object for the extend call.
     */
    design: function(obj) {
        var view = this.extend(obj);
        if(view.contentBinding) {
            view.attachToObservable(view.contentBinding);
        }
        view.id = M.Application.viewManager.getNextId();
        M.Application.viewManager.register(view);
        return view;
    },

     /**
     * Interface method.
     * Renders itself to HTML.
     * Render implementation is done by the special child obj of View (LabelView etc.).
     */
    render: function() {

    },

    /**
     * Interface method.
     * Updates itself in DOM.
     * Render implementation is done by the special child obj of View (LabelView etc.).
     */
    renderUpdate: function() {

    },

    /**
     * Triggers render() on all children.
     */
    renderChildViews: function() {
        if(this.childViews) {
            var childViews = $.trim(this.childViews).split(' ');
            for(var i in childViews) {
                if(this.type === 'M.PageView' && this[childViews[i]].type === 'M.TabBarView') {
                    this.hasTabBarView = YES;
                    this.tabBarView = this[childViews[i]];
                }
                this.html += this[childViews[i]].render();
            }
        }
    },

    /**
     * If the view's computedValue property is set, compute the value. This allows you to
     * apply a method to a dynamically set value. E.g. you can provide your value with an
     * toUpperCase().
     */
    computeValue: function() {
        if(this.computedValue) {
            this.value = this.computedValue.operation(this.computedValue.valuePattern ? this.value : this.computedValue.value, this);
        }
    },

    /**
     * Interface method.
     * Triggers rendering engine to style this view/apply a theme.
     */
    theme: function() {

    },

    /**
     * Triggers theme() on all children.
     */
    themeChildViews: function() {
        if(this.childViews) {
            var childViews = $.trim(this.childViews).split(' ');
            for(var i in childViews) {
                this[childViews[i]].theme();
            }
        }
    },

    /**
     * contentDidChange is called by the observable when observable's state did change.
     * It updates the view's value property.
     */
    contentDidChange: function(){
        var bindingPath = this.contentBinding.split('.');
        if(bindingPath && bindingPath.length === 3 && !(this.hasFocus && this.type === 'M.TextFieldView')) {
            this.value = eval(bindingPath[0])[bindingPath[1]][bindingPath[2]];
            this.renderUpdate();
            this.delegateValueUpdate();
        } else if(bindingPath && bindingPath.length === 3) {
            return;
        } else {
            M.Logger.log('bindingPath not valid', M.WARN);
        }
    },

    /**
     * This method attaches itself to an observable.
     *
     * @param {String} contentBinding The path to the observable property.
     */
    attachToObservable: function(contentBinding) {
        var bindingPath = contentBinding.split('.');
        if(bindingPath && bindingPath.length === 3 && eval(bindingPath[0]) && eval(bindingPath[0])[bindingPath[1]]) {
            eval(bindingPath[0])[bindingPath[1]].observable.attach(this, bindingPath[2]);
            this.isObserver = YES;
        } else {
            M.Logger.log('bindingPath not valid', M.WARN);
        }
    },

    /**
     * Interface method.
     * This method sets its value to the value it has in its DOM representation.
     */
    setValueFromDOM: function() {

    },

    /**
     * This method delegates any value changes to a controller, if the 'contentBindingReverse'
     * property is specified.
     */
    delegateValueUpdate: function() {
        /* delegate value updates to a binded controller,
           but only if the view currently is the master */
        if(this.contentBindingReverse && this.hasFocus) {
            var params = this.contentBindingReverse.split('.');
            var controller = eval(params[0]);
            controller[params[1]].set(params[2], this.value);
        }
    },

    /**
     * Interface method.
     * Method to append css styles and other style specific inline attributes to the rendered view.
     */
    style: function() {

    },

    /**
     * This method is called whenever the view gets the focus.
     */
    gotFocus: function() {
        this.hasFocus = YES;
    },

    /**
     * This method is called whenever the view lost the focus.
     */
    lostFocus: function() {
        this.hasFocus = NO;
    },

    /**
     * Secure the passed string.
     *
     * This is mainly used for securing input elements like text fields but since it is
     * part of M.View it can be used and called out of any view.
     *
     * So far we only replace '<' and '>' with their corresponding html entity. The functionality
     * of this method will be extended in the future.
     *
     * @param {String} str The string to be escaped
     */
    secure: function(str) {
        return str.replace(/</g, "<").replace(/>/g, ">");
    },

    nl2br: function(str) {
        if(str) {
            if(typeof(str) !== 'string') {
                str = String(str);
            }
            return str.replace(/\n/g, '<br />\n');
        }
    },

    /**
     * This method writes the view's html string into the DOM.
     */
    writeToDOM: function() {
        document.write(this.html);
    }

});<|endoftext|>


<commit_before>/**
 * skeleton.js
 * @flow
 */

import _ from "lodash";
import Backbone from "backbone";
import * as THREE from "three";
import TWEEN from "tween.js";
import Utils from "libs/utils";
import Store from "oxalis/throttled_store";
import type { SkeletonTracingType, TreeType, NodeType } from "oxalis/store";
import type { NodeWithTreeIdType } from "oxalis/model/sagas/update_actions";
import { diffTrees } from "oxalis/model/sagas/skeletontracing_saga";
import ParticleMaterialFactory, { NodeTypes } from "oxalis/geometries/materials/particle_material_factory";
import type { Vector3 } from "oxalis/constants";
import { getPlaneScalingFactor } from "oxalis/model/accessors/flycam_accessor";

const MAX_CAPACITY = 1000;

type BufferHelper = {
  addAttributes(geometry: THREE.BufferGeometry, capacity: number): void,
  buildMesh(geometry: THREE.BufferGeometry): THREE.Mesh,
}

type Buffer = {
  capacity: number,
  nextIndex: number,
  geometry: THREE.BufferGeometry,
  mesh: THREE.Mesh,
}

type BufferPosition = {
  buffer: Buffer,
  index: number,
}

type BufferCollection = {
  buffers: Array<Buffer>,
  idToBufferPosition: Map<number, BufferPosition>,
  freeList: Array<BufferPosition>,
  helper: BufferHelper,
}

type BufferOperation = (position: BufferPosition) => Array<THREE.BufferAttribute>;

const NodeBufferHelper = {
  addAttributes(geometry: THREE.BufferGeometry, capacity: number): void {
    geometry.addAttribute("position", new THREE.BufferAttribute(new Float32Array(capacity * 3), 3));
    geometry.addAttribute("treeColor", new THREE.BufferAttribute(new Float32Array(capacity * 3), 3));
    geometry.addAttribute("radius", new THREE.BufferAttribute(new Float32Array(capacity), 1));
    geometry.addAttribute("type", new THREE.BufferAttribute(new Float32Array(capacity), 1));
    geometry.addAttribute("nodeId", new THREE.BufferAttribute(new Float32Array(capacity), 1));
    geometry.addAttribute("treeId", new THREE.BufferAttribute(new Float32Array(capacity), 1));
  },

  buildMesh(geometry: THREE.BufferGeometry): THREE.Mesh {
    const material = ParticleMaterialFactory.getMaterial();
    return new THREE.Points(geometry, material);
  },
};

const EdgeBufferHelper = {
  addAttributes(geometry: THREE.BufferGeometry, capacity: number): void {
    geometry.addAttribute("position", new THREE.BufferAttribute(new Float32Array(capacity * 6), 3));
  },

  buildMesh(geometry: THREE.BufferGeometry): THREE.Mesh {
    const material = new THREE.LineBasicMaterial({
      color: new THREE.Color().fromArray([1, 0, 1]),
      linewidth: 10,
    });
    return new THREE.LineSegments(geometry, material);
  },
};

class Skeleton {
  // This class is supposed to collect all the Geometries that belong to the skeleton, like
  // nodes, edges and trees

  trigger: Function; // TODO remove

  // TODO move to store?
  isVisible: boolean;
  showInactiveTrees: boolean;

  prevTracing: SkeletonTracingType;

  nodes: BufferCollection;
  edges: BufferCollection;
  nodeMaterial: THREE.RawShaderMaterial;

  constructor() {
    _.extend(this, Backbone.Events);

    this.isVisible = true;
    this.showInactiveTrees = true;

    const trees = Store.getState().skeletonTracing.trees;
    const nodeCount = _.sum(_.map(trees, tree => _.size(tree.nodes)));
    const edgeCount = _.sum(_.map(trees, tree => _.size(tree.edges)));
    this.nodes = this.initializeBufferCollection(nodeCount, NodeBufferHelper);
    this.edges = this.initializeBufferCollection(edgeCount, EdgeBufferHelper);
    this.nodeMaterial = ParticleMaterialFactory.getMaterial();

    for (const tree of _.values(trees)) {
      this.createTree(tree);
    }
    for (const nodes of this.nodes.buffers) {
      nodes.geometry.computeBoundingSphere();
    }
    for (const edges of this.edges.buffers) {
      edges.geometry.computeBoundingSphere();
    }

    Store.subscribe(() => this.refresh());
    this.prevTracing = Store.getState().skeletonTracing;
  }

  initializeBufferCollection(initialCapacity: number, helper: BufferHelper): BufferCollection {
    const initialBuffer = this.initializeBuffer(Math.max(initialCapacity, MAX_CAPACITY), helper);

    return {
      buffers: [initialBuffer],
      idToBufferPosition: new Map(),
      freeList: [],
      helper,
    };
  }

  initializeBuffer(capacity: number, helper: BufferHelper): Buffer {
    const geometry = new THREE.BufferGeometry();
    helper.addAttributes(geometry, capacity);
    return {
      capacity,
      nextIndex: 0,
      geometry,
      mesh: helper.buildMesh(geometry),
    };
  }

  create(id: number, collection: BufferCollection, updateBoundingSphere: boolean, createFunc: BufferOperation) {
    let currentBuffer = collection.buffers[0];
    if (collection.freeList.length === 0 && currentBuffer.nextIndex >= currentBuffer.capacity) {
      currentBuffer = this.initializeBuffer(MAX_CAPACITY, collection.helper);
      this.trigger("newGeometries", [currentBuffer.mesh]); // TODO remove
      collection.buffers.unshift(currentBuffer);
    }
    const bufferPosition = collection.freeList.pop() || { buffer: currentBuffer, index: currentBuffer.nextIndex++ };
    collection.idToBufferPosition.set(id, bufferPosition);

    const changedAttributes = createFunc(bufferPosition);
    for (const attribute of changedAttributes) {
      attribute.needsUpdate = true;
    }

    if (updateBoundingSphere) {
      bufferPosition.buffer.geometry.computeBoundingSphere();
    }
  }

  delete(id: number, collection: BufferCollection, deleteFunc: BufferOperation) {
    const bufferPosition = collection.idToBufferPosition.get(id);
    if (bufferPosition != null) {
      const changedAttributes = deleteFunc(bufferPosition);
      for (const attribute of changedAttributes) {
        attribute.needsUpdate = true;
      }
      collection.idToBufferPosition.delete(id);
      collection.freeList.push(bufferPosition);
    }
  }

  update(id: number, collection: BufferCollection, updateFunc: BufferOperation) {
    const bufferPosition = collection.idToBufferPosition.get(id);
    if (bufferPosition != null) {
      const changedAttributes = updateFunc(bufferPosition);
      for (const attribute of changedAttributes) {
        attribute.needsUpdate = true;
      }
    }
  }

  refresh() {
    const state = Store.getState();
    const tracing = state.skeletonTracing;
    const diff = diffTrees(this.prevTracing.trees, tracing.trees);

    for (const update of diff) {
      switch (update.action) {
        case "createNode": {
          const treeColor = tracing.trees[update.value.treeId].color;
          this.createNode(update.value.id, update.value, treeColor);
          break;
        }
        case "deleteNode":
          this.deleteNode(update.value.treeId, update.value.id);
          break;
        case "createEdge": {
          const tree = tracing.trees[update.value.treeId];
          const source = tree.nodes[update.value.source];
          const target = tree.nodes[update.value.target];
          this.createEdge(tree.treeId, source, target);
          break;
        }
        case "deleteEdge":
          this.deleteEdge(update.value.treeId, update.value.source, update.value.target);
          break;
        case "updateNode":
          this.updateNodeRadius(update.value.treeId, update.value.id, update.value.radius);
          break;
        case "updateTree": {
          // diff branchpoints
          const treeId = update.value.id;
          const tree = tracing.trees[treeId];
          const prevTree = this.prevTracing.trees[treeId];
          const oldBranchPoints = prevTree.branchPoints.map(branchPoint => branchPoint.id);
          const newBranchPoints = tree.branchPoints.map(branchPoint => branchPoint.id);
          const { onlyA: deletedBranchPoints, onlyB: createdBranchPoints } = Utils.diffArrays(oldBranchPoints, newBranchPoints);

          for (const nodeId of deletedBranchPoints) {
            this.updateNodeType(treeId, nodeId, NodeTypes.NORMAL);
          }

          for (const nodeId of createdBranchPoints) {
            this.updateNodeType(treeId, nodeId, NodeTypes.BRANCH_POINT);
          }

          if (tree.color !== prevTree.color) {
            for (const node of _.values(tree.nodes)) {
              this.updateNodeColor(treeId, node.id, tree.color);
            }
          }
          break;
        }
        default:
      }
    }

    if (tracing.activeNodeId !== this.prevTracing.activeNodeId) {
      this.startNodeHighlightAnimation();
    }

    const { particleSize, scale, overrideNodeRadius } = state.userConfiguration;
    this.nodeMaterial.uniforms.planeZoomFactor.value = getPlaneScalingFactor(Store.getState().flycam);
    this.nodeMaterial.uniforms.overrideParticleSize.value = particleSize;
    this.nodeMaterial.uniforms.overrideNodeRadius.value = overrideNodeRadius;
    this.nodeMaterial.uniforms.viewportScale.value = scale;
    this.nodeMaterial.uniforms.activeTreeId.value = state.skeletonTracing.activeTreeId;
    this.nodeMaterial.uniforms.activeNodeId.value = state.skeletonTracing.activeNodeId;

    this.prevTracing = tracing;
  }

  getAllNodes() {
    return _.map(this.nodes.buffers, buffer => buffer.mesh);
  }

  getMeshes() {
    return [...this.nodes.buffers, ...this.edges.buffers].map(buffer => buffer.mesh);
  }

  // API

  // recursive Cantor pairing to generate a unique id from a list of numbers.
  combine(...numbers: Array<number>) {
    return numbers.reduce((acc, value) => 0.5 * (acc + value) * (acc + value + 1) + value);
  }

  createTree(tree: TreeType) {
    for (const node of _.values(tree.nodes)) {
      this.createNode(tree.treeId, node, tree.color, false);
    }
    for (const edge of tree.edges) {
      const source = tree.nodes[edge.source];
      const target = tree.nodes[edge.target];
      this.createEdge(tree.treeId, source, target, false);
    }
  }

  createNode(treeId: number, node: NodeType, treeColor: Vector3, updateBoundingSphere: boolean = true) {
    const id = this.combine(node.id, treeId);
    this.create(id, this.nodes, updateBoundingSphere, ({ buffer, index }) => {
      const attributes = buffer.geometry.attributes;
      attributes.position.set(node.position, index * 3);
      attributes.treeColor.set(treeColor, index * 3);
      attributes.radius.array[index] = node.radius;
      attributes.type.array[index] = NodeTypes.NORMAL;
      attributes.nodeId.array[index] = node.id;
      attributes.treeId.array[index] = treeId;
      return _.values(attributes);
    });
  }

  deleteNode(treeId: number, nodeId: number) {
    const id = this.combine(nodeId, treeId);
    this.delete(id, this.nodes, ({ buffer, index }) => {
      const attribute = buffer.geometry.attributes.type;
      attribute.array[index] = NodeTypes.INVALID;
      return [attribute];
    });
  }

  updateNodeColor(treeId: number, nodeId: number, color: Vector3) {
    const id = this.combine(nodeId, treeId);
    this.update(id, this.nodes, ({ buffer, index }) => {
      const attribute = buffer.geometry.attributes.color;
      attribute.set(color, index * 3);
      return [attribute];
    });
  }

  updateNodeRadius(treeId: number, nodeId: number, radius: number) {
    const id = this.combine(nodeId, treeId);
    this.update(id, this.nodes, ({ buffer, index }) => {
      const attribute = buffer.geometry.attributes.radius;
      attribute.array[index] = radius;
      return [attribute];
    });
  }

  updateNodeType(treeId: number, nodeId: number, type: number) {
    const id = this.combine(nodeId, treeId);
    this.update(id, this.nodes, ({ buffer, index }) => {
      const attribute = buffer.geometry.attributes.type;
      attribute.array[index] = type;
      return [attribute];
    });
  }

  createEdge(treeId: number, source: NodeType, target: NodeType, updateBoundingSphere: boolean = true) {
    const id = this.combine(treeId, source.id, target.id);
    this.create(id, this.edges, updateBoundingSphere, ({ buffer, index }) => {
      const attribute = buffer.geometry.attributes.position;
      attribute.set(source.position, index * 6);
      attribute.set(target.position, index * 6 + 3);
      return [attribute];
    });
  }

  deleteEdge(treeId: number, sourceId: number, targetId: number) {
    const id = this.combine(treeId, sourceId, targetId);
    this.delete(id, this.edges, ({ buffer, index }) => {
      const attribute = buffer.geometry.attributes.position;
      attribute.set([0, 0, 0, 0, 0, 0], index * 6);
      return [attribute];
    });
  }

  setVisibility(isVisible: boolean) {
    this.isVisible = isVisible;

    /* for (const mesh of this.getMeshes()) {
      mesh.isVisible = isVisible;
    }
    app.vent.trigger("rerender"); */
  }

  setVisibilityTemporary(/* isVisible: boolean */) {
    // for (const mesh of this.getMeshes()) {
    //   mesh.visible = isVisible && ((mesh.isVisible != null) ? mesh.isVisible : true);
    // }
    // (TODO: still needed?) app.vent.trigger("rerender");
  }

  restoreVisibility() {
//    this.setVisibilityTemporary(this.isVisible);
  }

  toggleVisibility() {
    this.setVisibility(!this.isVisible);
  }

  updateForCam(/* id: OrthoViewType */) {
    /* for (const tree of _.values(this.treeGeometryCache)) {
      //tree.showRadius(id !== OrthoViews.TDView && !Store.getState().userConfiguration.overrideNodeRadius);
    }

    if (id !== OrthoViews.TDView) {
      this.setVisibilityTemporary(this.isVisible);
    }
    this.setVisibilityTemporary(true);*/
  }

  toggleInactiveTreeVisibility() {
    // this.showInactiveTrees = !this.showInactiveTrees;
    // return this.setInactiveTreeVisibility(this.showInactiveTrees);
  }

  setInactiveTreeVisibility(/* isVisible: boolean */) {
    /* for (const mesh of this.getMeshes()) {
      mesh.isVisible = visible;
    }
    const treeGeometry = this.getTreeGeometry(Store.getState().skeletonTracing.activeTreeId);
    if (treeGeometry != null) {
      treeGeometry.edges.isVisible = true;
      treeGeometry.nodes.isVisible = true;
      app.vent.trigger("rerender");
    } */
  }

  setSizeAttenuation(/* sizeAttenuation: boolean */) {
    /* return _.map(this.treeGeometryCache, tree =>
      tree.setSizeAttenuation(sizeAttenuation));*/
  }

  async startNodeHighlightAnimation() {
    const normal = 1.0;
    const highlighted = 2.0;

    await this.animateNodeScale(normal, highlighted);
    await this.animateNodeScale(highlighted, normal);
  }

  animateNodeScale(from, to) {
    return new Promise((resolve, reject) => {
      const setScaleFactor = (scale) => {
        this.nodeMaterial.uniforms.activeNodeScaleFactor.value = scale;
      };

      const tweenAnimation = new TWEEN.Tween({ scaleFactor: from });
      tweenAnimation
      .to({ scaleFactor: to }, 100)
      .onUpdate(function onUpdate() {
        setScaleFactor(this.scaleFactor);
      })
      .onComplete(resolve)
      .onStop(reject)
      .start();
    });
  }
}

export default Skeleton;
<commit_msg>fixed node removal bug #1711<commit_after>/**
 * skeleton.js
 * @flow
 */

import _ from "lodash";
import Backbone from "backbone";
import * as THREE from "three";
import TWEEN from "tween.js";
import Utils from "libs/utils";
import Store from "oxalis/throttled_store";
import type { SkeletonTracingType, TreeType, NodeType } from "oxalis/store";
import type { NodeWithTreeIdType } from "oxalis/model/sagas/update_actions";
import { diffTrees } from "oxalis/model/sagas/skeletontracing_saga";
import ParticleMaterialFactory, { NodeTypes } from "oxalis/geometries/materials/particle_material_factory";
import type { Vector3 } from "oxalis/constants";
import { getPlaneScalingFactor } from "oxalis/model/accessors/flycam_accessor";

const MAX_CAPACITY = 1000;

type BufferHelper = {
  addAttributes(geometry: THREE.BufferGeometry, capacity: number): void,
  buildMesh(geometry: THREE.BufferGeometry): THREE.Mesh,
}

type Buffer = {
  capacity: number,
  nextIndex: number,
  geometry: THREE.BufferGeometry,
  mesh: THREE.Mesh,
}

type BufferPosition = {
  buffer: Buffer,
  index: number,
}

type BufferCollection = {
  buffers: Array<Buffer>,
  idToBufferPosition: Map<number, BufferPosition>,
  freeList: Array<BufferPosition>,
  helper: BufferHelper,
}

type BufferOperation = (position: BufferPosition) => Array<THREE.BufferAttribute>;

const NodeBufferHelper = {
  addAttributes(geometry: THREE.BufferGeometry, capacity: number): void {
    geometry.addAttribute("position", new THREE.BufferAttribute(new Float32Array(capacity * 3), 3));
    geometry.addAttribute("treeColor", new THREE.BufferAttribute(new Float32Array(capacity * 3), 3));
    geometry.addAttribute("radius", new THREE.BufferAttribute(new Float32Array(capacity), 1));
    geometry.addAttribute("type", new THREE.BufferAttribute(new Float32Array(capacity), 1));
    geometry.addAttribute("nodeId", new THREE.BufferAttribute(new Float32Array(capacity), 1));
    geometry.addAttribute("treeId", new THREE.BufferAttribute(new Float32Array(capacity), 1));
  },

  buildMesh(geometry: THREE.BufferGeometry): THREE.Mesh {
    const material = ParticleMaterialFactory.getMaterial();
    return new THREE.Points(geometry, material);
  },
};

const EdgeBufferHelper = {
  addAttributes(geometry: THREE.BufferGeometry, capacity: number): void {
    geometry.addAttribute("position", new THREE.BufferAttribute(new Float32Array(capacity * 6), 3));
  },

  buildMesh(geometry: THREE.BufferGeometry): THREE.Mesh {
    const material = new THREE.LineBasicMaterial({
      color: new THREE.Color().fromArray([1, 0, 1]),
      linewidth: 10,
    });
    return new THREE.LineSegments(geometry, material);
  },
};

class Skeleton {
  // This class is supposed to collect all the Geometries that belong to the skeleton, like
  // nodes, edges and trees

  trigger: Function; // TODO remove

  // TODO move to store?
  isVisible: boolean;
  showInactiveTrees: boolean;

  prevTracing: SkeletonTracingType;

  nodes: BufferCollection;
  edges: BufferCollection;
  nodeMaterial: THREE.RawShaderMaterial;

  constructor() {
    _.extend(this, Backbone.Events);

    this.isVisible = true;
    this.showInactiveTrees = true;

    const trees = Store.getState().skeletonTracing.trees;
    const nodeCount = _.sum(_.map(trees, tree => _.size(tree.nodes)));
    const edgeCount = _.sum(_.map(trees, tree => _.size(tree.edges)));
    this.nodes = this.initializeBufferCollection(nodeCount, NodeBufferHelper);
    this.edges = this.initializeBufferCollection(edgeCount, EdgeBufferHelper);
    this.nodeMaterial = ParticleMaterialFactory.getMaterial();

    for (const tree of _.values(trees)) {
      this.createTree(tree);
    }
    for (const nodes of this.nodes.buffers) {
      nodes.geometry.computeBoundingSphere();
    }
    for (const edges of this.edges.buffers) {
      edges.geometry.computeBoundingSphere();
    }

    Store.subscribe(() => this.refresh());
    this.prevTracing = Store.getState().skeletonTracing;
  }

  initializeBufferCollection(initialCapacity: number, helper: BufferHelper): BufferCollection {
    const initialBuffer = this.initializeBuffer(Math.max(initialCapacity, MAX_CAPACITY), helper);

    return {
      buffers: [initialBuffer],
      idToBufferPosition: new Map(),
      freeList: [],
      helper,
    };
  }

  initializeBuffer(capacity: number, helper: BufferHelper): Buffer {
    const geometry = new THREE.BufferGeometry();
    helper.addAttributes(geometry, capacity);
    return {
      capacity,
      nextIndex: 0,
      geometry,
      mesh: helper.buildMesh(geometry),
    };
  }

  create(id: number, collection: BufferCollection, updateBoundingSphere: boolean, createFunc: BufferOperation) {
    let currentBuffer = collection.buffers[0];
    if (collection.freeList.length === 0 && currentBuffer.nextIndex >= currentBuffer.capacity) {
      currentBuffer = this.initializeBuffer(MAX_CAPACITY, collection.helper);
      this.trigger("newGeometries", [currentBuffer.mesh]); // TODO remove
      collection.buffers.unshift(currentBuffer);
    }
    const bufferPosition = collection.freeList.pop() || { buffer: currentBuffer, index: currentBuffer.nextIndex++ };
    collection.idToBufferPosition.set(id, bufferPosition);

    const changedAttributes = createFunc(bufferPosition);
    for (const attribute of changedAttributes) {
      attribute.needsUpdate = true;
    }

    if (updateBoundingSphere) {
      bufferPosition.buffer.geometry.computeBoundingSphere();
    }
  }

  delete(id: number, collection: BufferCollection, deleteFunc: BufferOperation) {
    const bufferPosition = collection.idToBufferPosition.get(id);
    if (bufferPosition != null) {
      const changedAttributes = deleteFunc(bufferPosition);
      for (const attribute of changedAttributes) {
        attribute.needsUpdate = true;
      }
      collection.idToBufferPosition.delete(id);
      collection.freeList.push(bufferPosition);
    }
  }

  update(id: number, collection: BufferCollection, updateFunc: BufferOperation) {
    const bufferPosition = collection.idToBufferPosition.get(id);
    if (bufferPosition != null) {
      const changedAttributes = updateFunc(bufferPosition);
      for (const attribute of changedAttributes) {
        attribute.needsUpdate = true;
      }
    }
  }

  refresh() {
    const state = Store.getState();
    const tracing = state.skeletonTracing;
    const diff = diffTrees(this.prevTracing.trees, tracing.trees);

    for (const update of diff) {
      switch (update.action) {
        case "createNode": {
          const treeColor = tracing.trees[update.value.treeId].color;
          this.createNode(update.value.treeId, update.value, treeColor);
          break;
        }
        case "deleteNode":
          this.deleteNode(update.value.treeId, update.value.id);
          break;
        case "createEdge": {
          const tree = tracing.trees[update.value.treeId];
          const source = tree.nodes[update.value.source];
          const target = tree.nodes[update.value.target];
          this.createEdge(tree.treeId, source, target);
          break;
        }
        case "deleteEdge":
          this.deleteEdge(update.value.treeId, update.value.source, update.value.target);
          break;
        case "updateNode":
          this.updateNodeRadius(update.value.treeId, update.value.id, update.value.radius);
          break;
        case "updateTree": {
          // diff branchpoints
          const treeId = update.value.id;
          const tree = tracing.trees[treeId];
          const prevTree = this.prevTracing.trees[treeId];
          const oldBranchPoints = prevTree.branchPoints.map(branchPoint => branchPoint.id);
          const newBranchPoints = tree.branchPoints.map(branchPoint => branchPoint.id);
          const { onlyA: deletedBranchPoints, onlyB: createdBranchPoints } = Utils.diffArrays(oldBranchPoints, newBranchPoints);

          for (const nodeId of deletedBranchPoints) {
            this.updateNodeType(treeId, nodeId, NodeTypes.NORMAL);
          }

          for (const nodeId of createdBranchPoints) {
            this.updateNodeType(treeId, nodeId, NodeTypes.BRANCH_POINT);
          }

          if (tree.color !== prevTree.color) {
            for (const node of _.values(tree.nodes)) {
              this.updateNodeColor(treeId, node.id, tree.color);
            }
          }
          break;
        }
        default:
      }
    }

    if (tracing.activeNodeId !== this.prevTracing.activeNodeId) {
      this.startNodeHighlightAnimation();
    }

    const { particleSize, scale, overrideNodeRadius } = state.userConfiguration;
    this.nodeMaterial.uniforms.planeZoomFactor.value = getPlaneScalingFactor(Store.getState().flycam);
    this.nodeMaterial.uniforms.overrideParticleSize.value = particleSize;
    this.nodeMaterial.uniforms.overrideNodeRadius.value = overrideNodeRadius;
    this.nodeMaterial.uniforms.viewportScale.value = scale;
    this.nodeMaterial.uniforms.activeTreeId.value = state.skeletonTracing.activeTreeId;
    this.nodeMaterial.uniforms.activeNodeId.value = state.skeletonTracing.activeNodeId;

    this.prevTracing = tracing;
  }

  getAllNodes() {
    return _.map(this.nodes.buffers, buffer => buffer.mesh);
  }

  getMeshes() {
    return [...this.nodes.buffers, ...this.edges.buffers].map(buffer => buffer.mesh);
  }

  // API

  // recursive Cantor pairing to generate a unique id from a list of numbers.
  combineIds(...numbers: Array<number>) {
    return numbers.reduce((acc, value) => 0.5 * (acc + value) * (acc + value + 1) + value);
  }

  createTree(tree: TreeType) {
    for (const node of _.values(tree.nodes)) {
      this.createNode(tree.treeId, node, tree.color, false);
    }
    for (const edge of tree.edges) {
      const source = tree.nodes[edge.source];
      const target = tree.nodes[edge.target];
      this.createEdge(tree.treeId, source, target, false);
    }
  }

  createNode(treeId: number, node: NodeType, treeColor: Vector3, updateBoundingSphere: boolean = true) {
    debugger
    const id = this.combineIds(node.id, treeId);
    this.create(id, this.nodes, updateBoundingSphere, ({ buffer, index }) => {
      const attributes = buffer.geometry.attributes;
      attributes.position.set(node.position, index * 3);
      attributes.treeColor.set(treeColor, index * 3);
      attributes.radius.array[index] = node.radius;
      attributes.type.array[index] = NodeTypes.NORMAL;
      attributes.nodeId.array[index] = node.id;
      attributes.treeId.array[index] = treeId;
      return _.values(attributes);
    });
  }

  deleteNode(treeId: number, nodeId: number) {
    const id = this.combineIds(nodeId, treeId);
    this.delete(id, this.nodes, ({ buffer, index }) => {
      const attribute = buffer.geometry.attributes.type;
      attribute.array[index] = NodeTypes.INVALID;
      return [attribute];
    });
  }

  updateNodeColor(treeId: number, nodeId: number, color: Vector3) {
    const id = this.combineIds(nodeId, treeId);
    this.update(id, this.nodes, ({ buffer, index }) => {
      const attribute = buffer.geometry.attributes.color;
      attribute.set(color, index * 3);
      return [attribute];
    });
  }

  updateNodeRadius(treeId: number, nodeId: number, radius: number) {
    const id = this.combineIds(nodeId, treeId);
    this.update(id, this.nodes, ({ buffer, index }) => {
      const attribute = buffer.geometry.attributes.radius;
      attribute.array[index] = radius;
      return [attribute];
    });
  }

  updateNodeType(treeId: number, nodeId: number, type: number) {
    const id = this.combineIds(nodeId, treeId);
    this.update(id, this.nodes, ({ buffer, index }) => {
      const attribute = buffer.geometry.attributes.type;
      attribute.array[index] = type;
      return [attribute];
    });
  }

  createEdge(treeId: number, source: NodeType, target: NodeType, updateBoundingSphere: boolean = true) {
    const id = this.combineIds(treeId, source.id, target.id);
    this.create(id, this.edges, updateBoundingSphere, ({ buffer, index }) => {
      const attribute = buffer.geometry.attributes.position;
      attribute.set(source.position, index * 6);
      attribute.set(target.position, index * 6 + 3);
      return [attribute];
    });
  }

  deleteEdge(treeId: number, sourceId: number, targetId: number) {
    const id = this.combineIds(treeId, sourceId, targetId);
    this.delete(id, this.edges, ({ buffer, index }) => {
      const attribute = buffer.geometry.attributes.position;
      attribute.set([0, 0, 0, 0, 0, 0], index * 6);
      return [attribute];
    });
  }

  setVisibility(isVisible: boolean) {
    this.isVisible = isVisible;

    /* for (const mesh of this.getMeshes()) {
      mesh.isVisible = isVisible;
    }
    app.vent.trigger("rerender"); */
  }

  setVisibilityTemporary(/* isVisible: boolean */) {
    // for (const mesh of this.getMeshes()) {
    //   mesh.visible = isVisible && ((mesh.isVisible != null) ? mesh.isVisible : true);
    // }
    // (TODO: still needed?) app.vent.trigger("rerender");
  }

  restoreVisibility() {
//    this.setVisibilityTemporary(this.isVisible);
  }

  toggleVisibility() {
    this.setVisibility(!this.isVisible);
  }

  updateForCam(/* id: OrthoViewType */) {
    /* for (const tree of _.values(this.treeGeometryCache)) {
      //tree.showRadius(id !== OrthoViews.TDView && !Store.getState().userConfiguration.overrideNodeRadius);
    }

    if (id !== OrthoViews.TDView) {
      this.setVisibilityTemporary(this.isVisible);
    }
    this.setVisibilityTemporary(true);*/
  }

  toggleInactiveTreeVisibility() {
    // this.showInactiveTrees = !this.showInactiveTrees;
    // return this.setInactiveTreeVisibility(this.showInactiveTrees);
  }

  setInactiveTreeVisibility(/* isVisible: boolean */) {
    /* for (const mesh of this.getMeshes()) {
      mesh.isVisible = visible;
    }
    const treeGeometry = this.getTreeGeometry(Store.getState().skeletonTracing.activeTreeId);
    if (treeGeometry != null) {
      treeGeometry.edges.isVisible = true;
      treeGeometry.nodes.isVisible = true;
      app.vent.trigger("rerender");
    } */
  }

  setSizeAttenuation(/* sizeAttenuation: boolean */) {
    /* return _.map(this.treeGeometryCache, tree =>
      tree.setSizeAttenuation(sizeAttenuation));*/
  }

  async startNodeHighlightAnimation() {
    const normal = 1.0;
    const highlighted = 2.0;

    await this.animateNodeScale(normal, highlighted);
    await this.animateNodeScale(highlighted, normal);
  }

  animateNodeScale(from, to) {
    return new Promise((resolve, reject) => {
      const setScaleFactor = (scale) => {
        this.nodeMaterial.uniforms.activeNodeScaleFactor.value = scale;
      };

      const tweenAnimation = new TWEEN.Tween({ scaleFactor: from });
      tweenAnimation
      .to({ scaleFactor: to }, 100)
      .onUpdate(function onUpdate() {
        setScaleFactor(this.scaleFactor);
      })
      .onComplete(resolve)
      .onStop(reject)
      .start();
    });
  }
}

export default Skeleton;
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2018 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.auth.email',
  name: 'EmailTokenService',
  extends: 'foam.nanos.auth.token.AbstractTokenService',

  documentation: 'Implementation of Token Service used for verifying email addresses',

  javaImports: [
    'foam.core.FObject',
    'foam.dao.ArraySink',
    'foam.dao.DAO',
    'foam.dao.Sink',
    'foam.mlang.MLang',
    'foam.nanos.app.AppConfig',
    'foam.nanos.auth.token.Token',
    'foam.nanos.notification.email.EmailMessage',
    'foam.util.Emails.EmailsUtility',
    'java.util.Calendar',
    'java.util.HashMap',
    'java.util.List',
    'java.util.UUID'
  ],

  methods: [
    {
      name: 'generateTokenWithParameters',
      javaCode:
`try {
DAO tokenDAO = (DAO) getX().get("tokenDAO");
DAO userDAO  = (DAO) getX().get("localUserDAO");
AppConfig appConfig = (AppConfig) getX().get("appConfig");
String url = appConfig.getUrl()
    .replaceAll("/$", "");

Token token = new Token();
token.setUserId(user.getId());
token.setExpiry(generateExpiryDate());
token.setData(UUID.randomUUID().toString());
token = (Token) tokenDAO.put(token);

EmailMessage message = new EmailMessage();
message.setTo(new String[]{user.getEmail()});

HashMap<String, Object> args = new HashMap<>();
args.put("name", user.getFirstName());
args.put("link", url + "/service/verifyEmail?userId=" + user.getId() + "&token=" + token.getData() + "&redirect=/" );

EmailsUtility.sendEmailFromTemplate(getX(), user, message, "verifyEmail", args);
return true;
} catch(Throwable t) {
  t.printStackTrace();
  return false;
}`
  },
    {
      name: 'processToken',
      javaCode:
`DAO userDAO = (DAO) getX().get("localUserDAO");
DAO tokenDAO = (DAO) getX().get("tokenDAO");
Calendar calendar = Calendar.getInstance();

Sink sink = new ArraySink();
sink = tokenDAO.where(MLang.AND(
  MLang.EQ(Token.USER_ID, user.getId()),
  MLang.EQ(Token.PROCESSED, false),
  MLang.GT(Token.EXPIRY, calendar.getTime()),
  MLang.EQ(Token.DATA, token)
)).limit(1).select(sink);

List list = ((ArraySink) sink).getArray();
if ( list == null || list.size() == 0 ) {
  // token not found
  throw new RuntimeException("Token not found");
}

// set token processed to true
FObject result = (FObject) list.get(0);
Token clone = (Token) result.fclone();
clone.setProcessed(true);
tokenDAO.put(clone);

// set user email verified to true
user.setEmailVerified(true);
userDAO.put(user);
return true;`
    }
  ]
});
<commit_msg>Changed tokenDAO to localTokenDAO<commit_after>/**
 * @license
 * Copyright 2018 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.auth.email',
  name: 'EmailTokenService',
  extends: 'foam.nanos.auth.token.AbstractTokenService',

  documentation: 'Implementation of Token Service used for verifying email addresses',

  javaImports: [
    'foam.core.FObject',
    'foam.dao.ArraySink',
    'foam.dao.DAO',
    'foam.dao.Sink',
    'foam.mlang.MLang',
    'foam.nanos.app.AppConfig',
    'foam.nanos.auth.token.Token',
    'foam.nanos.notification.email.EmailMessage',
    'foam.util.Emails.EmailsUtility',
    'java.util.Calendar',
    'java.util.HashMap',
    'java.util.List',
    'java.util.UUID'
  ],

  methods: [
    {
      name: 'generateTokenWithParameters',
      javaCode:
`try {
DAO tokenDAO = (DAO) getX().get("localTokenDAO");
DAO userDAO  = (DAO) getX().get("localUserDAO");
AppConfig appConfig = (AppConfig) getX().get("appConfig");
String url = appConfig.getUrl()
    .replaceAll("/$", "");

Token token = new Token();
token.setUserId(user.getId());
token.setExpiry(generateExpiryDate());
token.setData(UUID.randomUUID().toString());
token = (Token) tokenDAO.put(token);

EmailMessage message = new EmailMessage();
message.setTo(new String[]{user.getEmail()});

HashMap<String, Object> args = new HashMap<>();
args.put("name", user.getFirstName());
args.put("link", url + "/service/verifyEmail?userId=" + user.getId() + "&token=" + token.getData() + "&redirect=/" );

EmailsUtility.sendEmailFromTemplate(getX(), user, message, "verifyEmail", args);
return true;
} catch(Throwable t) {
  t.printStackTrace();
  return false;
}`
  },
    {
      name: 'processToken',
      javaCode:
`DAO userDAO = (DAO) getX().get("localUserDAO");
DAO tokenDAO = (DAO) getX().get("localTokenDAO");
Calendar calendar = Calendar.getInstance();

Sink sink = new ArraySink();
sink = tokenDAO.where(MLang.AND(
  MLang.EQ(Token.USER_ID, user.getId()),
  MLang.EQ(Token.PROCESSED, false),
  MLang.GT(Token.EXPIRY, calendar.getTime()),
  MLang.EQ(Token.DATA, token)
)).limit(1).select(sink);

List list = ((ArraySink) sink).getArray();
if ( list == null || list.size() == 0 ) {
  // token not found
  throw new RuntimeException("Token not found");
}

// set token processed to true
FObject result = (FObject) list.get(0);
Token clone = (Token) result.fclone();
clone.setProcessed(true);
tokenDAO.put(clone);

// set user email verified to true
user.setEmailVerified(true);
userDAO.put(user);
return true;`
    }
  ]
});
<|endoftext|>