<commit_before>/**
* Unit conversion table used by measureTool to convert metric units to others,
* and to select appropriate units based on size of value.
*/
lingcod.measureConvTable = [
    [ 'metric',
        [ // distance measures
            ['m', 1, 500 ], // switch to km at 1/2 km
            ['km', 0.001, null ]
        ],
        [ // area measures
            ['sq m', 1, 100000 ], // switch to sq km at 1/10 sq km
            ['sq km', 0.000001, null ]
        ]
    ],
    [ 'english',
        [ // distance measures
            ['ft', 3.2808399, 0.9144 ], // switch to yards at 1 yd
            //['yd', 1.0936133, 804.672 ], // switch to miles at 1/2 mile
            ['mi', 0.000621371192, null ]
        ],
        [ // area measures
            ['sq ft', 10.7639104, 25899.8811 ], // switch to sq miles at 0.01 sq miles
            //['sq yd', 1.19599005, 1294994.06 ], // switch to sq miles at 1/2 sq mile
            ['sq mi', 0.000000386102159, null ]
        ]
    ],
    [ 'nautical',
        [ // distance measures
            ['naut mi', 0.000539956803, null ]
        ],
        [ // area measures
            ['sq naut mi', 0.00000029155335, null ]
        ]
    ]
];

/**
 * Creates a new measurement tool.
 *
 * @constructor
 */
lingcod.measureTool = function() {
    this.placemark = null;
    this.distTarget = null;
    this.areaTarget = null;
    this.gex = null;
    this.area = 0.0;
    this.distance = 0.0;
    this.units = 'metric';
    this.area_unit = 'sq m';
    this.distance_unit = 'm';
};

lingcod.measureTool.prototype.clear = function()
{
    this.area = 0.0;
    this.distance = 0.0;

    if ( this.distTarget )
    {
        this.gex.edit.endEditLineString( this.placemark.getGeometry() );
        document.getElementById(this.distTarget).innerHTML = 'N/A';
        this.distTarget = null;
    }

    if ( this.areaTarget )
    {
        this.gex.edit.endEditLineString( this.placemark.getGeometry().getOuterBoundary() );
        document.getElementById(this.areaTarget).innerHTML = 'N/A';
        this.areaTarget = null;
    }

    this.gex.dom.removeObject( this.placemark );
    this.placemark = null;
};

/**
 * Set the measure tool's system of measurement for reporting (still uses metric internally)
 * @param {String} units The name of the system of measure, i.e. 'english', 'metric', 'nautical'
 */
lingcod.measureTool.prototype.setUnits = function( units ) {
    this.units = units;
    this.updateDistance();
};

/**
 * Start accepting user input for shape-draw. Sets callbacks to keep measures updated and to switch to edit mode on completion.
 * @param {GEarthExtensions} gex The handle to the GEarthExtensions object
 * @param {String} areaSpanId The id of an HTML tag whose innerHTML will be overwritten with measure results.
 */
lingcod.measureTool.prototype.measureArea = function( gex, areaSpanId ) {
    var self = this;
    this.gex = gex;
    this.areaTarget = areaSpanId;

    this.placemark = gex.dom.addPlacemark({
        polygon: [],
        style: {
            line: { width: 2, color: '#ff0' },
            poly: { color: '8000ffff' }
        }
    });

    var drawLineStringOptions = {
        bounce: false,
        drawCallback: function() {
            self.updateDistance();
        },
        finishCallback: function() {
            var editLineStringOptions = {
                editCallback: function() {
                    self.updateDistance();
                }
            }
            gex.edit.editLineString( self.placemark.getGeometry().getOuterBoundary(), editLineStringOptions );
        }
    };

    gex.edit.drawLineString( this.placemark.getGeometry().getOuterBoundary(), drawLineStringOptions );
};

/**
 * Start accepting user input for line-draw. Sets callbacks to keep measures updated and to switch to edit mode on completion.
 * @param {GEarthExtensions} gex The handle to the GEarthExtensions object
 * @param {String} distSpanId The id of an HTML tag whose innerHTML will be overwritten with measure results.
 */
lingcod.measureTool.prototype.measureDistance = function( gex, distSpanId ) {
    var self = this;
    this.gex = gex;
    this.distTarget = distSpanId;

    this.placemark = gex.dom.addPlacemark({
        lineString: [],
        style: {
            line: { width: 2, color: '#ff0' }
        }
    });

    var drawLineStringOptions = {
        bounce: false,
        drawCallback: function() {
            self.updateDistance();
        },
        finishCallback: function() {
            var editLineStringOptions = {
                editCallback: function() {
                    self.updateDistance();
                }
            }
            gex.edit.editLineString( self.placemark.getGeometry(), editLineStringOptions );
        }
    };

    gex.edit.drawLineString( this.placemark.getGeometry(), drawLineStringOptions );
};

/**
 * Start accepting user input for shape-draw. Sets callbacks to keep measures updated and to switch to edit mode on completion.
 * @param {String} measure_type_str 'distance' or 'area' (assumes 'distance' if not 'area')
 * @param {Number} value The metric value to convert to the measureTool's currently set units.
 * @return [{Number}, {String}] The converted value and its units.
 */
lingcod.measureTool.prototype.convertMetricValue = function( measure_type_str, value )
{
    var SYSTEM = 0;
    var DISTANCE_MAPPINGS = 1;
    var AREA_MAPPINGS = 2;

    var UNIT = 0;
    var CONVERSION_FACTOR = 1;
    var RANGE_MAX = 2;

    var measure_type = DISTANCE_MAPPINGS;
    if ( measure_type_str == 'area' )
        measure_type = AREA_MAPPINGS;

    for ( var system_iter = 0; system_iter < lingcod.measureConvTable.length; system_iter++ )
    {
        if ( lingcod.measureConvTable[system_iter][SYSTEM] == this.units )
        {
            for ( var unit_iter = 0; unit_iter < lingcod.measureConvTable[system_iter][measure_type].length; unit_iter++ )
            {
                if ( value < lingcod.measureConvTable[system_iter][measure_type][unit_iter][RANGE_MAX]
                    || lingcod.measureConvTable[system_iter][measure_type][unit_iter][RANGE_MAX] == null )
                    return [ value *
                            lingcod.measureConvTable[system_iter][measure_type][unit_iter][CONVERSION_FACTOR],
                        lingcod.measureConvTable[system_iter][measure_type][unit_iter][UNIT] ];
            }
        }
    }

    return[ 0, 'invalid units set' ];
};

/**
 * Calculate the current measurement(s) based on the state of the measureTool's placemark object.
 * Writes values to target HTML object and stores internally.
 */
lingcod.measureTool.prototype.updateDistance = function()
{
    if ( this.areaTarget )
    {
        this.area = new geo.Polygon(this.placemark.getGeometry()).area();
        converted_val_n_unit = this.convertMetricValue( 'area', this.area );

        this.area = converted_val_n_unit[0];
        this.area_unit = converted_val_n_unit[1];

        document.getElementById(this.areaTarget).innerHTML =
            this.area.toFixed(2) + ' ' + this.area_unit;
    }

    if ( this.distTarget )
    {
        this.distance = new geo.Path(this.placemark.getGeometry()).distance();
        converted_val_n_unit = this.convertMetricValue( 'distance', this.distance );

        this.distance = converted_val_n_unit[0];
        this.distance_unit = converted_val_n_unit[1];

        document.getElementById(this.distTarget).innerHTML =
            this.distance.toFixed(2) + ' ' + this.distance_unit;
    }
};
<commit_msg>fix measureTool's re-init capacity<commit_after>/**
* Unit conversion table used by measureTool to convert metric units to others,
* and to select appropriate units based on size of value.
*/
lingcod.measureConvTable = [
    [ 'metric',
        [ // distance measures
            ['m', 1, 500 ], // switch to km at 1/2 km
            ['km', 0.001, null ]
        ],
        [ // area measures
            ['sq m', 1, 100000 ], // switch to sq km at 1/10 sq km
            ['sq km', 0.000001, null ]
        ]
    ],
    [ 'english',
        [ // distance measures
            ['ft', 3.2808399, 0.9144 ], // switch to yards at 1 yd
            //['yd', 1.0936133, 804.672 ], // switch to miles at 1/2 mile
            ['mi', 0.000621371192, null ]
        ],
        [ // area measures
            ['sq ft', 10.7639104, 25899.8811 ], // switch to sq miles at 0.01 sq miles
            //['sq yd', 1.19599005, 1294994.06 ], // switch to sq miles at 1/2 sq mile
            ['sq mi', 0.000000386102159, null ]
        ]
    ],
    [ 'nautical',
        [ // distance measures
            ['naut mi', 0.000539956803, null ]
        ],
        [ // area measures
            ['sq naut mi', 0.00000029155335, null ]
        ]
    ]
];

/**
 * Creates a new measurement tool.
 *
 * @constructor
 */
lingcod.measureTool = function() {
    this.placemark = null;
    this.distTarget = null;
    this.areaTarget = null;
    this.gex = null;
    this.area = 0.0;
    this.distance = 0.0;
    this.units = 'metric';
    this.area_unit = 'sq m';
    this.distance_unit = 'm';
};

lingcod.measureTool.prototype.clear = function()
{
    this.area = 0.0;
    this.distance = 0.0;

    if ( this.placemark )
    {
        if ( this.distTarget )
        {
            this.gex.edit.endEditLineString( this.placemark.getGeometry() );
            document.getElementById(this.distTarget).innerHTML = 'N/A';
            this.distTarget = null;
        }

        if ( this.areaTarget )
        {
            this.gex.edit.endEditLineString( this.placemark.getGeometry().getOuterBoundary() );
            document.getElementById(this.areaTarget).innerHTML = 'N/A';
            this.areaTarget = null;
        }

        this.gex.dom.removeObject( this.placemark );
        this.placemark = null;
    }
};

/**
 * Set the measure tool's system of measurement for reporting (still uses metric internally)
 * @param {String} units The name of the system of measure, i.e. 'english', 'metric', 'nautical'
 */
lingcod.measureTool.prototype.setUnits = function( units ) {
    this.units = units;
    this.updateDistance();
};

/**
 * Start accepting user input for shape-draw. Sets callbacks to keep measures updated and to switch to edit mode on completion.
 * @param {GEarthExtensions} gex The handle to the GEarthExtensions object
 * @param {String} areaSpanId The id of an HTML tag whose innerHTML will be overwritten with measure results.
 */
lingcod.measureTool.prototype.measureArea = function( gex, areaSpanId )
{
    var self = this;
    this.gex = gex;

    this.clear();

    this.areaTarget = areaSpanId;

    this.placemark = gex.dom.addPlacemark({
        polygon: [],
        style: {
            line: { width: 2, color: '#ff0' },
            poly: { color: '8000ffff' }
        }
    });

    var drawLineStringOptions = {
        bounce: false,
        drawCallback: function() {
            self.updateDistance();
        },
        finishCallback: function() {
            var editLineStringOptions = {
                editCallback: function() {
                    self.updateDistance();
                }
            }
            gex.edit.editLineString( self.placemark.getGeometry().getOuterBoundary(), editLineStringOptions );
        }
    };

    gex.edit.drawLineString( this.placemark.getGeometry().getOuterBoundary(), drawLineStringOptions );
};

/**
 * Start accepting user input for line-draw. Sets callbacks to keep measures updated and to switch to edit mode on completion.
 * @param {GEarthExtensions} gex The handle to the GEarthExtensions object
 * @param {String} distSpanId The id of an HTML tag whose innerHTML will be overwritten with measure results.
 */
lingcod.measureTool.prototype.measureDistance = function( gex, distSpanId )
{
    var self = this;
    this.gex = gex;

    this.clear();

    this.distTarget = distSpanId;

    this.placemark = gex.dom.addPlacemark({
        lineString: [],
        style: {
            line: { width: 2, color: '#ff0' }
        }
    });

    var drawLineStringOptions = {
        bounce: false,
        drawCallback: function() {
            self.updateDistance();
        },
        finishCallback: function() {
            var editLineStringOptions = {
                editCallback: function() {
                    self.updateDistance();
                }
            }
            gex.edit.editLineString( self.placemark.getGeometry(), editLineStringOptions );
        }
    };

    gex.edit.drawLineString( this.placemark.getGeometry(), drawLineStringOptions );
};

/**
 * Start accepting user input for shape-draw. Sets callbacks to keep measures updated and to switch to edit mode on completion.
 * @param {String} measure_type_str 'distance' or 'area' (assumes 'distance' if not 'area')
 * @param {Number} value The metric value to convert to the measureTool's currently set units.
 * @return [{Number}, {String}] The converted value and its units.
 */
lingcod.measureTool.prototype.convertMetricValue = function( measure_type_str, value )
{
    var SYSTEM = 0;
    var DISTANCE_MAPPINGS = 1;
    var AREA_MAPPINGS = 2;

    var UNIT = 0;
    var CONVERSION_FACTOR = 1;
    var RANGE_MAX = 2;

    var measure_type = DISTANCE_MAPPINGS;
    if ( measure_type_str == 'area' )
        measure_type = AREA_MAPPINGS;

    for ( var system_iter = 0; system_iter < lingcod.measureConvTable.length; system_iter++ )
    {
        if ( lingcod.measureConvTable[system_iter][SYSTEM] == this.units )
        {
            for ( var unit_iter = 0; unit_iter < lingcod.measureConvTable[system_iter][measure_type].length; unit_iter++ )
            {
                if ( value < lingcod.measureConvTable[system_iter][measure_type][unit_iter][RANGE_MAX]
                    || lingcod.measureConvTable[system_iter][measure_type][unit_iter][RANGE_MAX] == null )
                    return [ value *
                            lingcod.measureConvTable[system_iter][measure_type][unit_iter][CONVERSION_FACTOR],
                        lingcod.measureConvTable[system_iter][measure_type][unit_iter][UNIT] ];
            }
        }
    }

    return[ 0, 'invalid units set' ];
};

/**
 * Calculate the current measurement(s) based on the state of the measureTool's placemark object.
 * Writes values to target HTML object and stores internally.
 */
lingcod.measureTool.prototype.updateDistance = function()
{
    if ( this.areaTarget )
    {
        this.area = new geo.Polygon(this.placemark.getGeometry()).area();
        converted_val_n_unit = this.convertMetricValue( 'area', this.area );

        this.area = converted_val_n_unit[0];
        this.area_unit = converted_val_n_unit[1];

        document.getElementById(this.areaTarget).innerHTML =
            this.area.toFixed(2) + ' ' + this.area_unit;
    }

    if ( this.distTarget )
    {
        this.distance = new geo.Path(this.placemark.getGeometry()).distance();
        converted_val_n_unit = this.convertMetricValue( 'distance', this.distance );

        this.distance = converted_val_n_unit[0];
        this.distance_unit = converted_val_n_unit[1];

        document.getElementById(this.distTarget).innerHTML =
            this.distance.toFixed(2) + ' ' + this.distance_unit;
    }
};
<|endoftext|>


<commit_before>define( [

  "dojo/_base/declare"
, "dojo/topic"
, "dojo/on"
, "dojo/when"

, "dijit/_WidgetBase"
, "dijit/_TemplatedMixin"
, "dijit/_WidgetsInTemplateMixin"
, "dijit/form/TextBox"
, "dijit/Destroyable"

, "hotplate/hotDojoSubmit/defaultSubmit"

, "hotplate/hotDojoWidgets/AlertBar"
, "hotplate/hotDojoWidgets/_OverlayMixin"

], function(

  declare
, topic
, on
, when

, _WidgetBase
, _TemplatedMixin
, _WidgetsInTemplateMixin
, TextBox
, Destroyable

, ds

, AlertBar
, _OverlayMixin

){

  return declare( [ _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, _OverlayMixin, Destroyable ] , {

    templateString: '

You need to set a template for this widget!

',

    // These will be defined by the templateString
    formWidget: null,
    alertBarWidget: null,
    buttonWidget: null,

    alertBarDomPlacement: 'first',

    storeName: '',
    store: null,

    //storeFields: {},
    recordId: null,

    // Default values for editing form
    defaultValues: {},

    resetOnSuccess: false,

    baseClass: 'editing-widget',

     // The store for records
    store: null,

    _firstResize: true,

    postCreate: function(){
      this.inherited(arguments);

      var self = this;

      // If there is no alertBarWidget defined, then make one up and
      // place it at the top of the form
      if( ! this.alertBarWidget ){
        this.alertBarWidget = new AlertBar();
        this.alertBarWidget.placeAt( this.formWidget.containerNode, this.alertBarDomPlacement );
      }

      // Artificially add a hidden id field to the form. This way, once the form has loaded,
      // the id will be stored and it will then be re-submitted
      if( self.recordId ) {
        var idTextBox = new TextBox( { type:"hidden", name: self.store.idProperty, value: self.recordId } );
        idTextBox.placeAt( this.formWidget.containerNode, 'first' );
      }

      self.own(

        // When the form's overlay is clicked, try and show the form again
        on( self, 'overlayClick', function( e ){
          self.set( 'overlayStatus', { overlayed: false, clickable: false }  ); // CLICK OFF
          self.loadInfo();
        }),

        // Check self the record doesn't get updated by remote
        topic.subscribe('storeRecordUpdate', function( from, message, remote ){

          if( remote ){
            if( message.storeName === self.storeName && message.objectId == self.recordId ){
              self.set( 'overlayStatus', { overlayed: true, clickable: true }  ); // CLICK ON
            }
          }
        })
      );

      // Submit form, trying to save values
      this.formWidget.onSubmit = ds.defaultSubmit(this.formWidget, this.buttonWidget, function(){

        // Set the values about to be saved
        var formValues = self.formWidget.get('value');

        self.set('overlayStatus', { overlayed: true, clickable: false  } ); // LOADING ON

        // Try to save the values
        when( self.store.put( formValues )) .then(
          ds.UIMsg( self.buttonWidget, self.alertBarWidget, 'Saved!' ),
          ds.UIErrorMsg( self.formWidget, self.buttonWidget, self.alertBarWidget )
        ).then(
          function( res ){
            self.emit( 'successfulsubmit', { bubbles: false }  );
            self.set( 'overlayStatus', { overlayed: false, clickable: false  } ); // LOADING OFF
            if( self.resetOnSuccess ){
              self.formWidget.reset();
            }
            return res;
          },
          function( err ){
            self.emit( 'unsuccessfulsubmit', { bubbles: false }  );
            self.set( 'overlayStatus', { overlayed: false, clickable: false  } ); // LOADING OFF
            throw( err );
          }
        );

      }); // this.formWidget.onSubmit
    }, // postCreate()

    // It calls updateInfo in the first widget's resize
    resize: function(){
      if( this._firstResize ){
        this._firstResize = false;
        this.loadInfo();
      }
      this.inherited(arguments);
    },

    // loadInfo
    loadInfo: function(){

      this.inherited(arguments);

      var self = this;

      //console.log("loadInfo called for" , self.storeName );

      // ****************************************************************
      // There is a recordId set: it's the update of an existing value
      // ****************************************************************
      if( self.recordId ){

        // Set the overlay if loading. By default, this widget is overlayed
        this.set( 'overlayStatus', { overlayed: true, clickable: false } ); // RELOAD ON

        when( this.store.get( this.recordId )).then(
          ds.UIMsg(),
          ds.UIErrorMsg(null, null, this.alertBarWidget )
        ).then(
          function(res){

            // OK things worked out: the overlay can go, values are assigned to form
            self.set( 'overlayStatus', { overlayed: false, clickable: false } ); // RELOAD OFF
            self.formWidget.set( 'value', res ); // Thanks Karl Tiedt :D

            // Return for chaining...
            return res;

          },
          function(err){
            self.set( 'overlayStatus', { overlayed: false, clickable: false } ); // RELOAD OFF - Take out the unclickable overlay
            self.set( 'overlayStatus', { overlayed: true, clickable: true } ); // CLICK ON -- Put a new clickable one in

            // Error handlers need to rethrow...
            throw( err );
          }
        );

      // ****************************************************************
      // There is no recordId set: simply show defaults
      // ****************************************************************
      } else {
        self.formWidget.set( 'value', self.defaultValues ); // Thanks Karl Tiedt :D

      }
    },

    destroy: function(){
      this.inherited(arguments);
      // console.log("EDITING WIDGET DESTROYED!");
    },

  });

});

<commit_msg>Added code so that clicking on a placeHolder will actually focus on the sibling widget<commit_after>define( [

  "dojo/_base/declare"
, "dojo/topic"
, "dojo/on"
, "dojo/when"
, "dojo/dom-class"

, "dijit/_WidgetBase"
, "dijit/_TemplatedMixin"
, "dijit/_WidgetsInTemplateMixin"
, "dijit/form/TextBox"
, "dijit/Destroyable"

, "hotplate/hotDojoSubmit/defaultSubmit"

, "hotplate/hotDojoWidgets/AlertBar"
, "hotplate/hotDojoWidgets/_OverlayMixin"

], function(

  declare
, topic
, on
, when
, domClass

, _WidgetBase
, _TemplatedMixin
, _WidgetsInTemplateMixin
, TextBox
, Destroyable

, ds

, AlertBar
, _OverlayMixin

){

  return declare( [ _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, _OverlayMixin, Destroyable ] , {

    templateString: '

You need to set a template for this widget!

',

    // These will be defined by the templateString
    formWidget: null,
    alertBarWidget: null,
    buttonWidget: null,

    alertBarDomPlacement: 'first',

    storeName: '',
    store: null,

    //storeFields: {},
    recordId: null,

    // Default values for editing form
    defaultValues: {},

    resetOnSuccess: false,

    baseClass: 'editing-widget',

     // The store for records
    store: null,

    _firstResize: true,

    postCreate: function(){
      this.inherited(arguments);

      var self = this;

      // If there is no alertBarWidget defined, then make one up and
      // place it at the top of the form
      if( ! this.alertBarWidget ){
        this.alertBarWidget = new AlertBar();
        this.alertBarWidget.placeAt( this.formWidget.containerNode, this.alertBarDomPlacement );
      }

      // Artificially add a hidden id field to the form. This way, once the form has loaded,
      // the id will be stored and it will then be re-submitted
      if( self.recordId ) {
        var idTextBox = new TextBox( { type:"hidden", name: self.store.idProperty, value: self.recordId } );
        idTextBox.placeAt( this.formWidget.containerNode, 'first' );
      }

      self.own(

        // This is to make sure that DnD, if active, doesn't do a nasty preventDefault()
        // and therefore stops text selecting and moving cursor with mouse
        // Finding this one was a bit of a birch
        on( self.domNode, 'mousedown', function( e ){

          // For some reason, stopping propagation here will prevent focus
          // from happening if placeHolder there (!?). So, forcing focus on
          // previousSibling IF the click was on a placeholder;
          // TODO: UNDERSTAND WHY. This may have side effects. Stopping
          // propagation doesn't prevent the default from happening
          // if the placeHolder isn't there...
          if( domClass.contains( e.srcElement, 'dijitPlaceHolder' ) ){
            e.srcElement.previousSibling.focus();
          }

          // Stopping propagation, so that event won't reach the DnD
          // element above
          e.stopPropagation();
        }),

        // When the form's overlay is clicked, try and show the form again
        on( self, 'overlayClick', function( e ){
          self.set( 'overlayStatus', { overlayed: false, clickable: false }  ); // CLICK OFF
          self.loadInfo();
        }),

        // Check self the record doesn't get updated by remote
        topic.subscribe('storeRecordUpdate', function( from, message, remote ){

          if( remote ){
            if( message.storeName === self.storeName && message.objectId == self.recordId ){
              self.set( 'overlayStatus', { overlayed: true, clickable: true }  ); // CLICK ON
            }
          }
        })
      );

      // Submit form, trying to save values
      this.formWidget.onSubmit = ds.defaultSubmit(this.formWidget, this.buttonWidget, function(){

        // Set the values about to be saved
        var formValues = self.formWidget.get('value');

        self.set('overlayStatus', { overlayed: true, clickable: false  } ); // LOADING ON

        // Try to save the values
        when( self.store.put( formValues )) .then(
          ds.UIMsg( self.buttonWidget, self.alertBarWidget, 'Saved!' ),
          ds.UIErrorMsg( self.formWidget, self.buttonWidget, self.alertBarWidget )
        ).then(
          function( res ){
            self.emit( 'successfulsubmit', { bubbles: false }  );
            self.set( 'overlayStatus', { overlayed: false, clickable: false  } ); // LOADING OFF
            if( self.resetOnSuccess ){
              self.formWidget.reset();
            }
            return res;
          },
          function( err ){
            self.emit( 'unsuccessfulsubmit', { bubbles: false }  );
            self.set( 'overlayStatus', { overlayed: false, clickable: false  } ); // LOADING OFF
            throw( err );
          }
        );

      }); // this.formWidget.onSubmit
    }, // postCreate()

    // It calls updateInfo in the first widget's resize
    resize: function(){
      if( this._firstResize ){
        this._firstResize = false;
        this.loadInfo();
      }
      this.inherited(arguments);
    },

    // loadInfo
    loadInfo: function(){

      this.inherited(arguments);

      var self = this;

      //console.log("loadInfo called for" , self.storeName );

      // ****************************************************************
      // There is a recordId set: it's the update of an existing value
      // ****************************************************************
      if( self.recordId ){

        // Set the overlay if loading. By default, this widget is overlayed
        this.set( 'overlayStatus', { overlayed: true, clickable: false } ); // RELOAD ON

        when( this.store.get( this.recordId )).then(
          ds.UIMsg(),
          ds.UIErrorMsg(null, null, this.alertBarWidget )
        ).then(
          function(res){

            // OK things worked out: the overlay can go, values are assigned to form
            self.set( 'overlayStatus', { overlayed: false, clickable: false } ); // RELOAD OFF
            self.formWidget.set( 'value', res ); // Thanks Karl Tiedt :D

            // Return for chaining...
            return res;

          },
          function(err){
            self.set( 'overlayStatus', { overlayed: false, clickable: false } ); // RELOAD OFF - Take out the unclickable overlay
            self.set( 'overlayStatus', { overlayed: true, clickable: true } ); // CLICK ON -- Put a new clickable one in

            // Error handlers need to rethrow...
            throw( err );
          }
        );

      // ****************************************************************
      // There is no recordId set: simply show defaults
      // ****************************************************************
      } else {
        self.formWidget.set( 'value', self.defaultValues ); // Thanks Karl Tiedt :D

      }
    },

    destroy: function(){
      this.inherited(arguments);
      // console.log("EDITING WIDGET DESTROYED!");
    },

  });

});

<|endoftext|>


<commit_before>import THREE from 'three';
import Hammer from  'hammerjs';
import TouchEmulator from 'hammer-touchemulator';

import { Renderer }  from 'popmotion';
import { _ } from 'meteor/underscore';
import { tween, parallel, physics, easing }  from 'popmotion';
import { setSelectors, getSelectors } from './popmotionTHREESelectors.js';
import { store } from '/imports/store/index.js';

const LZCONST = 1.5;
const convertLevelToScale = x => Math.pow(LZCONST, x);
const convertScaleToLevel = x => Math.log(x) / Math.log(LZCONST);

class popmotionTHREERenderer extends Renderer {
	constructor(props) {
		super(props);
		this.onWindowResize = this.onWindowResize.bind(this);
		this.zoomTo = this.zoomTo.bind(this);
		this.panToStartDrag = this.panToStartDrag.bind(this);
		this.panToContinueDrag = this.panToContinueDrag.bind(this);
		this.panToEndWithInertia = this.panToEndWithInertia.bind(this);
		this.panToCenterZoom = this.panToCenterZoom.bind(this);

		// setup a clock, scene, camera and THREE renderer
		this.namespace = {};
		this.dragInertiaAction = undefined;
		this.dragStartPosition = { x: 0, y: 0 };
		this.container = this.props.container;
		this.clock = new THREE.Clock();
		this.scene = new THREE.Scene();
		this.scene.background = new THREE.Color(0x09253A);
		this.camera = new THREE.PerspectiveCamera(45, this.getWindowSize().aspect, 0.1, 1000);
		this.camera.position.z = 10;
		this.renderer = new THREE.WebGLRenderer({ preserveDrawingBuffer: true });
		this.renderer.localClippingEnabled = true;
		this.container.append(this.renderer.domElement);

		// setup touch events for this renderer
		this.touch = new Hammer(this.container, { });
		this.touch.get('pan').set({ direction: Hammer.DIRECTION_ALL });
		this.touch.get('pinch').set({ enable: true });

		// TouchEmulator();

		// setup Orbit Controls
		if (this.props.useOrbitControls) {
			this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
			this.controls.enableDamping = true;
			this.controls.dampingFactor = 0.05;
			this.controls.rotateSpeed = 0.05;
			this.controls.minAzimuthAngle = - Math.PI / 2;
			this.controls.maxAzimuthAngle = Math.PI / 2;
		}

		// setup lighting
		var ambientLight = new THREE.AmbientLight(0xffffff);
		this.scene.add(ambientLight);

		var lights = [];
		lights[ 0 ] = new THREE.PointLight(0xffffff, 1, 0);
		lights[ 1 ] = new THREE.PointLight(0xffffff, 1, 0);
		lights[ 2 ] = new THREE.PointLight(0x888888, 1, 0);

		lights[ 0 ].position.set(0, 200, 0);
		lights[ 1 ].position.set(100, 200, 100);
		lights[ 2 ].position.set(- 100, - 200, - 100);

		this.scene.add(lights[ 0 ]);
		this.scene.add(lights[ 1 ]);
		this.scene.add(lights[ 2 ]);

		// listen for resize events, and setup for initial size
		window.addEventListener('resize', this.onWindowResize, false);
		this.onWindowResize();
	}

	panToStartDrag(ev) {
		if (this.dragInertiaAction) this.dragInertiaAction.stop();
		this.dragStartPosition = this.onRead('camera.xy');
	}

	panToContinueDrag(ev) {
		this.set('camera.xy', {
			x: this.dragStartPosition.x - ev.deltaX / 100 / this.camera.zoom,
			y: this.dragStartPosition.y + ev.deltaY / 100 / this.camera.zoom,
		});
	}

	panToEndWithInertia(ev) {
		this.dragInertiaAction = physics({ velocity: 20, friction: 0.3 })
			.output(v => this.set('camera.xy', {
				x: this.dragStartPosition.x - ev.deltaX / 100 / this.camera.zoom - ev.velocityX * v / this.camera.zoom,
				y: this.dragStartPosition.y + ev.deltaY / 100 / this.camera.zoom + ev.velocityY * v / this.camera.zoom,
			}))
			.start();
	}

	panToCenterZoom(startScale, endScale) {
		if (this.dragInertiaAction) this.dragInertiaAction.stop();
		const sp = this.onRead('camera.xy');
		// parallel([
		// 	tween({ from: sp.x, to: sp.x / startScale * endScale })
		// 		.output(v => this.set('camera.x', v)),
		// 	tween({ from: sp.y, to: sp.y / startScale * endScale })
		// 		.output(v => this.set('camera.y', v)),
		// ]).start();
	}

	zoomIn() {
		const startScale = this.onRead('camera.zoom');
		const endScale = convertLevelToScale(convertScaleToLevel(startScale) + 1);
		this.panToCenterZoom(startScale, endScale);
		tween({ from: startScale, to: endScale })
			.output(v => this.set('camera.zoom', v))
			.start();
	}

	zoomOut() {
		const startScale = this.onRead('camera.zoom');
		const endScale = convertLevelToScale(convertScaleToLevel(startScale) - 1);
		this.panToCenterZoom(startScale, endScale);
		tween({ from: startScale, to: endScale })
			.output(v => this.set('camera.zoom', v))
			.start();
	}

	zoomTo(endScale) {
		const startScale = this.get('camera.zoom');
		this.panToCenterZoom(startScale, endScale);
		tween({ from: startScale, to: endScale })
			.output(v => this.set('camera.zoom', v))
			.start();
	}

	viewPlan() {
		tween({ from: this.get('camera.rotateX'), to: Math.PI/2 })
			.output(v => this.set('camera.rotateX', v))
			.start();
	}

	viewElev() {
		tween({ from: this.get('camera.rotateX'), to: 0 })
			.output(v => this.set('camera.rotateX', v))
			.start();
	}

	thresholdTo(endThreshold) {
		const startThreshold = this.get('img.u_threshold');
		store.set('threshold', { value: endThreshold });
		tween({ from: startThreshold, to: endThreshold })
			.output(v => this.set('img.u_threshold', v))
			.start();
	}

	thresholdUp() {
		const startThreshold = this.get('img.u_threshold');
		store.set('threshold', { value: startThreshold + 1 });
		tween({ from: startThreshold, to: startThreshold + 1 })
			.output(v => this.set('img.u_threshold', v))
			.start();
	}

	thresholdDown() {
		const startThreshold = this.get('img.u_threshold');
		store.set('threshold', { value: startThreshold - 1 });
		tween({ from: startThreshold, to: startThreshold - 1 })
			.output(v => this.set('img.u_threshold', v))
			.start();
	}

	getWindowSize() {
		const size = {
			width: window.innerWidth - this.container.offsetLeft,
			height: window.innerHeight - this.container.offsetTop - 4,
		};
		size.aspect = size.width / size.height;
		return size;
	}
	onWindowResize() {
		const size = this.getWindowSize();
		this.set('camera.aspect', size.aspect);
		this.renderer.setSize(size.width, size.height);
		this.renderer.setPixelRatio(window.devicePixelRatio);
	}

	addTHREEObjects(obj, parent) {
		// add THREE objects to a parent (or the scene by default)
	}

	setNameSpace(keys) {
		this.namespace = {
			camera: this.camera,
			scene: this.scene,
			...keys };
		return this;
	}

	onRender() {
		// Apply all the changed values to the THREE model
		_.each(this.changedValues, key => {
			const parts = key.split('.');
			const keyobj = this.namespace[parts[0]];
			const keyparam = parts[1];
			if (setSelectors[keyparam]) {
				setSelectors[keyparam](keyobj, this.state[key]);
			} else {
				console.log(`cannot set value for key:${keyparam}`);
			}
		});

		// this.camera.lookAt(new THREE.Vector3(0,0,0));
		if (this.controls && this.controls.update)
			this.controls.update();

		// Render the THREE model
		this.renderer.render( this.scene, this.camera );
	}

	onRead(key) {
		const parts = key.split('.');
		const keyobj = this.namespace[parts[0]];
		const keyparam = parts[1];
		return getSelectors[keyparam] ? getSelectors[keyparam](keyobj) : undefined;
	}
}

export default function (container, props) {
	return new popmotionTHREERenderer({
		container,
		...props,
	});
}<commit_msg>enh(sizing): fix sizing of webgl area based on container offet and viewport size<commit_after>import THREE from 'three';
import Hammer from  'hammerjs';
import TouchEmulator from 'hammer-touchemulator';

import { Renderer }  from 'popmotion';
import { _ } from 'meteor/underscore';
import { tween, parallel, physics, easing }  from 'popmotion';
import { setSelectors, getSelectors } from './popmotionTHREESelectors.js';
import { store } from '/imports/store/index.js';

const LZCONST = 1.5;
const convertLevelToScale = x => Math.pow(LZCONST, x);
const convertScaleToLevel = x => Math.log(x) / Math.log(LZCONST);

class popmotionTHREERenderer extends Renderer {
	constructor(props) {
		super(props);
		this.onWindowResize = this.onWindowResize.bind(this);
		this.zoomTo = this.zoomTo.bind(this);
		this.panToStartDrag = this.panToStartDrag.bind(this);
		this.panToContinueDrag = this.panToContinueDrag.bind(this);
		this.panToEndWithInertia = this.panToEndWithInertia.bind(this);
		this.panToCenterZoom = this.panToCenterZoom.bind(this);

		// setup a clock, scene, camera and THREE renderer
		this.namespace = {};
		this.dragInertiaAction = undefined;
		this.dragStartPosition = { x: 0, y: 0 };
		this.container = this.props.container;
		this.clock = new THREE.Clock();
		this.scene = new THREE.Scene();
		this.scene.background = new THREE.Color(0x09253A);
		this.camera = new THREE.PerspectiveCamera(45, this.getWindowSize().aspect, 0.1, 1000);
		this.camera.position.z = 10;
		this.renderer = new THREE.WebGLRenderer({ preserveDrawingBuffer: true });
		this.renderer.localClippingEnabled = true;
		this.container.append(this.renderer.domElement);

		// setup touch events for this renderer
		this.touch = new Hammer(this.container, { });
		this.touch.get('pan').set({ direction: Hammer.DIRECTION_ALL });
		this.touch.get('pinch').set({ enable: true });

		// TouchEmulator();

		// setup Orbit Controls
		if (this.props.useOrbitControls) {
			this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
			this.controls.enableDamping = true;
			this.controls.dampingFactor = 0.05;
			this.controls.rotateSpeed = 0.05;
			this.controls.minAzimuthAngle = - Math.PI / 2;
			this.controls.maxAzimuthAngle = Math.PI / 2;
		}

		// setup lighting
		var ambientLight = new THREE.AmbientLight(0xffffff);
		this.scene.add(ambientLight);

		var lights = [];
		lights[ 0 ] = new THREE.PointLight(0xffffff, 1, 0);
		lights[ 1 ] = new THREE.PointLight(0xffffff, 1, 0);
		lights[ 2 ] = new THREE.PointLight(0x888888, 1, 0);

		lights[ 0 ].position.set(0, 200, 0);
		lights[ 1 ].position.set(100, 200, 100);
		lights[ 2 ].position.set(- 100, - 200, - 100);

		this.scene.add(lights[ 0 ]);
		this.scene.add(lights[ 1 ]);
		this.scene.add(lights[ 2 ]);

		// listen for resize events, and setup for initial size
		window.addEventListener('resize', this.onWindowResize, false);
		this.onWindowResize();
	}

	panToStartDrag(ev) {
		if (this.dragInertiaAction) this.dragInertiaAction.stop();
		this.dragStartPosition = this.onRead('camera.xy');
	}

	panToContinueDrag(ev) {
		this.set('camera.xy', {
			x: this.dragStartPosition.x - ev.deltaX / 100 / this.camera.zoom,
			y: this.dragStartPosition.y + ev.deltaY / 100 / this.camera.zoom,
		});
	}

	panToEndWithInertia(ev) {
		this.dragInertiaAction = physics({ velocity: 20, friction: 0.3 })
			.output(v => this.set('camera.xy', {
				x: this.dragStartPosition.x - ev.deltaX / 100 / this.camera.zoom - ev.velocityX * v / this.camera.zoom,
				y: this.dragStartPosition.y + ev.deltaY / 100 / this.camera.zoom + ev.velocityY * v / this.camera.zoom,
			}))
			.start();
	}

	panToCenterZoom(startScale, endScale) {
		if (this.dragInertiaAction) this.dragInertiaAction.stop();
		const sp = this.onRead('camera.xy');
		// parallel([
		// 	tween({ from: sp.x, to: sp.x / startScale * endScale })
		// 		.output(v => this.set('camera.x', v)),
		// 	tween({ from: sp.y, to: sp.y / startScale * endScale })
		// 		.output(v => this.set('camera.y', v)),
		// ]).start();
	}

	zoomIn() {
		const startScale = this.onRead('camera.zoom');
		const endScale = convertLevelToScale(convertScaleToLevel(startScale) + 1);
		this.panToCenterZoom(startScale, endScale);
		tween({ from: startScale, to: endScale })
			.output(v => this.set('camera.zoom', v))
			.start();
	}

	zoomOut() {
		const startScale = this.onRead('camera.zoom');
		const endScale = convertLevelToScale(convertScaleToLevel(startScale) - 1);
		this.panToCenterZoom(startScale, endScale);
		tween({ from: startScale, to: endScale })
			.output(v => this.set('camera.zoom', v))
			.start();
	}

	zoomTo(endScale) {
		const startScale = this.get('camera.zoom');
		this.panToCenterZoom(startScale, endScale);
		tween({ from: startScale, to: endScale })
			.output(v => this.set('camera.zoom', v))
			.start();
	}

	viewPlan() {
		tween({ from: this.get('camera.rotateX'), to: Math.PI/2 })
			.output(v => this.set('camera.rotateX', v))
			.start();
	}

	viewElev() {
		tween({ from: this.get('camera.rotateX'), to: 0 })
			.output(v => this.set('camera.rotateX', v))
			.start();
	}

	thresholdTo(endThreshold) {
		const startThreshold = this.get('img.u_threshold');
		store.set('threshold', { value: endThreshold });
		tween({ from: startThreshold, to: endThreshold })
			.output(v => this.set('img.u_threshold', v))
			.start();
	}

	thresholdUp() {
		const startThreshold = this.get('img.u_threshold');
		store.set('threshold', { value: startThreshold + 1 });
		tween({ from: startThreshold, to: startThreshold + 1 })
			.output(v => this.set('img.u_threshold', v))
			.start();
	}

	thresholdDown() {
		const startThreshold = this.get('img.u_threshold');
		store.set('threshold', { value: startThreshold - 1 });
		tween({ from: startThreshold, to: startThreshold - 1 })
			.output(v => this.set('img.u_threshold', v))
			.start();
	}

	getWindowSize() {
		const offset = $(this.container).offset();
		const size = {
			width: window.innerWidth - offset.left,
			height: window.innerHeight - offset.top,
		};
		size.aspect = size.width / size.height;
		return size;
	}
	onWindowResize() {
		const size = this.getWindowSize();
		if (this.namespace.camera) this.set('camera.aspect', size.aspect);
		this.renderer.setSize(size.width, size.height);
		this.renderer.setPixelRatio(window.devicePixelRatio);
	}

	removeScrollBars() {
		$('body').css('overflow','hidden');
	}

	addTHREEObjects(obj, parent) {
		// add THREE objects to a parent (or the scene by default)
	}

	setNameSpace(keys) {
		this.namespace = {
			camera: this.camera,
			scene: this.scene,
			...keys };
		return this;
	}

	onRender() {
		// Apply all the changed values to the THREE model
		_.each(this.changedValues, key => {
			const parts = key.split('.');
			const keyobj = this.namespace[parts[0]];
			const keyparam = parts[1];
			if (setSelectors[keyparam]) {
				setSelectors[keyparam](keyobj, this.state[key]);
			} else {
				console.log(`cannot set value for key:${keyparam}`);
			}
		});

		// this.camera.lookAt(new THREE.Vector3(0,0,0));
		if (this.controls && this.controls.update)
			this.controls.update();

		// Render the THREE model
		this.renderer.render( this.scene, this.camera );
	}

	onRead(key) {
		const parts = key.split('.');
		const keyobj = this.namespace[parts[0]];
		const keyparam = parts[1];
		return getSelectors[keyparam] ? getSelectors[keyparam](keyobj) : undefined;
	}
}

export default function (container, props) {
	return new popmotionTHREERenderer({
		container,
		...props,
	});
}<|endoftext|>


<commit_before>'use strict;'

/**
 * Controller class for all site views
 */
function SiteView() {

    var that = this;

    that.load = function() {

        loadCommonEvents();

        loadClientEvents();

        loadReferenceDefinitionEvents();

        loadFilterByDepartmentCookie();

    }

    function loadClientEvents() {

        // Client creation overlay
        $('a.add_client').prepOverlay({
                subtype: 'ajax',
                filter: 'head>*,#content>*:not(div.configlet),dl.portalMessage.error,dl.portalMessage.info',
                formselector: '#client-base-edit',
                closeselector: '[name="form.button.cancel"]',
                width:'70%',
                noform:'close',
                config: {
                    closeOnEsc: false,
                    onLoad: function() {
                        // manually remove remarks
                        this.getOverlay().find("#archetypes-fieldname-Remarks").remove();
                    },
                    onClose: function(){
                        // here is where we'd populate the form controls, if we cared to.
                    }
                }
        });

        // Client combogrid searches by ID
        $("input[id*='ClientID']").combogrid({
            colModel: [{'columnName':'ClientUID','hidden':true},
                       {'columnName':'ClientID','width':'20','label':_('Client ID')},
                       {'columnName':'Title','width':'80','label':_('Title')}],
            showOn: true,
            width: '450px',
            url: window.portal_url + "/getClients?_authenticator=" + $('input[name="_authenticator"]').val(),
            select: function( event, ui ) {
                $(this).val(ui.item.ClientID);
                $(this).change();
                return false;
            }
        });

        // Display add Client button next to Client ID input for all
        // views except from Client View
        if($(".portaltype-client").length == 0){
            $("input[id='ClientID']")
                .after('' +
                       ' ' +
                       '');
        }

        // Confirm before resetting client specs to default lab specs
        $("a[href*='set_to_lab_defaults']").click(function(event){
            // always prevent default/
            // url is activated manually from 'Yes' below.
            url = $(this).attr("href");
            event.preventDefault();
            yes = _('Yes');
            no = _('No');
            var $confirmation = $("

")
                .html(_("This will remove all existing client analysis specifications "+
                        "and create copies of all lab specifications. "+
                        "Are you sure you want to do this?"))
                .dialog({
                    resizable:false,
                    title: _('Set to lab defaults'),
                    buttons: {
                        yes: function(event){
                            $(this).dialog("close");
                            window.location.href = url;
                        },
                        no: function(event){
                            $(this).dialog("close");
                        }
                    }
                });
        });
    }

    function loadReferenceDefinitionEvents() {

        // a reference definition is selected from the dropdown
        // (../../skins/bika/bika_widgets/referenceresultswidget.js)
        $('#ReferenceDefinition\\:list').change(function(){
            authenticator = $('input[name="_authenticator"]').val();
            uid = $(this).val();
            option = $(this).children(":selected").html();

            if (uid == '') {
                // No reference definition selected;
                // render empty widget.
                $("#Blank").prop('checked',false);
                $("#Hazardous").prop('checked',false);
                $('.bika-listing-table')
                    .load('referenceresults', {'_authenticator': authenticator});
                return;
            }

            if(option.search(_("(Blank)")) > -1){
                $("#Blank").prop('checked',true);
            } else {
                $("#Blank").prop('checked',false);
            }

            if(option.search(_("(Hazardous)")) > -1){
                $("#Hazardous").prop('checked',true);
            } else {
                $("#Hazardous").prop('checked',false);
            }

            $('.bika-listing-table')
                .load('referenceresults',
                    {'_authenticator': authenticator,
                     'uid':uid});
        });

        // If validation failed, and user is returned to page - requires reload.
        if ($('#ReferenceDefinition\\:list').val() != ''){
            $('#ReferenceDefinition\\:list').change();
        }
    }

    function loadCommonEvents() {

        var curDate = new Date();
        var y = curDate.getFullYear();
        var limitString = "1900:" + y;
        var dateFormat = _("date_format_short_datepicker");
        if (dateFormat == 'date_format_short_datepicker'){
            dateFormat = 'yy-mm-dd';
        }

        $("input.datepicker").live("click", function() {
            $(this).datepicker({
                showOn:"focus",
                showAnim:"",
                changeMonth:true,
                changeYear:true,
                dateFormat: dateFormat,
                yearRange: limitString
            })
            .click(function(){$(this).attr("value", "");})
            .focus();
        });

        $("input.datepicker_nofuture").live("click", function() {
            $(this).datepicker({
                showOn:"focus",
                showAnim:"",
                changeMonth:true,
                changeYear:true,
                maxDate: curDate,
                dateFormat: dateFormat,
                yearRange: limitString
            })
            .click(function(){$(this).attr("value", "");})
            .focus();
        });

        $("input.datepicker_2months").live("click", function() {
            $(this).datepicker({
                showOn:"focus",
                showAnim:"",
                changeMonth:true,
                changeYear:true,
                maxDate: "+0d",
                numberOfMonths: 2,
                dateFormat: dateFormat,
                yearRange: limitString
            })
            .click(function(){$(this).attr("value", "");})
            .focus();
        });

        $("input.datetimepicker_nofuture").live("click", function() {
            $(this).datetimepicker({
                showOn:"focus",
                showAnim:"",
                changeMonth:true,
                changeYear:true,
                maxDate: curDate,
                dateFormat: dateFormat,
                yearRange: limitString,
                timeFormat: "HH:mm",
                beforeShow: function() {
                        setTimeout(function(){
                            $('.ui-datepicker').css('z-index', 99999999999999);
                        }, 0);
                    }
            })
            .click(function(){$(this).attr("value", "");})
            .focus();
        });

        // Analysis Service popup trigger
        $('.service_title span:not(.before)').live("click", function(){
            var dialog = $("

");
            dialog
                .load(window.portal_url + "/analysisservice_popup",
                    {'service_title':$(this).closest('td').find("span[class^='state']").html(),
                    "analysis_uid":$(this).parents("tr").attr("uid"),
                    "_authenticator": $("input[name='_authenticator']").val()}
                )
                .dialog({
                    width:450,
                    height:450,
                    closeText: _("Close"),
                    resizable:true,
                    title: $(this).text()
                });
        });

        $('.numeric').live('paste', function(event){
            // Wait (next cycle) for value popluation and replace commas.
            var $self = $(this);
            window.setTimeout(function() {
                $self.val($self.val().replace(',','.'));
            }, 0);
        });

        $(".numeric").live("keypress", function(event) {
            var allowedKeys = [
                8,   // backspace
                9,   // tab
                13,  // enter
                35,  // end
                36,  // home
                37,  // left arrow
                39,  // right arrow
                46,  // delete - We don't support the del key in Opera because del == . == 46.
                44,  // ,
                60,  // <
                62,  // >
                45,  // -
                69,  // E
                101, // e,
                61   // =
            ];
            var isAllowedKey = allowedKeys.join(",").match(new RegExp(event.which)); // IE doesn't support indexOf
            // Some browsers just don't raise events for control keys. Easy. e.g. Safari backspace.
            if (!event.which || // Control keys in most browsers. e.g. Firefox tab is 0
                (48 <= event.which && event.which <= 57) || // Always 0 through 9
                isAllowedKey) { // Opera assigns values for control keys.
                // Wait (next cycle) for value popluation and replace commas.
                var $self = $(this);
                window.setTimeout(function() {
                    $self.val($self.val().replace(',','.'));
                }, 0);
                return;
            } else {
                event.preventDefault();
            }
        });

        // Archetypes :int and IntegerWidget inputs get filtered
        $("input[name*='\\:int'], .ArchetypesIntegerWidget input").keyup(function(e) {
            if (/\D/g.test(this.value)) {
                this.value = this.value.replace(/\D/g, "");
            }
        });

        // Archetypes :float and DecimalWidget inputs get filtered
        $("input[name*='\\:float'], .ArchetypesDecimalWidget input").keyup(function(e) {
            if (/[^.\d]/g.test(this.value)) {
                this.value = this.value.replace(/[^.\d]/g, "");
            }
        });

        /* Replace kss-bbb spinner with a quieter one */
        var timer, spinner, counter = 0;
        $(document).unbind("ajaxStart");
        $(document).unbind("ajaxStop");
        $('#ajax-spinner').remove();
        spinner = $('

');
        spinner.appendTo('body').hide();
        $(document).ajaxStart(function () {
            counter++;
            setTimeout(function () {
                if (counter > 0) {
                    spinner.show('fast');
                }
            }, 500);
        });
        function stop_spinner(){
            counter--;
            if (counter < 0){ counter = 0; }
            if (counter == 0) {
                clearTimeout(timer);
                spinner.stop();
                spinner.hide();
            }
        }
        $(document).ajaxStop(function () {
            stop_spinner();
        });
        $( document ).ajaxError(function( event, jqxhr, settings, thrownError ) {
            stop_spinner();
            window.bika.lims.log("Error at " + settings.url + ": " + thrownError);
        });
    }

    function portalAlert(html) {
        if ($('#portal-alert').length == 0) {
            $('#portal-header').append("

" + html + "

");
        } else {
            $('#portal-alert').append("
" + html + "
");
        }
        $('#portal-alert').fadeIn();
    }

    that.notificationPanel = function(data, mode) {
    /**
     * Render an alert inside the content panel. Used for autosave in ARView, for example.
     */
    $('#panel-notification').remove();
        $('div#viewlet-above-content-title').append(
            "
" +
            "
"
            + data +
            "

");

        $('#panel-notification').fadeIn("slow","linear", function(){
            setTimeout(function() {
                $('#panel-notification').fadeOut("slow","linear")
            }, 3000)
        });
    };

    function loadFilterByDepartment() {
        /**
        This function sets up the filter by department widget, the cookie and
        the auto-submit.
        Also it does auto-submit if admin wants to enable/disable the department filtering.
        */
        $('#department_filter_portlet').change(function(e) {
            $('#department_filter_submit').click();
        });

        $('#admin_dep_filter_enabled').change(function() {
            var cookiename = 'filter_by_department_info';
            if($(this).is(":checked")) {
                var deps=[];
                var all_dep_ids = document.getElementById('department_filter_portlet').options;
                for (var i = 0; i < all_dep_ids.length; i++) {
                  deps.push(all_dep_ids[i].value);
                }
                createCookie(cookiename, deps);
                createCookie('dep_filter_disabled','true');
                $('#department_filter_portlet').prop("disabled",true);
                location.reload();
              }else{
                createCookie('dep_filter_disabled','false');
                $('#department_filter_portlet').prop("disabled",false);
                $('#department_filter_submit').click();
              }
            });
          loadFilterByDepartmentCookie();
    }

    function loadFilterByDepartmentCookie(){
        /**
        This function checks if the cookie 'filter_by_department_info' is
        available. If the cookie exists, do nothing, if the cookie has not been
        created yet, checks the selected department in the 'filter by department
        cookie' and creates the cookie with the UID of the department.
        If cookie value is "disabled", it means the user is admin and filtering is disabled.
        */
        // Gettin the cookie
        var cookiename = 'filter_by_department_info';
        var cookie_val = readCookie(cookiename);
        console.log(cookie_val);
        if (cookie_val === null){
            // we need to create a cookie with the default value in selected
            // the portlet
            var dep_uid = $('.portlet#portletfilter_by_department form ' +
                'select#department_filter_portlet').find(":selected").val();
            createCookie(cookiename, dep_uid);
        }
        if (readCookie('dep_filter_disabled')==="true"){
            $('#admin_dep_filter_enabled').prop("checked",true);
            $('#department_filter_portlet').prop("disabled",true);
        }
    }
}
<commit_msg>NMRL-46- Admin should be able to filter by any department and also see lists without filtering<commit_after>'use strict;'

/**
 * Controller class for all site views
 */
function SiteView() {

    var that = this;

    that.load = function() {

        loadCommonEvents();

        loadClientEvents();

        loadReferenceDefinitionEvents();

        loadFilterByDepartmentCookie();

    }

    function loadClientEvents() {

        // Client creation overlay
        $('a.add_client').prepOverlay({
                subtype: 'ajax',
                filter: 'head>*,#content>*:not(div.configlet),dl.portalMessage.error,dl.portalMessage.info',
                formselector: '#client-base-edit',
                closeselector: '[name="form.button.cancel"]',
                width:'70%',
                noform:'close',
                config: {
                    closeOnEsc: false,
                    onLoad: function() {
                        // manually remove remarks
                        this.getOverlay().find("#archetypes-fieldname-Remarks").remove();
                    },
                    onClose: function(){
                        // here is where we'd populate the form controls, if we cared to.
                    }
                }
        });

        // Client combogrid searches by ID
        $("input[id*='ClientID']").combogrid({
            colModel: [{'columnName':'ClientUID','hidden':true},
                       {'columnName':'ClientID','width':'20','label':_('Client ID')},
                       {'columnName':'Title','width':'80','label':_('Title')}],
            showOn: true,
            width: '450px',
            url: window.portal_url + "/getClients?_authenticator=" + $('input[name="_authenticator"]').val(),
            select: function( event, ui ) {
                $(this).val(ui.item.ClientID);
                $(this).change();
                return false;
            }
        });

        // Display add Client button next to Client ID input for all
        // views except from Client View
        if($(".portaltype-client").length == 0){
            $("input[id='ClientID']")
                .after('' +
                       ' ' +
                       '');
        }

        // Confirm before resetting client specs to default lab specs
        $("a[href*='set_to_lab_defaults']").click(function(event){
            // always prevent default/
            // url is activated manually from 'Yes' below.
            url = $(this).attr("href");
            event.preventDefault();
            yes = _('Yes');
            no = _('No');
            var $confirmation = $("

")
                .html(_("This will remove all existing client analysis specifications "+
                        "and create copies of all lab specifications. "+
                        "Are you sure you want to do this?"))
                .dialog({
                    resizable:false,
                    title: _('Set to lab defaults'),
                    buttons: {
                        yes: function(event){
                            $(this).dialog("close");
                            window.location.href = url;
                        },
                        no: function(event){
                            $(this).dialog("close");
                        }
                    }
                });
        });
    }

    function loadReferenceDefinitionEvents() {

        // a reference definition is selected from the dropdown
        // (../../skins/bika/bika_widgets/referenceresultswidget.js)
        $('#ReferenceDefinition\\:list').change(function(){
            authenticator = $('input[name="_authenticator"]').val();
            uid = $(this).val();
            option = $(this).children(":selected").html();

            if (uid == '') {
                // No reference definition selected;
                // render empty widget.
                $("#Blank").prop('checked',false);
                $("#Hazardous").prop('checked',false);
                $('.bika-listing-table')
                    .load('referenceresults', {'_authenticator': authenticator});
                return;
            }

            if(option.search(_("(Blank)")) > -1){
                $("#Blank").prop('checked',true);
            } else {
                $("#Blank").prop('checked',false);
            }

            if(option.search(_("(Hazardous)")) > -1){
                $("#Hazardous").prop('checked',true);
            } else {
                $("#Hazardous").prop('checked',false);
            }

            $('.bika-listing-table')
                .load('referenceresults',
                    {'_authenticator': authenticator,
                     'uid':uid});
        });

        // If validation failed, and user is returned to page - requires reload.
        if ($('#ReferenceDefinition\\:list').val() != ''){
            $('#ReferenceDefinition\\:list').change();
        }
    }

    function loadCommonEvents() {

        var curDate = new Date();
        var y = curDate.getFullYear();
        var limitString = "1900:" + y;
        var dateFormat = _("date_format_short_datepicker");
        if (dateFormat == 'date_format_short_datepicker'){
            dateFormat = 'yy-mm-dd';
        }

        $("input.datepicker").live("click", function() {
            $(this).datepicker({
                showOn:"focus",
                showAnim:"",
                changeMonth:true,
                changeYear:true,
                dateFormat: dateFormat,
                yearRange: limitString
            })
            .click(function(){$(this).attr("value", "");})
            .focus();
        });

        $("input.datepicker_nofuture").live("click", function() {
            $(this).datepicker({
                showOn:"focus",
                showAnim:"",
                changeMonth:true,
                changeYear:true,
                maxDate: curDate,
                dateFormat: dateFormat,
                yearRange: limitString
            })
            .click(function(){$(this).attr("value", "");})
            .focus();
        });

        $("input.datepicker_2months").live("click", function() {
            $(this).datepicker({
                showOn:"focus",
                showAnim:"",
                changeMonth:true,
                changeYear:true,
                maxDate: "+0d",
                numberOfMonths: 2,
                dateFormat: dateFormat,
                yearRange: limitString
            })
            .click(function(){$(this).attr("value", "");})
            .focus();
        });

        $("input.datetimepicker_nofuture").live("click", function() {
            $(this).datetimepicker({
                showOn:"focus",
                showAnim:"",
                changeMonth:true,
                changeYear:true,
                maxDate: curDate,
                dateFormat: dateFormat,
                yearRange: limitString,
                timeFormat: "HH:mm",
                beforeShow: function() {
                        setTimeout(function(){
                            $('.ui-datepicker').css('z-index', 99999999999999);
                        }, 0);
                    }
            })
            .click(function(){$(this).attr("value", "");})
            .focus();
        });

        // Analysis Service popup trigger
        $('.service_title span:not(.before)').live("click", function(){
            var dialog = $("

");
            dialog
                .load(window.portal_url + "/analysisservice_popup",
                    {'service_title':$(this).closest('td').find("span[class^='state']").html(),
                    "analysis_uid":$(this).parents("tr").attr("uid"),
                    "_authenticator": $("input[name='_authenticator']").val()}
                )
                .dialog({
                    width:450,
                    height:450,
                    closeText: _("Close"),
                    resizable:true,
                    title: $(this).text()
                });
        });

        $('.numeric').live('paste', function(event){
            // Wait (next cycle) for value popluation and replace commas.
            var $self = $(this);
            window.setTimeout(function() {
                $self.val($self.val().replace(',','.'));
            }, 0);
        });

        $(".numeric").live("keypress", function(event) {
            var allowedKeys = [
                8,   // backspace
                9,   // tab
                13,  // enter
                35,  // end
                36,  // home
                37,  // left arrow
                39,  // right arrow
                46,  // delete - We don't support the del key in Opera because del == . == 46.
                44,  // ,
                60,  // <
                62,  // >
                45,  // -
                69,  // E
                101, // e,
                61   // =
            ];
            var isAllowedKey = allowedKeys.join(",").match(new RegExp(event.which)); // IE doesn't support indexOf
            // Some browsers just don't raise events for control keys. Easy. e.g. Safari backspace.
            if (!event.which || // Control keys in most browsers. e.g. Firefox tab is 0
                (48 <= event.which && event.which <= 57) || // Always 0 through 9
                isAllowedKey) { // Opera assigns values for control keys.
                // Wait (next cycle) for value popluation and replace commas.
                var $self = $(this);
                window.setTimeout(function() {
                    $self.val($self.val().replace(',','.'));
                }, 0);
                return;
            } else {
                event.preventDefault();
            }
        });

        // Archetypes :int and IntegerWidget inputs get filtered
        $("input[name*='\\:int'], .ArchetypesIntegerWidget input").keyup(function(e) {
            if (/\D/g.test(this.value)) {
                this.value = this.value.replace(/\D/g, "");
            }
        });

        // Archetypes :float and DecimalWidget inputs get filtered
        $("input[name*='\\:float'], .ArchetypesDecimalWidget input").keyup(function(e) {
            if (/[^.\d]/g.test(this.value)) {
                this.value = this.value.replace(/[^.\d]/g, "");
            }
        });

        /* Replace kss-bbb spinner with a quieter one */
        var timer, spinner, counter = 0;
        $(document).unbind("ajaxStart");
        $(document).unbind("ajaxStop");
        $('#ajax-spinner').remove();
        spinner = $('

');
        spinner.appendTo('body').hide();
        $(document).ajaxStart(function () {
            counter++;
            setTimeout(function () {
                if (counter > 0) {
                    spinner.show('fast');
                }
            }, 500);
        });
        function stop_spinner(){
            counter--;
            if (counter < 0){ counter = 0; }
            if (counter == 0) {
                clearTimeout(timer);
                spinner.stop();
                spinner.hide();
            }
        }
        $(document).ajaxStop(function () {
            stop_spinner();
        });
        $( document ).ajaxError(function( event, jqxhr, settings, thrownError ) {
            stop_spinner();
            window.bika.lims.log("Error at " + settings.url + ": " + thrownError);
        });
    }

    function portalAlert(html) {
        if ($('#portal-alert').length == 0) {
            $('#portal-header').append("

" + html + "

");
        } else {
            $('#portal-alert').append("
" + html + "
");
        }
        $('#portal-alert').fadeIn();
    }

    that.notificationPanel = function(data, mode) {
    /**
     * Render an alert inside the content panel. Used for autosave in ARView, for example.
     */
    $('#panel-notification').remove();
        $('div#viewlet-above-content-title').append(
            "
" +
            "
"
            + data +
            "

");

        $('#panel-notification').fadeIn("slow","linear", function(){
            setTimeout(function() {
                $('#panel-notification').fadeOut("slow","linear")
            }, 3000)
        });
    };

    function loadFilterByDepartment() {
        /**
        This function sets up the filter by department widget, the cookie and
        the auto-submit.
        Also it does auto-submit if admin wants to enable/disable the department filtering.
        */
        $('#department_filter_portlet').change(function(e) {
            $('#department_filter_submit').click();
        });

        $('#admin_dep_filter_enabled').change(function() {
            var cookiename = 'filter_by_department_info';
            if($(this).is(":checked")) {
                createCookie(cookiename, "disabled");
                $('#department_filter_portlet').prop("disabled",true);
                location.reload();
              }else{
                $('#department_filter_portlet').prop("disabled",false);
                $('#department_filter_submit').click();
              }
            });
          loadFilterByDepartmentCookie();
    }

    function loadFilterByDepartmentCookie(){
        /**
        This function checks if the cookie 'filter_by_department_info' is
        available. If the cookie exists, do nothing, if the cookie has not been
        created yet, checks the selected department in the 'filter by department
        cookie' and creates the cookie with the UID of the department.
        If cookie value is "disabled", it means the user is admin and filtering is disabled.
        */
        // Gettin the cookie
        var cookiename = 'filter_by_department_info';
        var cookie_val = readCookie(cookiename);
        if (cookie_val === null){
            // we need to create a cookie with the default value in selected
            // the portlet
            var dep_uid = $('.portlet#portletfilter_by_department form ' +
                'select#department_filter_portlet').find(":selected").val();
            createCookie(cookiename, dep_uid);
        }
        if (cookie_val === "disabled"){
            $('#admin_dep_filter_enabled').prop("checked",true);
            $('#department_filter_portlet').prop("disabled",true);
        }
    }
}
<|endoftext|>


<commit_before>/**
 * OpenEyes
 *
 * (C) OpenEyes Foundation, 2017
 * This file is part of OpenEyes.
 * OpenEyes is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * OpenEyes 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. See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License along with OpenEyes in a file titled COPYING. If not, see <http://www.gnu.org/licenses/>.
 *
 * @package OpenEyes
 * @link http://www.openeyes.org.uk
 * @author OpenEyes <info@openeyes.org.uk>
 * @copyright Copyright (c) 2017, OpenEyes Foundation
 * @license http://www.gnu.org/licenses/gpl-3.0.html The GNU General Public License V3.0
 */

/**
 * Package or core JS functionality that is namespaced correctly for consistency with direction of travel
 * for JS functionality in OpenEyes. Introduced to supercede module.js as functionality from that is split
 * into appropriate controller behaviour for the elements. Code in here should be written so that it is always
 * included for any element use. The primary goal is to provide central singleton functions to manage interlinked
 * behaviour.
 */

var OpenEyes = OpenEyes || {};

OpenEyes.OphCiExamination = OpenEyes.OphCiExamination || {};

(function(exports) {
    function DiagnosisCore()
    {
        this.diagnosesBySource = {};
        this.sourceIndex = 0;
    }

    DiagnosisCore.prototype.registerForSync = function(element)
    {
        // ideally elements that are to be updated with diagnoses should register for notifications
        // here. Not to be implemented immediately though.
    };

    /**
     * Look for elements that should be notified of diagnoses, and provide the latest set of diagnoses to them.
     */
    DiagnosisCore.prototype.sync = function()
    {
        // look for diagnosis element. if it exists, send it the current state of the data
        var diagnosesElement = $('#OphCiExamination_diagnoses');
        if (diagnosesElement.length) {
            var controller = diagnosesElement.data('controller');
            controller.setExternalDiagnoses(this.diagnosesBySource);
        }
    };

    /**
     * Track element sources with a unique id
     *
     * @param element
     * @returns {*|jQuery}
     */
    DiagnosisCore.prototype.getSourceId = function(element)
    {
        var id = $(element).data('diagnosis-core-id');
        if (id === undefined) {
            id = this.sourceIndex++;
            $(element).data('diagnosis-core-id', id);
        }
        return id;
    };

    /**
     * Singleton function for any element to call when wanting to update diagnoses that have been
     * found from user interaction.
     *
     * @param diagnoses
     * @param source
     */
    DiagnosisCore.prototype.setForSource = function(diagnoses, sourceElement)
    {
        source = this.getSourceId(sourceElement);
        if (!(source in this.diagnosesBySource)) {
            this.diagnosesBySource[source] = Array();
        }
        var removedDiagnoses = Array();
        for (var i = 0; i < this.diagnosesBySource[source].length; i++) {
            var diagnosis = this.diagnosesBySource[source][i];
            if (!diagnosis in diagnoses) {
                removedDiagnoses.push(diagnosis)
            }
        }
        this.diagnosesBySource[source] = diagnoses;
        if (removedDiagnoses.length > 0)
            this.removeDiagnosesFromSource(removedDiagnoses, source);

        // run a sync
        this.sync();
    };

    /**
     * This function may be entirely unnecessary depending on how much work we can do with Diagnosis element control
     *
     * @param diagnoses
     * @param removeSource
     */
    DiagnosisCore.prototype.removeDiagnosesFromSource = function(diagnoses, removeSource)
    {
        var dontRemove = Array();
        for (source in this.diagnosesBySource) {
            if (source === removeSource)
                continue;

            if (this.diagnosesBySource.hasOwnProperty(source)) {
                for (var i = 0; i < this.diagnosesBySource[source]; i++) {
                    var otherSourceDiagnosis = this.diagnosesBySource[source][i];
                    if (otherSourceDiagnosis in diagnoses) {
                        dontRemove.push(otherSourceDiagnosis);
                    }
                }
            }
        }
        // sync the arrays

        // do the remove

        // run a sync
    };

    exports.Diagnosis = new DiagnosisCore();

})(OpenEyes.OphCiExamination);<commit_msg>[OE-6450] tidy up of core diagnoses register<commit_after>/**
 * OpenEyes
 *
 * (C) OpenEyes Foundation, 2017
 * This file is part of OpenEyes.
 * OpenEyes is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * OpenEyes 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. See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License along with OpenEyes in a file titled COPYING. If not, see <http://www.gnu.org/licenses/>.
 *
 * @package OpenEyes
 * @link http://www.openeyes.org.uk
 * @author OpenEyes <info@openeyes.org.uk>
 * @copyright Copyright (c) 2017, OpenEyes Foundation
 * @license http://www.gnu.org/licenses/gpl-3.0.html The GNU General Public License V3.0
 */

/**
 * Package or core JS functionality that is namespaced correctly for consistency with direction of travel
 * for JS functionality in OpenEyes. Introduced to supercede module.js as functionality from that is split
 * into appropriate controller behaviour for the elements. Code in here should be written so that it is always
 * included for any element use. The primary goal is to provide central singleton functions to manage interlinked
 * behaviour.
 */

var OpenEyes = OpenEyes || {};

OpenEyes.OphCiExamination = OpenEyes.OphCiExamination || {};

(function(exports) {
    function DiagnosisCore()
    {
        this.diagnosesBySource = {};
        this.sourceIndex = 0;
    }

    /**
     * @todo: implement this function for greater flexibility when more elements need to track external diagnoses
     * @param element
     */
    DiagnosisCore.prototype.registerForSync = function(element)
    {
        // ideally elements that are to be updated with diagnoses should register for notifications
        // here. Not to be implemented immediately though.
    };

    /**
     * Look for elements that should be notified of diagnoses, and provide the latest set of diagnoses to them.
     */
    DiagnosisCore.prototype.sync = function()
    {
        // look for diagnosis element. if it exists, send it the current state of the data
        var diagnosesElement = $('#OphCiExamination_diagnoses');
        if (diagnosesElement.length) {
            var controller = diagnosesElement.data('controller');
            controller.setExternalDiagnoses(this.diagnosesBySource);
        }
    };

    /**
     * Track element sources with a unique id
     *
     * @param element
     * @returns {*|jQuery}
     */
    DiagnosisCore.prototype.getSourceId = function(element)
    {
        var id = $(element).data('diagnosis-core-id');
        if (id === undefined) {
            id = this.sourceIndex++;
            $(element).data('diagnosis-core-id', id);
        }
        return id;
    };

    /**
     * Singleton function for any element to call when wanting to update diagnoses that have been
     * found from user interaction.
     *
     * @param diagnoses
     * @param sourceElement
     */
    DiagnosisCore.prototype.setForSource = function(diagnoses, sourceElement)
    {
        var source = this.getSourceId(sourceElement);
        this.diagnosesBySource[source] = diagnoses;

        // run a sync
        this.sync();
    };

    exports.Diagnosis = new DiagnosisCore();

})(OpenEyes.OphCiExamination);<|endoftext|>


<commit_before>//
// パズル固有スクリプト部 ボックス版 box.js
//
(function(pidlist, classbase){
	if(typeof module==='object' && module.exports){module.exports = [pidlist, classbase];}
	else{ pzpr.classmgr.makeCustom(pidlist, classbase);}
}(
['box'], {
//---------------------------------------------------------
// マウス入力系
MouseEvent:{
	use : true,

	mouseinput : function(){
		if(this.puzzle.playmode){
			if(this.mousestart || this.mousemove){ this.inputcell();}
		}
		else if(this.puzzle.editmode){
			if(this.mousestart){ this.input_onstart();}
		}
	},

	input_onstart : function(){
		var excell = this.getcell_excell();
		if(excell.isnull || excell.group!=='excell'){ return;}

		if(excell!==this.cursor.getex()){
			this.setcursor(excell);
		}
		else{
			this.inputnumber(excell);
		}
	},
	inputnumber : function(excell){
		var qn = excell.qnum, max=excell.getmaxnum();
		if(this.btn==='left'){ excell.setQnum(qn!==max ? qn+1 : 0);}
		else if(this.btn==='right'){ excell.setQnum(qn!==0 ? qn-1 : max);}

		excell.draw();
	}
},

//---------------------------------------------------------
// キーボード入力系
KeyEvent:{
	enablemake : true,
	moveTarget : function(ca){
		var cursor = this.cursor;
		var excell0 = cursor.getex(), dir = excell0.NDIR;
		switch(ca){
			case 'up':    if(cursor.bx===cursor.minx && cursor.miny<cursor.by){ dir=excell0.UP;} break;
			case 'down':  if(cursor.bx===cursor.minx && cursor.maxy>cursor.by){ dir=excell0.DN;} break;
			case 'left':  if(cursor.by===cursor.miny && cursor.minx<cursor.bx){ dir=excell0.LT;} break;
			case 'right': if(cursor.by===cursor.miny && cursor.maxx>cursor.bx){ dir=excell0.RT;} break;
		}

		if(dir!==excell0.NDIR){
			cursor.movedir(dir,2);

			excell0.draw();
			cursor.draw();

			return true;
		}
		return false;
	},

	keyinput : function(ca){
		this.key_inputexcell(ca);
	},
	key_inputexcell : function(ca){
		var excell = this.cursor.getex(), qn = excell.qnum;
		var max = excell.getmaxnum();

		if('0'<=ca && ca<='9'){
			var num = +ca;

			if(qn<=0 || this.prev!==excell){
				if(num<=max){ excell.setQnum(num);}
			}
			else{
				if(qn*10+num<=max){ excell.setQnum(qn*10+num);}
				else if (num<=max){ excell.setQnum(num);}
			}
		}
		else if(ca===' ' || ca==='-'){ excell.setQnum(0);}
		else{ return;}

		this.prev = excell;
		this.cursor.draw();
	}
},

TargetCursor:{
	initCursor : function(){
		this.init(-1,-1);
	}
},

//---------------------------------------------------------
// 盤面管理系
EXCell:{
	qnum : 0,

	disInputHatena : true,

	maxnum : function(){
		var bx=this.bx, by=this.by;
		if(bx===-1 && by===-1){ return 0;}
		var sum=0;
		for(var n=(bx===-1?this.board.rows:this.board.cols);n>0;n--){ sum+=n;}
		return sum;
	},
	minnum : 0
},

Board:{
	cols : 9,
	rows : 9,

	hasexcell : 1
},
BoardExec:{
	adjustBoardData : function(key,d){
		var bx1=(d.x1|1), by1=(d.y1|1);
		this.qnumw = [];
		this.qnumh = [];

		var bd=this.board;
		for(var by=by1;by<=d.y2;by+=2){ this.qnumw[by] = bd.getex(-1,by).qnum;}
		for(var bx=bx1;bx<=d.x2;bx+=2){ this.qnumh[bx] = bd.getex(bx,-1).qnum;}
	},
	adjustBoardData2 : function(key,d){
		var xx=(d.x1+d.x2), yy=(d.y1+d.y2), bx1=(d.x1|1), by1=(d.y1|1);

		var bd=this.board;
		switch(key){
		case this.FLIPY: // 上下反転
			for(var bx=bx1;bx<=d.x2;bx+=2){ bd.getex(bx,-1).setQnum(this.qnumh[bx]);}
			break;

		case this.FLIPX: // 左右反転
			for(var by=by1;by<=d.y2;by+=2){ bd.getex(-1,by).setQnum(this.qnumw[by]);}
			break;

		case this.TURNR: // 右90°反転
			for(var by=by1;by<=d.y2;by+=2){ bd.getex(-1,by).setQnum(this.qnumh[by]);}
			for(var bx=bx1;bx<=d.x2;bx+=2){ bd.getex(bx,-1).setQnum(this.qnumw[xx-bx]);}
			break;

		case this.TURNL: // 左90°反転
			for(var by=by1;by<=d.y2;by+=2){ bd.getex(-1,by).setQnum(this.qnumh[yy-by]);}
			for(var bx=bx1;bx<=d.x2;bx+=2){ bd.getex(bx,-1).setQnum(this.qnumw[bx]);}
			break;
		}
	}
},

//---------------------------------------------------------
// 画像表示系
Graphic:{
	paint : function(){
		this.drawBGCells();
		this.drawDotCells(false);
		this.drawShadedCells();
		this.drawGrid();

		this.drawBGEXcells();
		this.drawNumbers_box();

		this.drawCircledNumbers_box();

		this.drawChassis();

		this.drawTarget();
	},

	getCanvasCols : function(){
		return this.getBoardCols()+2*this.margin+1;
	},
	getCanvasRows : function(){
		return this.getBoardRows()+2*this.margin+1;
	},
	getOffsetCols : function(){ return 0.5;},
	getOffsetRows : function(){ return 0.5;},

	drawNumbers_box : function(){
		var g = this.vinc('excell_number', 'auto');

		var exlist = this.range.excells;
		for(var i=0;i<exlist.length;i++){
			var excell = exlist[i];
			if(excell.id>=this.board.cols+this.board.rows){ continue;}

			g.vi = "excell_text_"+excell.id;
			if(excell.bx>=0 || excell.by>=0){
				g.fillStyle = (excell.error!==1 ? this.fontcolor : this.fontErrcolor);
				this.disptext(""+excell.qnum, excell.bx*this.bw, excell.by*this.bh);
			}
			else{ g.vhide();}
		}
	},

	drawCircledNumbers_box : function(){
		var list = [], bd = this.board;
		var x1=this.range.x1, y1=this.range.y1, x2=this.range.x2, y2=this.range.y2;
		if(x2>=bd.maxbx){ for(var by=(y1|1),max=Math.min(bd.maxby,y2);by<=max;by+=2){ list.push([bd.maxbx+1,by]);}}
		if(y2>=bd.maxby){ for(var bx=(x1|1),max=Math.min(bd.maxbx,x2);bx<=max;bx+=2){ list.push([bx,bd.maxby+1]);}}

		var g = this.vinc('excell_circle', 'auto', true);
		var rsize = this.cw*0.36;
		g.fillStyle   = this.circledcolor;
		g.strokeStyle = this.quescolor;
		for(var i=0;i<list.length;i++){
			var num = ((list[i][0]!==bd.maxbx+1 ? list[i][0] : list[i][1])+1)>>1;
			g.vid = ["ex2_cir_",list[i][0],list[i][1]].join("_");
			if(num>0){
				g.shapeCircle(list[i][0]*this.bw, list[i][1]*this.bh, rsize);
			}
			else{ g.vhide();}
		}

		var option = {globalratio:0.95};
		g = this.vinc('excell_number2', 'auto');
		g.fillStyle = this.fontcolor;
		for(var i=0;i<list.length;i++){
			var num = ((list[i][0]!==bd.maxbx+1 ? list[i][0] : list[i][1])+1)>>1;
			g.vid = ["ex2_cirtext_",list[i][0],list[i][1]].join("_");
			if(num>0){
				this.disptext(""+num, list[i][0]*this.bw, list[i][1]*this.bh, option);
			}
			else{ g.vhide();}
		}
	}
},

//---------------------------------------------------------
// URLエンコード/デコード処理
Encode:{
	decodePzpr : function(type){
		this.decodeBox();
	},
	encodePzpr : function(type){
		this.encodeBox();
	},

	decodeBox : function(){
		var ec=0, bstr = this.outbstr, bd = this.board;
		for(var a=0;a<bstr.length;a++){
			var ca=bstr.charAt(a), excell=bd.excell[ec];
			if(ca==='-'){ excell.qnum = parseInt(bstr.substr(a+1,2),32); a+=2;}
			else        { excell.qnum = parseInt(ca,32);}
			ec++;
			if(ec >= bd.cols+bd.rows){ a++; break;}
		}

		this.outbstr = bstr.substr(a);
	},
	encodeBox : function(){
		var cm="", bd = this.board;
		for(var ec=0,len=bd.cols+bd.rows;ec<len;ec++){
			var qnum=bd.excell[ec].qnum;
			if(qnum<32){ cm+=("" +qnum.toString(32));}
			else       { cm+=("-"+qnum.toString(32));}
		}

		this.outbstr += cm;
	}
},
//---------------------------------------------------------
FileIO:{
	decodeData : function(){
		var bd = this.board, item = this.getItemList(bd.rows+1);
		for(var i=0;i<item.length;i++) {
			var ca = item[i];
			if(ca==="."){ continue;}

			var bx = i%(bd.cols+1)*2-1, by = ((i/(bd.cols+1))<<1)-1;
			var excell = bd.getex(bx,by);
			if(!excell.isnull){
				excell.qnum = +ca;
			}

			var cell = bd.getc(bx,by);
			if(!cell.isnull){
				if     (ca==="#"){ cell.qans = 1;}
				else if(ca==="+"){ cell.qsub = 1;}
			}
		}
	},
	encodeData : function(){
		var bd = this.board;
		for(var by=-1;by<bd.maxby;by+=2){
			for(var bx=-1;bx<bd.maxbx;bx+=2){
				var excell = bd.getex(bx,by);
				if(!excell.isnull){
					if(excell.id<bd.cols+bd.rows){
						this.datastr += (excell.qnum+" ");
					}
					else{
						this.datastr += ". ";
					}
					continue;
				}

				var cell = bd.getc(bx,by);
				if(!cell.isnull){
					if     (cell.qans===1){ this.datastr += "# ";}
					else if(cell.qsub===1){ this.datastr += "+ ";}
					else                  { this.datastr += ". ";}
					continue;
				}

				this.datastr += ". ";
			}
			this.datastr += "\n";
		}
	}
},

//---------------------------------------------------------
// 正解判定処理実行部
AnsCheck:{
	checklist : [
		"checkShadeCellExist",
		"checkShadeCells"
	],

	checkShadeCells : function(type){
		var bd = this.board;
		for(var ec=0;ec<bd.excell.length;ec++){
			var excell = bd.excell[ec];
			var qn=excell.qnum, pos=excell.getaddr(), val=0, cell;
			var clist=new this.klass.CellList();
			if(pos.by===-1 && pos.bx>0 && pos.bx<2*bd.cols){
				cell = pos.move(0,2).getc();
				while(!cell.isnull){
					if(cell.qans===1){ val+=((pos.by+1)>>1);}
					clist.add(cell);
					cell = pos.move(0,2).getc();
				}
			}
			else if(pos.bx===-1 && pos.by>0 && pos.by<2*bd.rows){
				cell = pos.move(2,0).getc();
				while(!cell.isnull){
					if(cell.qans===1){ val+=((pos.bx+1)>>1);}
					clist.add(cell);
					cell = pos.move(2,0).getc();
				}
			}
			else{ continue;}
			if(qn===val){ continue;}

			this.failcode.add("nmSumRowShadeNe");
			if(this.checkOnly){ break;}
			excell.seterr(1);
			clist.seterr(1);
		}
	}
},

FailCode:{
	nmSumRowShadeNe : ["数字と黒マスになった数字の合計が正しくありません。","A number is not equal to the sum of the number of shaded cells."]
}
}));
<commit_msg>box: 数字が上書きされてしまう不具合を修正<commit_after>//
// パズル固有スクリプト部 ボックス版 box.js
//
(function(pidlist, classbase){
	if(typeof module==='object' && module.exports){module.exports = [pidlist, classbase];}
	else{ pzpr.classmgr.makeCustom(pidlist, classbase);}
}(
['box'], {
//---------------------------------------------------------
// マウス入力系
MouseEvent:{
	use : true,

	mouseinput : function(){
		if(this.puzzle.playmode){
			if(this.mousestart || this.mousemove){ this.inputcell();}
		}
		else if(this.puzzle.editmode){
			if(this.mousestart){ this.input_onstart();}
		}
	},

	input_onstart : function(){
		var excell = this.getcell_excell();
		if(excell.isnull || excell.group!=='excell'){ return;}

		if(excell!==this.cursor.getex()){
			this.setcursor(excell);
		}
		else{
			this.inputnumber(excell);
		}
	},
	inputnumber : function(excell){
		var qn = excell.qnum, max=excell.getmaxnum();
		if(this.btn==='left'){ excell.setQnum(qn!==max ? qn+1 : 0);}
		else if(this.btn==='right'){ excell.setQnum(qn!==0 ? qn-1 : max);}

		excell.draw();
	}
},

//---------------------------------------------------------
// キーボード入力系
KeyEvent:{
	enablemake : true,
	moveTarget : function(ca){
		var cursor = this.cursor;
		var excell0 = cursor.getex(), dir = excell0.NDIR;
		switch(ca){
			case 'up':    if(cursor.bx===cursor.minx && cursor.miny<cursor.by){ dir=excell0.UP;} break;
			case 'down':  if(cursor.bx===cursor.minx && cursor.maxy>cursor.by){ dir=excell0.DN;} break;
			case 'left':  if(cursor.by===cursor.miny && cursor.minx<cursor.bx){ dir=excell0.LT;} break;
			case 'right': if(cursor.by===cursor.miny && cursor.maxx>cursor.bx){ dir=excell0.RT;} break;
		}

		if(dir!==excell0.NDIR){
			cursor.movedir(dir,2);

			excell0.draw();
			cursor.draw();

			return true;
		}
		return false;
	},

	keyinput : function(ca){
		this.key_inputexcell(ca);
	},
	key_inputexcell : function(ca){
		var excell = this.cursor.getex(), qn = excell.qnum;
		var max = excell.getmaxnum();

		if('0'<=ca && ca<='9'){
			var num = +ca;

			if(qn<=0 || this.prev!==excell){
				if(num<=max){ excell.setQnum(num);}
			}
			else{
				if(qn*10+num<=max){ excell.setQnum(qn*10+num);}
				else if (num<=max){ excell.setQnum(num);}
			}
		}
		else if(ca===' ' || ca==='-'){ excell.setQnum(0);}
		else{ return;}

		this.prev = excell;
		this.cursor.draw();
	}
},

TargetCursor:{
	initCursor : function(){
		this.init(-1,-1);
	}
},

//---------------------------------------------------------
// 盤面管理系
EXCell:{
	qnum : 0,

	disInputHatena : true,

	maxnum : function(){
		var bx=this.bx, by=this.by;
		if(bx===-1 && by===-1){ return 0;}
		var sum=0;
		for(var n=(bx===-1?this.board.rows:this.board.cols);n>0;n--){ sum+=n;}
		return sum;
	},
	minnum : 0
},

Board:{
	cols : 9,
	rows : 9,

	hasexcell : 1
},
BoardExec:{
	adjustBoardData : function(key,d){
		var bx1=(d.x1|1), by1=(d.y1|1);
		this.qnumw = [];
		this.qnumh = [];

		var bd=this.board;
		for(var by=by1;by<=d.y2;by+=2){ this.qnumw[by] = bd.getex(-1,by).qnum;}
		for(var bx=bx1;bx<=d.x2;bx+=2){ this.qnumh[bx] = bd.getex(bx,-1).qnum;}
	},
	adjustBoardData2 : function(key,d){
		var xx=(d.x1+d.x2), yy=(d.y1+d.y2), bx1=(d.x1|1), by1=(d.y1|1);

		var bd=this.board;
		switch(key){
		case this.FLIPY: // 上下反転
			for(var bx=bx1;bx<=d.x2;bx+=2){ bd.getex(bx,-1).setQnum(this.qnumh[bx]);}
			break;

		case this.FLIPX: // 左右反転
			for(var by=by1;by<=d.y2;by+=2){ bd.getex(-1,by).setQnum(this.qnumw[by]);}
			break;

		case this.TURNR: // 右90°反転
			for(var by=by1;by<=d.y2;by+=2){ bd.getex(-1,by).setQnum(this.qnumh[by]);}
			for(var bx=bx1;bx<=d.x2;bx+=2){ bd.getex(bx,-1).setQnum(this.qnumw[xx-bx]);}
			break;

		case this.TURNL: // 左90°反転
			for(var by=by1;by<=d.y2;by+=2){ bd.getex(-1,by).setQnum(this.qnumh[yy-by]);}
			for(var bx=bx1;bx<=d.x2;bx+=2){ bd.getex(bx,-1).setQnum(this.qnumw[bx]);}
			break;
		}
	}
},

//---------------------------------------------------------
// 画像表示系
Graphic:{
	paint : function(){
		this.drawBGCells();
		this.drawDotCells(false);
		this.drawShadedCells();
		this.drawGrid();

		this.drawBGEXcells();
		this.drawNumbers_box();

		this.drawCircledNumbers_box();

		this.drawChassis();

		this.drawTarget();
	},

	getCanvasCols : function(){
		return this.getBoardCols()+2*this.margin+1;
	},
	getCanvasRows : function(){
		return this.getBoardRows()+2*this.margin+1;
	},
	getOffsetCols : function(){ return 0.5;},
	getOffsetRows : function(){ return 0.5;},

	drawNumbers_box : function(){
		var g = this.vinc('excell_number', 'auto');

		var exlist = this.range.excells;
		for(var i=0;i<exlist.length;i++){
			var excell = exlist[i];
			if(excell.id>=this.board.cols+this.board.rows){ continue;}

			g.vid = "excell_text_"+excell.id;
			if(excell.bx>=0 || excell.by>=0){
				g.fillStyle = (excell.error!==1 ? this.fontcolor : this.fontErrcolor);
				this.disptext(""+excell.qnum, excell.bx*this.bw, excell.by*this.bh);
			}
			else{ g.vhide();}
		}
	},

	drawCircledNumbers_box : function(){
		var list = [], bd = this.board;
		var x1=this.range.x1, y1=this.range.y1, x2=this.range.x2, y2=this.range.y2;
		if(x2>=bd.maxbx){ for(var by=(y1|1),max=Math.min(bd.maxby,y2);by<=max;by+=2){ list.push([bd.maxbx+1,by]);}}
		if(y2>=bd.maxby){ for(var bx=(x1|1),max=Math.min(bd.maxbx,x2);bx<=max;bx+=2){ list.push([bx,bd.maxby+1]);}}

		var g = this.vinc('excell_circle', 'auto', true);
		var rsize = this.cw*0.36;
		g.fillStyle   = this.circledcolor;
		g.strokeStyle = this.quescolor;
		for(var i=0;i<list.length;i++){
			var num = ((list[i][0]!==bd.maxbx+1 ? list[i][0] : list[i][1])+1)>>1;
			g.vid = ["ex2_cir_",list[i][0],list[i][1]].join("_");
			if(num>0){
				g.shapeCircle(list[i][0]*this.bw, list[i][1]*this.bh, rsize);
			}
			else{ g.vhide();}
		}

		var option = {globalratio:0.95};
		g = this.vinc('excell_number2', 'auto');
		g.fillStyle = this.fontcolor;
		for(var i=0;i<list.length;i++){
			var num = ((list[i][0]!==bd.maxbx+1 ? list[i][0] : list[i][1])+1)>>1;
			g.vid = ["ex2_cirtext_",list[i][0],list[i][1]].join("_");
			if(num>0){
				this.disptext(""+num, list[i][0]*this.bw, list[i][1]*this.bh, option);
			}
			else{ g.vhide();}
		}
	}
},

//---------------------------------------------------------
// URLエンコード/デコード処理
Encode:{
	decodePzpr : function(type){
		this.decodeBox();
	},
	encodePzpr : function(type){
		this.encodeBox();
	},

	decodeBox : function(){
		var ec=0, bstr = this.outbstr, bd = this.board;
		for(var a=0;a<bstr.length;a++){
			var ca=bstr.charAt(a), excell=bd.excell[ec];
			if(ca==='-'){ excell.qnum = parseInt(bstr.substr(a+1,2),32); a+=2;}
			else        { excell.qnum = parseInt(ca,32);}
			ec++;
			if(ec >= bd.cols+bd.rows){ a++; break;}
		}

		this.outbstr = bstr.substr(a);
	},
	encodeBox : function(){
		var cm="", bd = this.board;
		for(var ec=0,len=bd.cols+bd.rows;ec<len;ec++){
			var qnum=bd.excell[ec].qnum;
			if(qnum<32){ cm+=("" +qnum.toString(32));}
			else       { cm+=("-"+qnum.toString(32));}
		}

		this.outbstr += cm;
	}
},
//---------------------------------------------------------
FileIO:{
	decodeData : function(){
		var bd = this.board, item = this.getItemList(bd.rows+1);
		for(var i=0;i<item.length;i++) {
			var ca = item[i];
			if(ca==="."){ continue;}

			var bx = i%(bd.cols+1)*2-1, by = ((i/(bd.cols+1))<<1)-1;
			var excell = bd.getex(bx,by);
			if(!excell.isnull){
				excell.qnum = +ca;
			}

			var cell = bd.getc(bx,by);
			if(!cell.isnull){
				if     (ca==="#"){ cell.qans = 1;}
				else if(ca==="+"){ cell.qsub = 1;}
			}
		}
	},
	encodeData : function(){
		var bd = this.board;
		for(var by=-1;by<bd.maxby;by+=2){
			for(var bx=-1;bx<bd.maxbx;bx+=2){
				var excell = bd.getex(bx,by);
				if(!excell.isnull){
					if(excell.id<bd.cols+bd.rows){
						this.datastr += (excell.qnum+" ");
					}
					else{
						this.datastr += ". ";
					}
					continue;
				}

				var cell = bd.getc(bx,by);
				if(!cell.isnull){
					if     (cell.qans===1){ this.datastr += "# ";}
					else if(cell.qsub===1){ this.datastr += "+ ";}
					else                  { this.datastr += ". ";}
					continue;
				}

				this.datastr += ". ";
			}
			this.datastr += "\n";
		}
	}
},

//---------------------------------------------------------
// 正解判定処理実行部
AnsCheck:{
	checklist : [
		"checkShadeCellExist",
		"checkShadeCells"
	],

	checkShadeCells : function(type){
		var bd = this.board;
		for(var ec=0;ec<bd.excell.length;ec++){
			var excell = bd.excell[ec];
			var qn=excell.qnum, pos=excell.getaddr(), val=0, cell;
			var clist=new this.klass.CellList();
			if(pos.by===-1 && pos.bx>0 && pos.bx<2*bd.cols){
				cell = pos.move(0,2).getc();
				while(!cell.isnull){
					if(cell.qans===1){ val+=((pos.by+1)>>1);}
					clist.add(cell);
					cell = pos.move(0,2).getc();
				}
			}
			else if(pos.bx===-1 && pos.by>0 && pos.by<2*bd.rows){
				cell = pos.move(2,0).getc();
				while(!cell.isnull){
					if(cell.qans===1){ val+=((pos.bx+1)>>1);}
					clist.add(cell);
					cell = pos.move(2,0).getc();
				}
			}
			else{ continue;}
			if(qn===val){ continue;}

			this.failcode.add("nmSumRowShadeNe");
			if(this.checkOnly){ break;}
			excell.seterr(1);
			clist.seterr(1);
		}
	}
},

FailCode:{
	nmSumRowShadeNe : ["数字と黒マスになった数字の合計が正しくありません。","A number is not equal to the sum of the number of shaded cells."]
}
}));
<|endoftext|>