<commit_before>// ==========================================================================
//                            DG.SliderView
//
//  Author:   William Finzer
//
//  Copyright (c) 2014 by The Concord Consortium, Inc. All rights reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// ==========================================================================

/** @class  DG.SliderView - The top level view for a slider

 @extends DG.View
 */
DG.SliderView = SC.View.extend(
  /** @scope DG.SliderView.prototype */
  (function() {
    var kWidth = 32,
        kThumbHeight = 36,
        kAxisHeight = 20,
        kButtonHeight = 18,
        kButtonWidth = 20,
        kGap = 5,
        tMouseDownInfo,
        tDraggingThumb = false;

    return {
      backgroundColor: "white",

      /**
       The model on which this view is based.
       @property { DG.SliderModel }
       */
      model: null,

      /**
       The model on which this view is based.
       @property { DG.CellLinearAxisView }
       */
      axisView: null,

      /**
       Subview that displays draggable thumb.
       @property { SC.ImageView }
       */
      thumbView: null,

      /**
       Subview that displays slider name and value.
       @property { SC.IconButton }
       */
      startButton: null,

      /**
       Subview that displays slider name and value.
       @property { SC.LabelView }
       */
      valueView: null,

      /**
       * SC.View styled to be vertical tic mark at end of axis
       */
      leftMarker: null,
      rightMarker: null,

      /**
       Set to true when user editing of valueView begins
       @property {Boolean}
       */
      userEdit: false,

      /**
       Stores the value of the slider before a mousedrag begins
       @property { Number }
       */
      _previousValue: 0,

      /**
       My model's value if there is one.
       @property { Number }
       */
      value: function() {
        return this.getPath('model.value');
      }.property('model').cacheable(),

      valueDidChange: function() {
        this.notifyPropertyChange('value');
      }.observes('*model.value'),

      /**
       The name of my model's global value
       @property { String }
       */
      name: function() {
        return this.getPath('model.name');
      }.property('model').cacheable(),

      nameDidChange: function() {
        this.notifyPropertyChange('name');
      }.observes('*model.name'),

      /**
       The screen coordinate of the model value.
       @property { Number }
       */
      thumbCoord: function() {
        var tValue = this.get('value'),
            tAxisView = this.get('axisView');
        return (SC.none( tValue) || SC.none( tAxisView)) ? null : tAxisView.dataToCoordinate( tValue);
      }.property('value'),

      thumbCoordDidChange: function() {
        this.notifyPropertyChange('thumbCoord');
      }.observes('*axisView.model.lowerBound', '*axisView.model.upperBound'),

      init: function() {
        // Notice that we are explicitly creating and laying out our subviews. This is because
        // when we tried doing it through childViews (the usual way), something about the dependencies
        // wouldn't work. (Might be worth figuring out.)
        sc_super();
        DG.assert( !SC.none( this.get( 'model' ) ) );

        this.set( 'axisView', DG.CellLinearAxisView.create(
          { layout: { left: kWidth / 2, right: kWidth / 2, height: kAxisHeight, bottom: 0 },
            orientation: 'horizontal', isDropTarget: false } ) );
        this.appendChild( this.axisView );
        this.setPath( 'axisView.model', this.getPath( 'model.axis' ) );

        this._previousValue = this.getPath('model.value');

        this.set('thumbView',
            DG.IconButton.create( {
              layout: { bottom: kAxisHeight, width: kWidth, height: kThumbHeight },
              iconExtent: { width: kWidth, height: kThumbHeight },
              iconClass: 'thumb-icon',
              classNames: 'display-styles'.w(),
              toolTip: 'DG.SliderView.thumbView.toolTip', // "Start/stop animation"
              localize: true,
              touchPriority: true,
              mouseDown: function() { return NO; }
            }));
        this.appendChild( this.thumbView );

        this.set('startButton',

          DG.IconButton.create( {
            layout: {top: kGap + 2, left: 2, width: kButtonWidth + 2, height: kButtonHeight + 2},
            iconExtent: { width: kButtonWidth, height: kButtonHeight },
            classNames: 'display-styles'.w(),
            target: this,
            action: 'toggleAnimation',
            toolTip: 'DG.SliderView.startButton.toolTip', // "Start/stop animation"
            localize: true
          }));

        this.appendChild( this.startButton );

        this.set('valueView',
          SC.LabelView.create(DG.MouseAndTouchView, {
            layout: { left: kButtonWidth + kGap, top: 3, bottom: kAxisHeight + 16 },
            classNames: 'slider-label'.w(),
            isEditable: true,
            exampleNode: null,
            inlineEditorWillBeginEditing: function( iEditor, iValue, iEditable) {
              sc_super();
              var tFrame = this.get('frame'),
                  kXGap = 4, kYGap = 5,
                  tOrigin = DG.ViewUtilities.viewToWindowCoordinates( { x: kXGap - 2, y: kYGap - 7 }, this);

              this.parentView.set('userEdit', true);

              // SC 1.10 introduced a new inline editor model in which
              // an 'exampleNode' is used to adjust inline editor style.
              var exampleNode = this.get('exampleNode');
              if(!exampleNode) {
                var parentNode = this.getPath('parentView.layer');
                exampleNode = this.get('layer').cloneNode(false);
                exampleNode.id = exampleNode.id + "-clone";
                exampleNode.style.visibility = 'hidden';
                parentNode.appendChild(exampleNode);
                this.set('exampleNode', exampleNode);
              }
              exampleNode.style.left = 0 + 'px';
              exampleNode.style.top = 9 + 'px';

              iEditor.set({ exampleElement: exampleNode,
                            exampleFrame: { x: tOrigin.x, y: tOrigin.y,
                                            width: tFrame.width - 2 * kXGap,
                                            height: tFrame.height - 2 * kYGap }});
            },
            doIt: function() {
              this.beginEditing();
            }
        }));
        this.appendChild( this.valueView );

        this.set('leftMarker',
            SC.View.create({
              classNames: 'slider-marker'.w(),
              layout: { left: 15, bottom: 19, width: 2, height: 8 }
            }));
        this.appendChild( this.leftMarker);
        this.set('rightMarker',
            SC.View.create({
              classNames: 'slider-marker'.w(),
              layout: { right: 14, bottom: 19, width: 2, height: 8 }
            }));
        this.appendChild( this.rightMarker);
      },

      _destroy: function () {
        var controller = this.get('controller');
        if( controller)
          controller.stopAnimation();
        this.model.destroy(); // so that it can unlink observers
      },

      willDestroy: function() {
        console.log('SliderView.willDestroy');
        sc_super();
      },

      destroy: function () {
        console.log('SliderView.destroy');
        this._destroy();
        sc_super();
      },

      viewDidResize: function() {
        sc_super();
        this.positionThumb();
      },

      /**
       * Use the value to position the thumbView.
       */
      positionThumb: function() {
        var tCoord = this.get('thumbCoord');
        if( DG.isFinite( tCoord)) {
          var thumbView = this.get('thumbView');
          if( thumbView)
            thumbView.adjust({left: Math.round( tCoord)});
        }
      }.observes('thumbCoord'),

      /**
       * Use the value to position the thumbView.
       */
      updateValueView: function() {
        var tAxis = this.get('axisView'),
            tDelta = tAxis.coordinateToData( 1) - tAxis.coordinateToData( 0),
            tLogDelta = Math.log( tDelta) / Math.LN10,
            tMaxDecimals = Math.ceil( -tLogDelta),
            tModel = this.get('model'),
            tName = SC.none( tModel) ? '' : tModel.get('name'),
            tNumber = SC.none( tModel) ? '' : DG.Format.number().group('').fractionDigits( 0, tMaxDecimals)( tModel.get('value'));

        this.setPath('valueView.value', tName + ' = ' + tNumber);
      }.observes('thumbCoord', 'name'),

      mouseDown: function( iEvent) {
        DG.globalEditorLock.commitCurrentEdit();
        var tThumbFrame = this.getPath('thumbView.frame');
        tMouseDownInfo = {
          pagePoint: { x: iEvent.pageX, y: iEvent.pageY },
          viewPoint: DG.ViewUtilities.windowToViewCoordinates( { x: iEvent.pageX, y: iEvent.pageY }, this),
          thumbCoord: this.get('thumbCoord')
        };
        if( DG.ViewUtilities.ptInRect( tMouseDownInfo.viewPoint, tThumbFrame)) {
          this._previousValue = this.getPath('model.value');
          tDraggingThumb = true;
          return YES;  // We handled it
        }
        else
          // Let other event handling proceed - especially double-click
          return NO;
      },

      mouseDragged: function( iEvent) {
        if( tDraggingThumb) {
          var tUpperBound = this.getPath('axisView.model.upperBound'),
              tLowerBound = this.getPath('axisView.model.lowerBound'),
              tDelta = iEvent.pageX - tMouseDownInfo.pagePoint.x,
              tNewCoord = tMouseDownInfo.thumbCoord + tDelta,
              tNewValue;
          tNewValue = Math.min( Math.max( this.get('axisView').coordinateToData( tNewCoord),
              tLowerBound), tUpperBound);
          this.setPath('model.value', tNewValue);
        }
      },

      mouseUp: function() {
        if( tDraggingThumb) {
          var this_ = this;
          DG.UndoHistory.execute(DG.Command.create({
            name: 'slider.change',
            undoString: 'DG.Undo.slider.change',
            redoString: 'DG.Redo.slider.change',
            log: "sliderThumbDrag: { \"name\": \"%@\", \"newValue\": %@ }".fmt(this_.getPath('model.name'), this_.getPath('model.value')),
            _componentId: this_.getPath('controller.model.id'),
            _controller: function() {
              return DG.currDocumentController().componentControllersMap[this._componentId];
            },
            _view: function() {
              return this._controller().get('sliderView');
            },
            _newValue: this_.getPath('model.value'),
            _prevValue: this_._previousValue,
            execute: function() {
              this._view().get('model').encompassValue();
              if (this._newValue === this._prevValue) {
                this.causedChange = false;
              }
            },
            undo: function() {
              var view = this._view();
              view.setPath('model.value', this._prevValue);
              view.get('model').encompassValue();
            },
            redo: function() {
              var view = this._view();
              view.setPath('model.value', this._newValue);
              view.get('model').encompassValue();
            }
          }));
        }
        tDraggingThumb = false;
      },

      touchStart: function(evt){
        return this.mouseDown(evt);
      },
      touchEnd: function(evt){
        return this.mouseUp(evt);
      },
      touchesDragged: function( evt, touches) {
        return this.mouseDragged( evt);
      },

      toggleAnimation: function() {
        var controller = this.get('controller');
        if( controller)
          controller.toggleAnimation();
      },

      /**
       * Updates the start button appearance
       */
      isAnimatingDidChange: function() {
        var startButton = this.get('startButton');
        if( !this.getPath('controller.isAnimating')) {
          startButton.set('iconClass', 'play-icon');
        }
        else {
          startButton.set('iconClass', 'pause-icon');
        }
      }.observes('*controller.isAnimating'),

      /**
       * Parses the user-edited contents of valueView. If it can identify a variable name and value,
       * the global value name and value will be set accordingly.
       */
      valueViewWasEdited: function() {
        // regular expression for matching 'variable = value'
        // TODO: Do better with white space
        // TODO: Handle scientific notation? Thousands separators? Internationalization?
        var kMatchExp = /^\s*([a-zA-Z_][\w]*)\s*=\s*([+\-]?[0-9.]+)/,
            tResult, tNewValue, tNewName;
        // We only want to parse the expression if the user actually edited it
        if( this.get('userEdit')) {
          var tNewString = this.getPath('valueView.value');
          tNewString = tNewString.replace('\u2212', '-');
          this.set('userEdit', false);
          tResult = kMatchExp.exec( tNewString);
          DG.logUser("sliderEdit: { expression: '%@', result: %@ }",
                                      tNewString, !SC.none( tResult) ? "success" : "failure");
          if( !SC.none( tResult)) {
            tNewValue = parseFloat( tResult[ 2]);
            tNewName = tResult[ 1];
            this.setPathIfChanged('model.value', tNewValue);
            this.setPathIfChanged('model.name', tNewName);
            this.get('model').encompassValue();
          }
          else
            this.updateValueView();
        }
      }.observes('.valueView.value')

    };
  }()) );

<commit_msg>[Fixed #127496761] Slider thumbnail does not respond to drag on iPad Added touchStart function at the thumb level<commit_after>// ==========================================================================
//                            DG.SliderView
//
//  Author:   William Finzer
//
//  Copyright (c) 2014 by The Concord Consortium, Inc. All rights reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// ==========================================================================

/** @class  DG.SliderView - The top level view for a slider

 @extends DG.View
 */
DG.SliderView = SC.View.extend(
  /** @scope DG.SliderView.prototype */
  (function() {
    var kWidth = 32,
        kThumbHeight = 36,
        kAxisHeight = 20,
        kButtonHeight = 18,
        kButtonWidth = 20,
        kGap = 5,
        tMouseDownInfo,
        tDraggingThumb = false;

    return {
      backgroundColor: "white",

      /**
       The model on which this view is based.
       @property { DG.SliderModel }
       */
      model: null,

      /**
       The model on which this view is based.
       @property { DG.CellLinearAxisView }
       */
      axisView: null,

      /**
       Subview that displays draggable thumb.
       @property { SC.ImageView }
       */
      thumbView: null,

      /**
       Subview that displays slider name and value.
       @property { SC.IconButton }
       */
      startButton: null,

      /**
       Subview that displays slider name and value.
       @property { SC.LabelView }
       */
      valueView: null,

      /**
       * SC.View styled to be vertical tic mark at end of axis
       */
      leftMarker: null,
      rightMarker: null,

      /**
       Set to true when user editing of valueView begins
       @property {Boolean}
       */
      userEdit: false,

      /**
       Stores the value of the slider before a mousedrag begins
       @property { Number }
       */
      _previousValue: 0,

      /**
       My model's value if there is one.
       @property { Number }
       */
      value: function() {
        return this.getPath('model.value');
      }.property('model').cacheable(),

      valueDidChange: function() {
        this.notifyPropertyChange('value');
      }.observes('*model.value'),

      /**
       The name of my model's global value
       @property { String }
       */
      name: function() {
        return this.getPath('model.name');
      }.property('model').cacheable(),

      nameDidChange: function() {
        this.notifyPropertyChange('name');
      }.observes('*model.name'),

      /**
       The screen coordinate of the model value.
       @property { Number }
       */
      thumbCoord: function() {
        var tValue = this.get('value'),
            tAxisView = this.get('axisView');
        return (SC.none( tValue) || SC.none( tAxisView)) ? null : tAxisView.dataToCoordinate( tValue);
      }.property('value'),

      thumbCoordDidChange: function() {
        this.notifyPropertyChange('thumbCoord');
      }.observes('*axisView.model.lowerBound', '*axisView.model.upperBound'),

      init: function() {
        // Notice that we are explicitly creating and laying out our subviews. This is because
        // when we tried doing it through childViews (the usual way), something about the dependencies
        // wouldn't work. (Might be worth figuring out.)
        sc_super();
        DG.assert( !SC.none( this.get( 'model' ) ) );

        this.set( 'axisView', DG.CellLinearAxisView.create(
          { layout: { left: kWidth / 2, right: kWidth / 2, height: kAxisHeight, bottom: 0 },
            orientation: 'horizontal', isDropTarget: false } ) );
        this.appendChild( this.axisView );
        this.setPath( 'axisView.model', this.getPath( 'model.axis' ) );

        this._previousValue = this.getPath('model.value');

        this.set('thumbView',
            DG.IconButton.create( {
              layout: { bottom: kAxisHeight, width: kWidth, height: kThumbHeight },
              iconExtent: { width: kWidth, height: kThumbHeight },
              iconClass: 'thumb-icon',
              classNames: 'display-styles'.w(),
              toolTip: 'DG.SliderView.thumbView.toolTip', // "Start/stop animation"
              localize: true,
              touchPriority: true,
              mouseDown: function() { return NO; },
              touchStart: function() {
                return this.mouseDown();
              }
            }));
        this.appendChild( this.thumbView );

        this.set('startButton',

          DG.IconButton.create( {
            layout: {top: kGap + 2, left: 2, width: kButtonWidth + 2, height: kButtonHeight + 2},
            iconExtent: { width: kButtonWidth, height: kButtonHeight },
            classNames: 'display-styles'.w(),
            target: this,
            action: 'toggleAnimation',
            toolTip: 'DG.SliderView.startButton.toolTip', // "Start/stop animation"
            localize: true
          }));

        this.appendChild( this.startButton );

        this.set('valueView',
          SC.LabelView.create(DG.MouseAndTouchView, {
            layout: { left: kButtonWidth + kGap, top: 3, bottom: kAxisHeight + 16 },
            classNames: 'slider-label'.w(),
            isEditable: true,
            exampleNode: null,
            inlineEditorWillBeginEditing: function( iEditor, iValue, iEditable) {
              sc_super();
              var tFrame = this.get('frame'),
                  kXGap = 4, kYGap = 5,
                  tOrigin = DG.ViewUtilities.viewToWindowCoordinates( { x: kXGap - 2, y: kYGap - 7 }, this);

              this.parentView.set('userEdit', true);

              // SC 1.10 introduced a new inline editor model in which
              // an 'exampleNode' is used to adjust inline editor style.
              var exampleNode = this.get('exampleNode');
              if(!exampleNode) {
                var parentNode = this.getPath('parentView.layer');
                exampleNode = this.get('layer').cloneNode(false);
                exampleNode.id = exampleNode.id + "-clone";
                exampleNode.style.visibility = 'hidden';
                parentNode.appendChild(exampleNode);
                this.set('exampleNode', exampleNode);
              }
              exampleNode.style.left = 0 + 'px';
              exampleNode.style.top = 9 + 'px';

              iEditor.set({ exampleElement: exampleNode,
                            exampleFrame: { x: tOrigin.x, y: tOrigin.y,
                                            width: tFrame.width - 2 * kXGap,
                                            height: tFrame.height - 2 * kYGap }});
            },
            doIt: function() {
              this.beginEditing();
            }
        }));
        this.appendChild( this.valueView );

        this.set('leftMarker',
            SC.View.create({
              classNames: 'slider-marker'.w(),
              layout: { left: 15, bottom: 12, width: 2, height: 15 }
            }));
        this.appendChild( this.leftMarker);
        this.set('rightMarker',
            SC.View.create({
              classNames: 'slider-marker'.w(),
              layout: { right: 14, bottom: 12, width: 2, height: 15 }
            }));
        this.appendChild( this.rightMarker);
      },

      _destroy: function () {
        var controller = this.get('controller');
        if( controller)
          controller.stopAnimation();
        this.model.destroy(); // so that it can unlink observers
      },

      willDestroy: function() {
        console.log('SliderView.willDestroy');
        sc_super();
      },

      destroy: function () {
        console.log('SliderView.destroy');
        this._destroy();
        sc_super();
      },

      viewDidResize: function() {
        sc_super();
        this.positionThumb();
      },

      /**
       * Use the value to position the thumbView.
       */
      positionThumb: function() {
        var tCoord = this.get('thumbCoord');
        if( DG.isFinite( tCoord)) {
          var thumbView = this.get('thumbView');
          if( thumbView)
            thumbView.adjust({left: Math.round( tCoord)});
        }
      }.observes('thumbCoord'),

      /**
       * Use the value to position the thumbView.
       */
      updateValueView: function() {
        var tAxis = this.get('axisView'),
            tDelta = tAxis.coordinateToData( 1) - tAxis.coordinateToData( 0),
            tLogDelta = Math.log( tDelta) / Math.LN10,
            tMaxDecimals = Math.ceil( -tLogDelta),
            tModel = this.get('model'),
            tName = SC.none( tModel) ? '' : tModel.get('name'),
            tNumber = SC.none( tModel) ? '' : DG.Format.number().group('').fractionDigits( 0, tMaxDecimals)( tModel.get('value'));

        this.setPath('valueView.value', tName + ' = ' + tNumber);
      }.observes('thumbCoord', 'name'),

      mouseDown: function( iEvent) {
        DG.globalEditorLock.commitCurrentEdit();
        var tThumbFrame = this.getPath('thumbView.frame');
        tMouseDownInfo = {
          pagePoint: { x: iEvent.pageX, y: iEvent.pageY },
          viewPoint: DG.ViewUtilities.windowToViewCoordinates( { x: iEvent.pageX, y: iEvent.pageY }, this),
          thumbCoord: this.get('thumbCoord')
        };
        if( DG.ViewUtilities.ptInRect( tMouseDownInfo.viewPoint, tThumbFrame)) {
          this._previousValue = this.getPath('model.value');
          tDraggingThumb = true;
          return YES;  // We handled it
        }
        else
          // Let other event handling proceed - especially double-click
          return NO;
      },

      mouseDragged: function( iEvent) {
        if( tDraggingThumb) {
          var tUpperBound = this.getPath('axisView.model.upperBound'),
              tLowerBound = this.getPath('axisView.model.lowerBound'),
              tDelta = iEvent.pageX - tMouseDownInfo.pagePoint.x,
              tNewCoord = tMouseDownInfo.thumbCoord + tDelta,
              tNewValue;
          tNewValue = Math.min( Math.max( this.get('axisView').coordinateToData( tNewCoord),
              tLowerBound), tUpperBound);
          this.setPath('model.value', tNewValue);
        }
      },

      mouseUp: function() {
        if( tDraggingThumb) {
          var this_ = this;
          DG.UndoHistory.execute(DG.Command.create({
            name: 'slider.change',
            undoString: 'DG.Undo.slider.change',
            redoString: 'DG.Redo.slider.change',
            log: "sliderThumbDrag: { \"name\": \"%@\", \"newValue\": %@ }".fmt(this_.getPath('model.name'), this_.getPath('model.value')),
            _componentId: this_.getPath('controller.model.id'),
            _controller: function() {
              return DG.currDocumentController().componentControllersMap[this._componentId];
            },
            _view: function() {
              return this._controller().get('sliderView');
            },
            _newValue: this_.getPath('model.value'),
            _prevValue: this_._previousValue,
            execute: function() {
              this._view().get('model').encompassValue();
              if (this._newValue === this._prevValue) {
                this.causedChange = false;
              }
            },
            undo: function() {
              var view = this._view();
              view.setPath('model.value', this._prevValue);
              view.get('model').encompassValue();
            },
            redo: function() {
              var view = this._view();
              view.setPath('model.value', this._newValue);
              view.get('model').encompassValue();
            }
          }));
        }
        tDraggingThumb = false;
      },

      touchStart: function(evt){
        return this.mouseDown(evt);
      },
      touchEnd: function(evt){
        return this.mouseUp(evt);
      },
      touchesDragged: function( evt, touches) {
        return this.mouseDragged( evt);
      },

      toggleAnimation: function() {
        var controller = this.get('controller');
        if( controller)
          controller.toggleAnimation();
      },

      /**
       * Updates the start button appearance
       */
      isAnimatingDidChange: function() {
        var startButton = this.get('startButton');
        if( !this.getPath('controller.isAnimating')) {
          startButton.set('iconClass', 'play-icon');
        }
        else {
          startButton.set('iconClass', 'pause-icon');
        }
      }.observes('*controller.isAnimating'),

      /**
       * Parses the user-edited contents of valueView. If it can identify a variable name and value,
       * the global value name and value will be set accordingly.
       */
      valueViewWasEdited: function() {
        // regular expression for matching 'variable = value'
        // TODO: Do better with white space
        // TODO: Handle scientific notation? Thousands separators? Internationalization?
        var kMatchExp = /^\s*([a-zA-Z_][\w]*)\s*=\s*([+\-]?[0-9.]+)/,
            tResult, tNewValue, tNewName;
        // We only want to parse the expression if the user actually edited it
        if( this.get('userEdit')) {
          var tNewString = this.getPath('valueView.value');
          tNewString = tNewString.replace('\u2212', '-');
          this.set('userEdit', false);
          tResult = kMatchExp.exec( tNewString);
          DG.logUser("sliderEdit: { expression: '%@', result: %@ }",
                                      tNewString, !SC.none( tResult) ? "success" : "failure");
          if( !SC.none( tResult)) {
            tNewValue = parseFloat( tResult[ 2]);
            tNewName = tResult[ 1];
            this.setPathIfChanged('model.value', tNewValue);
            this.setPathIfChanged('model.name', tNewName);
            this.get('model').encompassValue();
          }
          else
            this.updateValueView();
        }
      }.observes('.valueView.value')

    };
  }()) );

<|endoftext|>


<commit_before><commit_msg>Fix return value<commit_after><|endoftext|>


<commit_before><commit_msg>fix applab mocha tests<commit_after><|endoftext|>


<commit_before><commit_msg>remove unnecessary small-raffle-meter.js. Fix #64<commit_after><|endoftext|>


<commit_before><commit_msg>Renames  to  making this method private<commit_after><|endoftext|>


<commit_before><commit_msg>quick luacheck fix<commit_after><|endoftext|>


<commit_before>Helpers = initHelpers();

function initHelpers() {
  "use strict";
  var useCustomCss = false;

  return {
    report: reportHTML,
    isObj: isPlainObject,
    useJQ: loadJQ,
    augment: extensions,
    useCSS: setCustomCss
  };

  function reportHTML() {
    var report = document.querySelector('#result') ||
        function() {
          var r = document.createElement('div');
          r.id = 'result';
          document.querySelector('body').appendChild(r);
          return r;
        }();
    report.innerHTML += '
' + [].slice.call(arguments).join() + '
';
    if (useCustomCss) {
      var rp = document.querySelector('#result:last-child');
      setTimeout(function () {rp.className = 'fadeIn'}, 100);
    }
  }

  function setCustomCss(yn) {
    useCustomCss = yn;
    if (useCustomCss) {
     loadCSS();
    } else {
     unloadCSS();
    }
  }

  function isPlainObject(item) {
    return (
        item &&
        typeof item === "object" &&
        Object.prototype.toString.call(item) === "object Object" &&
        !item.nodeType
    );
  }

  function loadJQ(cb) {
      if (w.jQuery) {
        return function () {return void( cb && cb());}
      }
      return function () {
        var head  = d.querySelector('head')
           ,jq    = d.createElement('script');
        void( cb && jq.addEventListener('load',cb) );
        jq.src    = '//code.jquery.com/jquery-2.1.1.min.js';
        return head.appendChild(jq);
      };
  }

  function loadCSS() {
   if (document.querySelector('#HelperCSS')) {
    return true;
   }
   var css = document.createElement('link');
   css.href = "https://rawgit.com/KooiInc/Helpers/master/Helpers.css";
   css.type = "text/css";
   css.rel = "stylesheet";
   css.id = "HelperCSS";
   document.querySelector('head').appendChild(css);
  }

  function unloadCSS() {
   var css = document.querySelector('#HelperCSS');
   void (css && document.querySelector('head').removeChild(css));
  }

   // a few usefull augments/polyfills
  function extensions() {
        // remove double values from an array
        function noDoubles(arr) {
          arr = arr || this;
          return arr.filter(function(val) {
           return !this[val] ? ((this[val] = true), true) : false;
          }, {});
        }

        function args2Array(args){
          var arr = [];
          for (var i=0;i<args.length;i+=1) {
            arr.push(args[i]);
          }
          return arr;
        }

        function stringformat() {
                var args = args2Array(arguments);
                return this.replace(/(\{\d+\})/g, function(a){
                    return args[+(a.substr(1,a.length-2))||0];
                });
        };

        // determine value frequencies in an array
        function frequencies(arr) {
          var mapped = {sum: 0};
          (arr || this).map(function (a){
              if (!(a in this)) { this[a] = 1; }
              else { this[a] += 1; }
              this.sum += 1;
              return a; }, mapped
          );
          return mapped;
        }

        String.Format = function(){
            var args = [].slice.call(arguments);
            return stringformat.apply(args[0],args.slice(1));
        };

        String.prototype.format = function () {
          return stringformat.apply(this,arguments);
        }

        String.prototype.repeat = function(n){
            var s = this, r = '';
            while(n--) {
                r += s;
            }
            return r;
        };

        // run functions sequentially
        Function.prototype.andThen = function () {
         var args = [].slice.call(arguments)
            ,next = args.slice(1);
         this();
         return args[0] && next.length
                 ? args[0].andThen.apply(args[0], next)
                 : args.length == 1  ? args[0]()
                 : true;
        };

        // is character @ [atpos] upperCase?
        boolean:String.prototype.charIsUpper = function (atpos){
          var chr = this.charAt(atpos);
          return /[A-Z]|[\u0080-\u024F]/.test(chr) && chr === chr.toUpperCase();
        };

        String.prototype.firstUp = function () {
          return this.slice(0,1).toUpperCase()+this.slice(1).toLowerCase();
        }

        String.prototype.isValidEmail = function() {
          // should be sufficient
          return  /^[\w._-]{1,}[+]?[\w._-]{0,}@[\w.-]+\.[a-zA-Z]{2,6}$/.test(this);
        }

        Array.prototype.toRE = function (){
          try { return RegExp.apply(null, this); }
          catch(e) { return /.*/; }
        }

        Array.toRE = function(arr) {
          return ([].toRE.call(arr));
        }

        String.prototype.reCleanup = function(encodeHTML){
          var str = encodeHTML ? this.replace(/[\u0080-\u024F]/g, function(a) {return '&#'+a.charCodeAt(0)+';';}) : this;
          return str.replace(/[?*|.+$\/]|\\/g, function(c) {return c==='\\' ? '' : '\\\\'+c;});
        }

        String.concat = function(joinchar){
          joinchar = joinchar || '';
          return [].slice.call(arguments).join(joinchar);
        }

        // MDN Array filter polyfill
        if (!Array.prototype.filter) {
          Array.prototype.filter = function(fun)
          {
            if (this === void 0 || this === null)
              throw new TypeError();

            var t = Object(this);
            var len = t.length >>> 0;
            if (typeof fun != "function")
              throw new TypeError();

            var res = [];
            var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
            for (var i = 0; i < len; i++) {
              if (i in t)  {
                var val = t[i];
                if (fun.call(thisArg, val, i, t))
                  res.push(val);
              }
            }

            return res;
          };
        }

        // MDN Array map polyfill
        if (!Array.prototype.map) {
          Array.prototype.map = function(fun)
          {
            if (this === void 0 || this === null)
              throw new TypeError();

            var t = Object(this);
            var len = t.length >>> 0;
            if (typeof fun !== "function")
              throw new TypeError();

            var res = new Array(len);
            var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
            for (var i = 0; i < len; i++) {
              if (i in t)
                res[i] = fun.call(thisArg, t[i], i, t);
            }

            return res;
        };
       }
       Array.prototype.uniquify = noDoubles;
       Array.prototype.frequencies = Array.prototype.frequencies || frequencies;

       Array.prototype.each = Array.prototype.each || function (fn,rewrite) {
       for (var i = 0; i < this.length; i++) {
        if (rewrite){
          this[i] = fn(this[i]);
        } else {
         fn(this[i]);
        }
       }
       return this;
     };
  }
}
<commit_msg>Update Helpers.js<commit_after>Helpers = initHelpers();

function initHelpers() {
  "use strict";
  var useCustomCss = false;

  return {
    report: reportHTML,
    isObj: isPlainObject,
    useJQ: loadJQ,
    augment: extensions,
    useCSS: setCustomCss
  };

  function reportHTML() {
    var report = document.querySelector('#result') ||
        function() {
          var r = document.createElement('div');
          r.id = 'result';
          document.querySelector('body').appendChild(r);
          return r;
        }();
    report.innerHTML += '
' + [].slice.call(arguments).join() + '
';
    if (useCustomCss) {
      var rp = document.querySelector('#result:last-child');
      var to = function () { rp.className = 'fadeIn'; }
      setTimeout(to, 200);
    }
  }

  function setCustomCss(yn) {
    useCustomCss = yn;
    if (useCustomCss) {
     loadCSS();
    } else {
     unloadCSS();
    }
  }

  function isPlainObject(item) {
    return (
        item &&
        typeof item === "object" &&
        Object.prototype.toString.call(item) === "object Object" &&
        !item.nodeType
    );
  }

  function loadJQ(cb) {
      if (w.jQuery) {
        return function () {return void( cb && cb());}
      }
      return function () {
        var head  = d.querySelector('head')
           ,jq    = d.createElement('script');
        void( cb && jq.addEventListener('load',cb) );
        jq.src    = '//code.jquery.com/jquery-2.1.1.min.js';
        return head.appendChild(jq);
      };
  }

  function loadCSS() {
   if (document.querySelector('#HelperCSS')) {
    return true;
   }
   var css = document.createElement('link');
   css.href = "https://rawgit.com/KooiInc/Helpers/master/Helpers.css";
   css.type = "text/css";
   css.rel = "stylesheet";
   css.id = "HelperCSS";
   document.querySelector('head').appendChild(css);
  }

  function unloadCSS() {
   var css = document.querySelector('#HelperCSS');
   void (css && document.querySelector('head').removeChild(css));
  }

   // a few usefull augments/polyfills
  function extensions() {
        // remove double values from an array
        function noDoubles(arr) {
          arr = arr || this;
          return arr.filter(function(val) {
           return !this[val] ? ((this[val] = true), true) : false;
          }, {});
        }

        function args2Array(args){
          var arr = [];
          for (var i=0;i<args.length;i+=1) {
            arr.push(args[i]);
          }
          return arr;
        }

        function stringformat() {
                var args = args2Array(arguments);
                return this.replace(/(\{\d+\})/g, function(a){
                    return args[+(a.substr(1,a.length-2))||0];
                });
        };

        // determine value frequencies in an array
        function frequencies(arr) {
          var mapped = {sum: 0};
          (arr || this).map(function (a){
              if (!(a in this)) { this[a] = 1; }
              else { this[a] += 1; }
              this.sum += 1;
              return a; }, mapped
          );
          return mapped;
        }

        String.Format = function(){
            var args = [].slice.call(arguments);
            return stringformat.apply(args[0],args.slice(1));
        };

        String.prototype.format = function () {
          return stringformat.apply(this,arguments);
        }

        String.prototype.repeat = function(n){
            var s = this, r = '';
            while(n--) {
                r += s;
            }
            return r;
        };

        // run functions sequentially
        Function.prototype.andThen = function () {
         var args = [].slice.call(arguments)
            ,next = args.slice(1);
         this();
         return args[0] && next.length
                 ? args[0].andThen.apply(args[0], next)
                 : args.length == 1  ? args[0]()
                 : true;
        };

        // is character @ [atpos] upperCase?
        boolean:String.prototype.charIsUpper = function (atpos){
          var chr = this.charAt(atpos);
          return /[A-Z]|[\u0080-\u024F]/.test(chr) && chr === chr.toUpperCase();
        };

        String.prototype.firstUp = function () {
          return this.slice(0,1).toUpperCase()+this.slice(1).toLowerCase();
        }

        String.prototype.isValidEmail = function() {
          // should be sufficient
          return  /^[\w._-]{1,}[+]?[\w._-]{0,}@[\w.-]+\.[a-zA-Z]{2,6}$/.test(this);
        }

        Array.prototype.toRE = function (){
          try { return RegExp.apply(null, this); }
          catch(e) { return /.*/; }
        }

        Array.toRE = function(arr) {
          return ([].toRE.call(arr));
        }

        String.prototype.reCleanup = function(encodeHTML){
          var str = encodeHTML ? this.replace(/[\u0080-\u024F]/g, function(a) {return '&#'+a.charCodeAt(0)+';';}) : this;
          return str.replace(/[?*|.+$\/]|\\/g, function(c) {return c==='\\' ? '' : '\\\\'+c;});
        }

        String.concat = function(joinchar){
          joinchar = joinchar || '';
          return [].slice.call(arguments).join(joinchar);
        }

        // MDN Array filter polyfill
        if (!Array.prototype.filter) {
          Array.prototype.filter = function(fun)
          {
            if (this === void 0 || this === null)
              throw new TypeError();

            var t = Object(this);
            var len = t.length >>> 0;
            if (typeof fun != "function")
              throw new TypeError();

            var res = [];
            var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
            for (var i = 0; i < len; i++) {
              if (i in t)  {
                var val = t[i];
                if (fun.call(thisArg, val, i, t))
                  res.push(val);
              }
            }

            return res;
          };
        }

        // MDN Array map polyfill
        if (!Array.prototype.map) {
          Array.prototype.map = function(fun)
          {
            if (this === void 0 || this === null)
              throw new TypeError();

            var t = Object(this);
            var len = t.length >>> 0;
            if (typeof fun !== "function")
              throw new TypeError();

            var res = new Array(len);
            var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
            for (var i = 0; i < len; i++) {
              if (i in t)
                res[i] = fun.call(thisArg, t[i], i, t);
            }

            return res;
        };
       }
       Array.prototype.uniquify = noDoubles;
       Array.prototype.frequencies = Array.prototype.frequencies || frequencies;

       Array.prototype.each = Array.prototype.each || function (fn,rewrite) {
       for (var i = 0; i < this.length; i++) {
        if (rewrite){
          this[i] = fn(this[i]);
        } else {
         fn(this[i]);
        }
       }
       return this;
     };
  }
}
<|endoftext|>


<commit_before><commit_msg>Build class skeletons<commit_after>
// class Impetus
function Impetus()
{
}
// function getSound(String url):ImpetusSound
Impetus.prototype.getSound = function(url)
{
};

// class ImpetusSound
function ImpetusSound(url)
{
    this.url = url; // :String
}
// function playNew():ImpetusChannel
ImpetusSound.prototype.playNew = function()
{
};
// function stopAll():void
ImpetusSound.prototype.stopAll = function()
{
};

// class ImpetusChannel
function ImpetusChannel()
{
}
// function play():void
ImpetusChannel.prototype.play = function()
{
};
// function stop():void
ImpetusChannel.prototype.stop = function()
{
};
// function setPos(int pos):void
ImpetusChannel.prototype.setPos = function(pos)
{
};
// function getPos():int
ImpetusChannel.prototype.getPos = function()
{
};
<|endoftext|>


<commit_before>/* @flow */

import { parseText } from 'compiler/parser/text-parser'
import { parseStyleText } from 'web/util/style'
import {
  getAndRemoveAttr,
  getBindingAttr,
  baseWarn
} from 'compiler/helpers'

function transformNode (el: ASTElement, options: CompilerOptions) {
  const warn = options.warn || baseWarn
  const staticStyle = getAndRemoveAttr(el, 'style')
  if (staticStyle) {
    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production') {
      const expression = parseText(staticStyle, options.delimiters)
      if (expression) {
        warn(
            `style="${staticStyle}": ` +
            'Interpolation inside attributes has been removed. ' +
            'Use v-bind or the colon shorthand instead. For example, ' +
            'instead of
, use
.'
        )
      }
    }
    el.staticStyle = JSON.stringify(parseStyleText(staticStyle))
  }

  const styleBinding = getBindingAttr(el, 'style', false /* getStatic */)
  if (styleBinding) {
    el.styleBinding = styleBinding
  }
}

function genData (el: ASTElement): string {
  let data = ''
  if (el.staticStyle) {
    data += `staticStyle:${el.staticStyle},`
  }
  if (el.styleBinding) {
    data += `style:(${el.styleBinding}),`
  }
  return data
}

export default {
  staticKeys: ['staticStyle'],
  transformNode,
  genData
}
<commit_msg>tweak indent<commit_after>/* @flow */

import { parseText } from 'compiler/parser/text-parser'
import { parseStyleText } from 'web/util/style'
import {
  getAndRemoveAttr,
  getBindingAttr,
  baseWarn
} from 'compiler/helpers'

function transformNode (el: ASTElement, options: CompilerOptions) {
  const warn = options.warn || baseWarn
  const staticStyle = getAndRemoveAttr(el, 'style')
  if (staticStyle) {
    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production') {
      const expression = parseText(staticStyle, options.delimiters)
      if (expression) {
        warn(
          `style="${staticStyle}": ` +
          'Interpolation inside attributes has been removed. ' +
          'Use v-bind or the colon shorthand instead. For example, ' +
          'instead of
, use
.'
        )
      }
    }
    el.staticStyle = JSON.stringify(parseStyleText(staticStyle))
  }

  const styleBinding = getBindingAttr(el, 'style', false /* getStatic */)
  if (styleBinding) {
    el.styleBinding = styleBinding
  }
}

function genData (el: ASTElement): string {
  let data = ''
  if (el.staticStyle) {
    data += `staticStyle:${el.staticStyle},`
  }
  if (el.styleBinding) {
    data += `style:(${el.styleBinding}),`
  }
  return data
}

export default {
  staticKeys: ['staticStyle'],
  transformNode,
  genData
}
<|endoftext|>


<commit_before>/* Copyright (c) 2015 Jonathan Herman. MIT License. */
/* https://github.com/jdh11235/algorithms */

function randomlyPartition (n, p) {
	//n: number to be partitioned
	//p: number of partitions
	//n and p must be positive integers
	//n must be greater than or equal to p

	function randomFromRange (min, max) {
		return Math.round(Math.random() * (max - min) + min);
	}

	function isInteger (n) {
		return (n % 2 === 0 || n % 2 === 1);
	}

	function shuffle (set) {
		return set.sort(function () {
			return 0.5 - Math.random();
		});
	}

	function fork (n) {
		var split = randomFromRange(1, n - 1);
		return [split, n - split];
	}

	//test: n and p are numbers
	if ( !(typeof n === 'number' && typeof p === 'number') ) {
		return false;
	}

	//test: n and p are integers
	if ( !(isInteger(n) && isInteger(p) && n > 0 && p > 0) ) {
		return false;
	}

	//test: n is greater than or equal to p
	if ( !(n >= p) ) {
		return false;
	}

	var set = [n];
	var subset;
	var index;
	var pos;
	var forked;

	//partition set recursively until set has p partitions
	for (var i = 0, i2 = 0; i < p - 1; i++) {
		subset = [];
		index = [];

		//filter into subset: numbers greater than 1 (because 1 can't be split into two positive integers)
		for (i2 = 0; i2 < set.length; i2++) {
			if (set[i2] > 1) {
				subset.push(set[i2]);
				index.push(i2);
			}
		}

		//randomly select number from subset
		pos = randomFromRange(0, subset.length - 1);

		//randomly partition number into two parts
		forked = fork(subset[pos]);

		//replace number in set with the two resulting partitions of that number
		set.splice(index[pos], 1, forked[0], forked[1]);
	}

	return shuffle(set);
}
<commit_msg>definition tests clarification<commit_after>/* Copyright (c) 2015 Jonathan Herman. MIT License. */
/* https://github.com/jdh11235/algorithms */

function randomlyPartition (n, p) {
	//n: number to be partitioned
	//p: number of partitions
	//n and p must be positive integers
	//n must be greater than or equal to p

	function randomFromRange (min, max) {
		return Math.round(Math.random() * (max - min) + min);
	}

	function isInteger (n) {
		return (n % 2 === 0 || n % 2 === 1);
	}

	function shuffle (set) {
		return set.sort(function () {
			return 0.5 - Math.random();
		});
	}

	function fork (n) {
		var split = randomFromRange(1, n - 1);
		return [split, n - split];
	}

	//test: n and p exist and are numbers
	if ( !(typeof n === 'number' && typeof p === 'number') ) {
		return false;
	}

	//test: n and p are positive integers
	if ( !(isInteger(n) && isInteger(p) && n > 0 && p > 0) ) {
		return false;
	}

	//test: n is greater than or equal to p
	if ( !(n >= p) ) {
		return false;
	}

	var set = [n];
	var subset;
	var index;
	var pos;
	var forked;

	//partition set recursively until set has p partitions
	for (var i = 0, i2 = 0; i < p - 1; i++) {
		subset = [];
		index = [];

		//filter into subset: numbers greater than 1 (because 1 can't be split into two positive integers)
		for (i2 = 0; i2 < set.length; i2++) {
			if (set[i2] > 1) {
				subset.push(set[i2]);
				index.push(i2);
			}
		}

		//randomly select number from subset
		pos = randomFromRange(0, subset.length - 1);

		//randomly partition number into two parts
		forked = fork(subset[pos]);

		//replace number in set with the two resulting partitions of that number
		set.splice(index[pos], 1, forked[0], forked[1]);
	}

	return shuffle(set);
}
<|endoftext|>


<commit_before>import { lst } from "./misc.js"

// BIDI HELPERS

export function iterateBidiSections(order, from, to, f) {
  if (!order) return f(from, to, "ltr", 0)
  let found = false
  for (let i = 0; i < order.length; ++i) {
    let part = order[i]
    if (part.from < to && part.to > from || from == to && part.to == from) {
      f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i)
      found = true
    }
  }
  if (!found) f(from, to, "ltr")
}

export let bidiOther = null
export function getBidiPartAt(order, ch, sticky) {
  let found
  bidiOther = null
  for (let i = 0; i < order.length; ++i) {
    let cur = order[i]
    if (cur.from < ch && cur.to > ch) return i
    if (cur.to == ch) {
      if (cur.from != cur.to && sticky == "before") found = i
      else bidiOther = i
    }
    if (cur.from == ch) {
      if (cur.from != cur.to && sticky != "before") found = i
      else bidiOther = i
    }
  }
  return found != null ? found : bidiOther
}

// Bidirectional ordering algorithm
// See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
// that this (partially) implements.

// One-char codes used for character types:
// L (L):   Left-to-Right
// R (R):   Right-to-Left
// r (AL):  Right-to-Left Arabic
// 1 (EN):  European Number
// + (ES):  European Number Separator
// % (ET):  European Number Terminator
// n (AN):  Arabic Number
// , (CS):  Common Number Separator
// m (NSM): Non-Spacing Mark
// b (BN):  Boundary Neutral
// s (B):   Paragraph Separator
// t (S):   Segment Separator
// w (WS):  Whitespace
// N (ON):  Other Neutrals

// Returns null if characters are ordered as they appear
// (left-to-right), or an array of sections ({from, to, level}
// objects) in the order in which they occur visually.
let bidiOrdering = (function() {
  // Character types for codepoints 0 to 0xff
  let lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"
  // Character types for codepoints 0x600 to 0x6f9
  let arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"
  function charType(code) {
    if (code <= 0xf7) return lowTypes.charAt(code)
    else if (0x590 <= code && code <= 0x5f4) return "R"
    else if (0x600 <= code && code <= 0x6f9) return arabicTypes.charAt(code - 0x600)
    else if (0x6ee <= code && code <= 0x8ac) return "r"
    else if (0x2000 <= code && code <= 0x200b) return "w"
    else if (code == 0x200c) return "b"
    else return "L"
  }

  let bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/
  let isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/

  function BidiSpan(level, from, to) {
    this.level = level
    this.from = from; this.to = to
  }

  return function(str, direction) {
    let outerType = direction == "ltr" ? "L" : "R"

    if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) return false
    let len = str.length, types = []
    for (let i = 0; i < len; ++i)
      types.push(charType(str.charCodeAt(i)))

    // W1. Examine each non-spacing mark (NSM) in the level run, and
    // change the type of the NSM to the type of the previous
    // character. If the NSM is at the start of the level run, it will
    // get the type of sor.
    for (let i = 0, prev = outerType; i < len; ++i) {
      let type = types[i]
      if (type == "m") types[i] = prev
      else prev = type
    }

    // W2. Search backwards from each instance of a European number
    // until the first strong type (R, L, AL, or sor) is found. If an
    // AL is found, change the type of the European number to Arabic
    // number.
    // W3. Change all ALs to R.
    for (let i = 0, cur = outerType; i < len; ++i) {
      let type = types[i]
      if (type == "1" && cur == "r") types[i] = "n"
      else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R" }
    }

    // W4. A single European separator between two European numbers
    // changes to a European number. A single common separator between
    // two numbers of the same type changes to that type.
    for (let i = 1, prev = types[0]; i < len - 1; ++i) {
      let type = types[i]
      if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1"
      else if (type == "," && prev == types[i+1] &&
               (prev == "1" || prev == "n")) types[i] = prev
      prev = type
    }

    // W5. A sequence of European terminators adjacent to European
    // numbers changes to all European numbers.
    // W6. Otherwise, separators and terminators change to Other
    // Neutral.
    for (let i = 0; i < len; ++i) {
      let type = types[i]
      if (type == ",") types[i] = "N"
      else if (type == "%") {
        let end
        for (end = i + 1; end < len && types[end] == "%"; ++end) {}
        let replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"
        for (let j = i; j < end; ++j) types[j] = replace
        i = end - 1
      }
    }

    // W7. Search backwards from each instance of a European number
    // until the first strong type (R, L, or sor) is found. If an L is
    // found, then change the type of the European number to L.
    for (let i = 0, cur = outerType; i < len; ++i) {
      let type = types[i]
      if (cur == "L" && type == "1") types[i] = "L"
      else if (isStrong.test(type)) cur = type
    }

    // N1. A sequence of neutrals takes the direction of the
    // surrounding strong text if the text on both sides has the same
    // direction. European and Arabic numbers act as if they were R in
    // terms of their influence on neutrals. Start-of-level-run (sor)
    // and end-of-level-run (eor) are used at level run boundaries.
    // N2. Any remaining neutrals take the embedding direction.
    for (let i = 0; i < len; ++i) {
      if (isNeutral.test(types[i])) {
        let end
        for (end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
        let before = (i ? types[i-1] : outerType) == "L"
        let after = (end < len ? types[end] : outerType) == "L"
        let replace = before == after ? (before ? "L" : "R") : outerType
        for (let j = i; j < end; ++j) types[j] = replace
        i = end - 1
      }
    }

    // Here we depart from the documented algorithm, in order to avoid
    // building up an actual levels array. Since there are only three
    // levels (0, 1, 2) in an implementation that doesn't take
    // explicit embedding into account, we can build up the order on
    // the fly, without following the level-based algorithm.
    let order = [], m
    for (let i = 0; i < len;) {
      if (countsAsLeft.test(types[i])) {
        let start = i
        for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
        order.push(new BidiSpan(0, start, i))
      } else {
        let pos = i, at = order.length
        for (++i; i < len && types[i] != "L"; ++i) {}
        for (let j = pos; j < i;) {
          if (countsAsNum.test(types[j])) {
            if (pos < j) order.splice(at, 0, new BidiSpan(1, pos, j))
            let nstart = j
            for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
            order.splice(at, 0, new BidiSpan(2, nstart, j))
            pos = j
          } else ++j
        }
        if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i))
      }
    }
    if (direction == "ltr") {
      if (order[0].level == 1 && (m = str.match(/^\s+/))) {
        order[0].from = m[0].length
        order.unshift(new BidiSpan(0, 0, m[0].length))
      }
      if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
        lst(order).to -= m[0].length
        order.push(new BidiSpan(0, len - m[0].length, len))
      }
    }

    return direction == "rtl" ? order.reverse() : order
  }
})()

// Get the bidi ordering for the given line (and cache it). Returns
// false for lines that are fully left-to-right, and an array of
// BidiSpan objects otherwise.
export function getOrder(line, direction) {
  let order = line.order
  if (order == null) order = line.order = bidiOrdering(line.text, direction)
  return order
}
<commit_msg>Fix bug in bidi algorithm<commit_after>import { lst } from "./misc.js"

// BIDI HELPERS

export function iterateBidiSections(order, from, to, f) {
  if (!order) return f(from, to, "ltr", 0)
  let found = false
  for (let i = 0; i < order.length; ++i) {
    let part = order[i]
    if (part.from < to && part.to > from || from == to && part.to == from) {
      f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i)
      found = true
    }
  }
  if (!found) f(from, to, "ltr")
}

export let bidiOther = null
export function getBidiPartAt(order, ch, sticky) {
  let found
  bidiOther = null
  for (let i = 0; i < order.length; ++i) {
    let cur = order[i]
    if (cur.from < ch && cur.to > ch) return i
    if (cur.to == ch) {
      if (cur.from != cur.to && sticky == "before") found = i
      else bidiOther = i
    }
    if (cur.from == ch) {
      if (cur.from != cur.to && sticky != "before") found = i
      else bidiOther = i
    }
  }
  return found != null ? found : bidiOther
}

// Bidirectional ordering algorithm
// See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
// that this (partially) implements.

// One-char codes used for character types:
// L (L):   Left-to-Right
// R (R):   Right-to-Left
// r (AL):  Right-to-Left Arabic
// 1 (EN):  European Number
// + (ES):  European Number Separator
// % (ET):  European Number Terminator
// n (AN):  Arabic Number
// , (CS):  Common Number Separator
// m (NSM): Non-Spacing Mark
// b (BN):  Boundary Neutral
// s (B):   Paragraph Separator
// t (S):   Segment Separator
// w (WS):  Whitespace
// N (ON):  Other Neutrals

// Returns null if characters are ordered as they appear
// (left-to-right), or an array of sections ({from, to, level}
// objects) in the order in which they occur visually.
let bidiOrdering = (function() {
  // Character types for codepoints 0 to 0xff
  let lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"
  // Character types for codepoints 0x600 to 0x6f9
  let arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"
  function charType(code) {
    if (code <= 0xf7) return lowTypes.charAt(code)
    else if (0x590 <= code && code <= 0x5f4) return "R"
    else if (0x600 <= code && code <= 0x6f9) return arabicTypes.charAt(code - 0x600)
    else if (0x6ee <= code && code <= 0x8ac) return "r"
    else if (0x2000 <= code && code <= 0x200b) return "w"
    else if (code == 0x200c) return "b"
    else return "L"
  }

  let bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/
  let isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/

  function BidiSpan(level, from, to) {
    this.level = level
    this.from = from; this.to = to
  }

  return function(str, direction) {
    let outerType = direction == "ltr" ? "L" : "R"

    if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) return false
    let len = str.length, types = []
    for (let i = 0; i < len; ++i)
      types.push(charType(str.charCodeAt(i)))

    // W1. Examine each non-spacing mark (NSM) in the level run, and
    // change the type of the NSM to the type of the previous
    // character. If the NSM is at the start of the level run, it will
    // get the type of sor.
    for (let i = 0, prev = outerType; i < len; ++i) {
      let type = types[i]
      if (type == "m") types[i] = prev
      else prev = type
    }

    // W2. Search backwards from each instance of a European number
    // until the first strong type (R, L, AL, or sor) is found. If an
    // AL is found, change the type of the European number to Arabic
    // number.
    // W3. Change all ALs to R.
    for (let i = 0, cur = outerType; i < len; ++i) {
      let type = types[i]
      if (type == "1" && cur == "r") types[i] = "n"
      else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R" }
    }

    // W4. A single European separator between two European numbers
    // changes to a European number. A single common separator between
    // two numbers of the same type changes to that type.
    for (let i = 1, prev = types[0]; i < len - 1; ++i) {
      let type = types[i]
      if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1"
      else if (type == "," && prev == types[i+1] &&
               (prev == "1" || prev == "n")) types[i] = prev
      prev = type
    }

    // W5. A sequence of European terminators adjacent to European
    // numbers changes to all European numbers.
    // W6. Otherwise, separators and terminators change to Other
    // Neutral.
    for (let i = 0; i < len; ++i) {
      let type = types[i]
      if (type == ",") types[i] = "N"
      else if (type == "%") {
        let end
        for (end = i + 1; end < len && types[end] == "%"; ++end) {}
        let replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"
        for (let j = i; j < end; ++j) types[j] = replace
        i = end - 1
      }
    }

    // W7. Search backwards from each instance of a European number
    // until the first strong type (R, L, or sor) is found. If an L is
    // found, then change the type of the European number to L.
    for (let i = 0, cur = outerType; i < len; ++i) {
      let type = types[i]
      if (cur == "L" && type == "1") types[i] = "L"
      else if (isStrong.test(type)) cur = type
    }

    // N1. A sequence of neutrals takes the direction of the
    // surrounding strong text if the text on both sides has the same
    // direction. European and Arabic numbers act as if they were R in
    // terms of their influence on neutrals. Start-of-level-run (sor)
    // and end-of-level-run (eor) are used at level run boundaries.
    // N2. Any remaining neutrals take the embedding direction.
    for (let i = 0; i < len; ++i) {
      if (isNeutral.test(types[i])) {
        let end
        for (end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
        let before = (i ? types[i-1] : outerType) == "L"
        let after = (end < len ? types[end] : outerType) == "L"
        let replace = before == after ? (before ? "L" : "R") : outerType
        for (let j = i; j < end; ++j) types[j] = replace
        i = end - 1
      }
    }

    // Here we depart from the documented algorithm, in order to avoid
    // building up an actual levels array. Since there are only three
    // levels (0, 1, 2) in an implementation that doesn't take
    // explicit embedding into account, we can build up the order on
    // the fly, without following the level-based algorithm.
    let order = [], m
    for (let i = 0; i < len;) {
      if (countsAsLeft.test(types[i])) {
        let start = i
        for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
        order.push(new BidiSpan(0, start, i))
      } else {
        let pos = i, at = order.length, isRTL = direction == "rtl" ? 1 : 0
        for (++i; i < len && types[i] != "L"; ++i) {}
        for (let j = pos; j < i;) {
          if (countsAsNum.test(types[j])) {
            if (pos < j) { order.splice(at, 0, new BidiSpan(1, pos, j)); at += isRTL }
            let nstart = j
            for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
            order.splice(at, 0, new BidiSpan(2, nstart, j))
            at += isRTL
            pos = j
          } else ++j
        }
        if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i))
      }
    }
    if (direction == "ltr") {
      if (order[0].level == 1 && (m = str.match(/^\s+/))) {
        order[0].from = m[0].length
        order.unshift(new BidiSpan(0, 0, m[0].length))
      }
      if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
        lst(order).to -= m[0].length
        order.push(new BidiSpan(0, len - m[0].length, len))
      }
    }

    return direction == "rtl" ? order.reverse() : order
  }
})()

// Get the bidi ordering for the given line (and cache it). Returns
// false for lines that are fully left-to-right, and an array of
// BidiSpan objects otherwise.
export function getOrder(line, direction) {
  let order = line.order
  if (order == null) order = line.order = bidiOrdering(line.text, direction)
  return order
}
<|endoftext|>


<commit_before>(function(){

	var infoItems = [
		"screen.availHeight",
		"screen.availWidth",
		"screen.height",
		"screen.width",

		"window.innerHeight",
		"window.innerWidth",
		"window.outerHeight",
		"window.outerWidth",

		"Widget.Device.widgetEngineProvider",
		"Widget.Device.widgetEngineVersion",
		"Widget.Device.widgetEngineName",
		"Widget.Device.getAvailableApplications()",

		"Widget.Device.DeviceInfo.phoneModel",
		"Widget.Device.DeviceInfo.phoneOS",
		"Widget.Device.DeviceInfo.phoneFirmware",
		"Widget.Device.DeviceInfo.phoneScreenHeightDefault",
		"Widget.Device.DeviceInfo.phoneScreenWidthDefault"
	];

	function getObj(path, context){
		var current = context || this;
		var bits = path.split(/\s*\.\s*/);
		var bit;
		while(current && (bit = bits.shift()) != null){
			if(bit.slice(-2) === "()"){
				current = current[bit.slice(0, -2)]();
			}else{
				current = current[bit];
			}
		}

		return current;
	}

	var _escapeNode = document.createElement("span");
	function e(str) {
		_escapeNode.innerHTML = "";
		_escapeNode.appendChild(document.createTextNode(str));
		return _escapeNode.innerHTML;
	}

	function getType(obj) {
		var type = typeof obj;
		if (type === "object") {
			type = Object.prototype.toString.call(obj).slice(8, -1)
		}

		return type;
	}

	var labelIndent = 0;
	function makeLabel(obj) {
		var type = getType(obj);
		var indent = Array(labelIndent+1).join("  ");
		if(obj && obj.length && type !== "string" && type !== "String" && type !== "function" && type !== "Function"){
			labelIndent++;
			obj = Array.prototype.map.call(obj, makeLabel);
			obj = ["["].concat(obj, indent + "]").join("\n");
			labelIndent--;
		}else if(type === "Object"){
			var props = [];
			var i = 0;
			var origLabelIndent = labelIndent;
			labelIndent = 0;
			for(var prop in obj){
				if (i > 25) {
					props[i++] = indent + "  \u2026";
					break;
				}
				props[i++] = indent + "  " + prop + ": " + makeLabel(obj[prop]);
			}
			obj = ["{"].concat(props, indent + "}").join("\n");
			labelIndent = origLabelIndent;
		}

		return indent + "(" + type + ") " + obj;
	}

var infoHTML = "
" +
	infoItems.map(function(item){
		var obj = getObj(item);
		return '
' + e(item) + '
' +
				'

' + e(makeLabel(obj)) + '

';
	}).join("") +
	"
";

	dohx.add({
		name: "Widget Information",
		tests: [
			{
				id: 1,
				instructions: infoHTML,
				name: "Widget Information",
				test: function(){
					console.log(arguments)
				}
			}
		]
	});

}());
<commit_msg>Added “navigator.userAgent” to info widget<commit_after>(function(){

	var infoItems = [
		"screen.availHeight",
		"screen.availWidth",
		"screen.height",
		"screen.width",

		"window.innerHeight",
		"window.innerWidth",
		"window.outerHeight",
		"window.outerWidth",

		"navigator.userAgent",

		"Widget.Device.widgetEngineProvider",
		"Widget.Device.widgetEngineVersion",
		"Widget.Device.widgetEngineName",
		"Widget.Device.getAvailableApplications()",

		"Widget.Device.DeviceInfo.phoneModel",
		"Widget.Device.DeviceInfo.phoneOS",
		"Widget.Device.DeviceInfo.phoneFirmware",
		"Widget.Device.DeviceInfo.phoneScreenHeightDefault",
		"Widget.Device.DeviceInfo.phoneScreenWidthDefault"
	];

	function getObj(path, context){
		var current = context || this;
		var bits = path.split(/\s*\.\s*/);
		var bit;
		while(current && (bit = bits.shift()) != null){
			if(bit.slice(-2) === "()"){
				current = current[bit.slice(0, -2)]();
			}else{
				current = current[bit];
			}
		}

		return current;
	}

	var _escapeNode = document.createElement("span");
	function e(str) {
		_escapeNode.innerHTML = "";
		_escapeNode.appendChild(document.createTextNode(str));
		return _escapeNode.innerHTML;
	}

	function getType(obj) {
		var type = typeof obj;
		if (type === "object") {
			type = Object.prototype.toString.call(obj).slice(8, -1)
		}

		return type;
	}

	var labelIndent = 0;
	function makeLabel(obj) {
		var type = getType(obj);
		var indent = Array(labelIndent+1).join("  ");
		if(obj && obj.length && type !== "string" && type !== "String" && type !== "function" && type !== "Function"){
			labelIndent++;
			obj = Array.prototype.map.call(obj, makeLabel);
			obj = ["["].concat(obj, indent + "]").join("\n");
			labelIndent--;
		}else if(type === "Object"){
			var props = [];
			var i = 0;
			var origLabelIndent = labelIndent;
			labelIndent = 0;
			for(var prop in obj){
				if (i > 25) {
					props[i++] = indent + "  \u2026";
					break;
				}
				props[i++] = indent + "  " + prop + ": " + makeLabel(obj[prop]);
			}
			obj = ["{"].concat(props, indent + "}").join("\n");
			labelIndent = origLabelIndent;
		}

		return indent + "(" + type + ") " + obj;
	}

var infoHTML = "
" +
	infoItems.map(function(item){
		var obj = getObj(item);
		return '
' + e(item) + '
' +
				'

' + e(makeLabel(obj)) + '

';
	}).join("") +
	"
";

	dohx.add({
		name: "Widget Information",
		tests: [
			{
				id: 1,
				instructions: infoHTML,
				name: "Widget Information",
				test: function(){
					console.log(arguments)
				}
			}
		]
	});

}());
<|endoftext|>