<commit_before>import React from 'react';
import {Dimensions, StyleSheet, Text, View, TouchableHighlight, PanResponder} from 'react-native';
import {spring} from 'navigation-react-native';

export default class Next extends React.Component {
  constructor(props) {
    super(props);
    this.panResponder = PanResponder.create({
      onMoveShouldSetPanResponder: this.handleMoveShouldSetPanResponder.bind(this),
      onPanResponderMove: this.handlePanResponderMove.bind(this),
      onPanResponderRelease: this.handlePanResponderEnd.bind(this),
      onPanResponderTerminate: this.handlePanResponderEnd.bind(this),
    });
  }
  handleMoveShouldSetPanResponder(e, gestureState) {
    var {stateNavigator} = this.props;
    return stateNavigator.canNavigateBack(1);
  }
  handlePanResponderMove(e, gestureState) {
    var {moveScene} = this.props;
    moveScene({translate: spring(Math.max(0, gestureState.dx) / Dimensions.get('window').width)});
  }
  handlePanResponderEnd(e, gestureState) {
    var {moveScene, stateNavigator} = this.props;
    if (gestureState.dx / Dimensions.get('window').width < 0.5)
      moveScene(null);
    else
      stateNavigator.navigateBack(1);
  }
  render() {
    const {stateNavigator} = this.props;
    const {url, crumbs} = stateNavigator.stateContext;
    return (
      <View
        style={[
          {backgroundColor: crumbs.length % 2 === 0 ? '#036' : '#f36207'},
          styles.scene
        ]}
        {...this.panResponder.panHandlers}
        >
        <TouchableHighlight
          underlayColor="transparent"
          onPress={() => {
            if (url === stateNavigator.stateContext.url)
              stateNavigator.navigate('scene');
        }}>
          <Text style={styles.text}>Scene {crumbs.length}</Text>
        </TouchableHighlight>
      </View>
    )
  }
};

const styles = StyleSheet.create({
  scene: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  text: {
    fontSize: 40,
    color: '#fff',
    fontWeight: 'bold',
  },
});
<commit_msg>Reduced swipe hit size<commit_after>import React from 'react';
import {Dimensions, StyleSheet, Text, View, TouchableHighlight, PanResponder} from 'react-native';
import {spring} from 'navigation-react-native';

export default class Next extends React.Component {
  constructor(props) {
    super(props);
    this.panResponder = PanResponder.create({
      onMoveShouldSetPanResponder: this.handleMoveShouldSetPanResponder.bind(this),
      onPanResponderMove: this.handlePanResponderMove.bind(this),
      onPanResponderRelease: this.handlePanResponderEnd.bind(this),
      onPanResponderTerminate: this.handlePanResponderEnd.bind(this),
    });
  }
  handleMoveShouldSetPanResponder(e, gestureState) {
    var {stateNavigator} = this.props;
    return stateNavigator.canNavigateBack(1);
  }
  handlePanResponderMove(e, gestureState) {
    var {moveScene} = this.props;
    moveScene({translate: spring(Math.max(0, gestureState.dx) / Dimensions.get('window').width)});
  }
  handlePanResponderEnd(e, gestureState) {
    var {moveScene, stateNavigator} = this.props;
    if (gestureState.dx / Dimensions.get('window').width < 0.4)
      moveScene(null);
    else
      stateNavigator.navigateBack(1);
  }
  render() {
    const {stateNavigator} = this.props;
    const {url, crumbs} = stateNavigator.stateContext;
    return (
      <View
        style={[
          {backgroundColor: crumbs.length % 2 === 0 ? '#036' : '#f36207'},
          styles.scene
        ]}
        {...this.panResponder.panHandlers}
        >
        <TouchableHighlight
          underlayColor="transparent"
          onPress={() => {
            if (url === stateNavigator.stateContext.url)
              stateNavigator.navigate('scene');
        }}>
          <Text style={styles.text}>Scene {crumbs.length}</Text>
        </TouchableHighlight>
      </View>
    )
  }
};

const styles = StyleSheet.create({
  scene: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  text: {
    fontSize: 40,
    color: '#fff',
    fontWeight: 'bold',
  },
});
<|endoftext|>


<commit_before>// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('views/text_field') ;

/**
  @class

  The inline text editor is used to display an editable area for controls
  that are not always editable such as label views and source list views.

  You generally will not use the inline editor directly but instead will
  invoke beginEditing() and endEditing() on the views yous are
  editing. If you would like to use the inline editor for your own views,
  you can do that also by using the editing API described here.

  h2. Using the Inline Editor in Your Own Views

  If you need to use the inline editor with custom views you have written,
  you will need to work with the class methods to begin, commit, and discard
  editing changes.

  h3. Starting the Editor

  The inline editor works by positioning itself over the top of your view
  with the same offset, width, and font information.  As the user types, the
  field will automatically resize vertically to make room for the user's text.

  To activate the inline editor you must call beginEdition() with at least
  the target view you want the editor to position itself on top of:

  {{{
    SC.InlineTextFieldView.beginEditing({
      target: view, validator: validator
    }) ;
  }}}

  You can pass a variety of options to this method to configure the inline
  editor behavior, including:

  - *frame* The editors initial frame in viewport coordinates (REQ)
  - *exampleElement* A DOM element to use when copying styles.
  - *delegate* Optional delegate to receive update notices.  If not passed, the target view will be treated as the delegate. (REQ)
  - *value* The initial value of the edit field.  If not passed, the value property of the target view will be used instead.
  - *multiline* If YES then the hitting return will add to the value instead of exiting the inline editor.
  - *selectedRange* The range of text that should be selected.  If omitted, then the insertion point will be placed at the end of the value.
  - *commitOnBlur* If YES then bluring will commit the value, otherwise it will discard the current value.  Defaults to YES.
  - *validator* Optional validator will be attached to the field.

  If the inline editor is currently in use elsewhere, it will automatically
  close itself over there and begin editing for your view instead.  The
  editor expects your source view to implement the InlineTextFieldViewDelegate
  protocol.

  h2. Commiting or Discarding Changes

  Normally the editor will automatically commit or discard its changes
  whenever the user exits the edit mode.  If you need to force the editor to
  end editing, you can do so by calling commitEditing() or discardEditing():

  {{{
    SC.InlineTextFieldView.commitEditing();
    SC.InlineTextFieldView.discardEditing();
  }}}

  Both methods will try to end the editing context and will call the
  relevent delegate methods on the delegate you passed to beginEditing().

  Note that it is possible an editor may not be able to commit editing
  changes because either the delegate disallowed it or because its validator
  failed.  In this case commitEditing() will return NO.  If you want to
  end editing anyway, you can discard the editing changes instead by calling
  discardEditing().  This method will generally suceed unless your delegate
  refuses it as well.

  @extends SC.View
  @extends SC.DelegateSupport
  @extends SC.InlineEditorDelegate
  @since SproutCore 1.0
*/
SC.InlineTextFieldView = SC.TextFieldView.extend(SC.DelegateSupport, SC.InlineEditorDelegate,
/** @scope SC.InlineTextFieldView.prototype */ {

  /**
    Invoked by the class method to begin editing on an inline editor.

    You generally should call the class method beginEditing() instead of
    this one since it will make sure to create and use the shared editor
    instance.

    @params options {Hash} hash of options for editing
    @returns {Boolean} YES if editor began editing, NO if it failed.
  */
  beginEditing: function(options) {
    var layout={}, pane;
    // end existing editing if necessary
    this.beginPropertyChanges();
    if (this.get('isEditing') && !this.blurEditor()) {
      this.endPropertyChanges();  return NO ;
    }

    this._optframe = options.frame ;
    this._optIsCollection = options.isCollection;
    this._exampleElement = options.exampleElement ;
    this._delegate = options.delegate ;

    if (!this._optframe || !this._delegate) {
      throw "At least frame and delegate options are required for inline editor";
    }

    this._originalValue = options.value || '' ;
    this._multiline = (options.multiline !== undefined) ? options.multiline : NO ;
    this._commitOnBlur =  (options.commitOnBlur !== undefined) ? options.commitOnBlur : YES ;

    // set field values
    this.set('validator', options.validator) ;
    this.set('value', this._originalValue) ;
    //this.set('selectedRange', options.selectedRange || { start: this._originalValue.length, length: 0 }) ;

    this.set('isEditing', YES) ;

    // add to window.

    pane = this._delegate.pane();

    layout.height = this._optframe.height;
    layout.width=this._optframe.width;
    if(this._optIsCollection && this._delegate.get('layout').left){
      layout.left=this._optframe.x-this._delegate.get('layout').left-pane.$()[0].offsetLeft;
    }else{
      layout.left=this._optframe.x-pane.$()[0].offsetLeft;
    }
    if(this._optIsCollection && this._delegate.get('layout').top){
      layout.top=this._optframe.y-this._delegate.get('layout').top-pane.$()[0].offsetTop;
    }else{
      layout.top=this._optframe.y-pane.$()[0].offsetTop;
    }

    this.set('layout', layout);

    this.set('parentNode', pane);
    // get style for view.

    pane.appendChild(this);

    SC.RunLoop.begin().end();

    var del = this._delegate ;

    this._className = this.getDelegateProperty(del,"inlineEditorClassName");
    if(this._className && !this.hasClassName(this._className)) {
        this.setClassName(this._className,true);
      }

    this.invokeDelegateMethod(del, 'inlineEditorWillBeginEditing', this) ;
    // this.resizeToFit(this.getFieldValue()) ;

    // allow notifications to go
    this.endPropertyChanges() ;

    // and become first responder
    this.becomeFirstResponder() ;

    this.invokeDelegateMethod(del, 'inlineEditorDidBeginEditing', this) ;
  },

  /**
    Tries to commit the current value of the field and end editing.

    Do not use this method, use the class method instead.
  */
  commitEditing: function() {
    // try to validate field.  If it fails, return false.
    if (!SC.$ok(this.validateSubmit())) return NO ;
    return this._endEditing(this.get('value')) ;
  },

  /**
    Tries to discard the current value of the field and end editing.

    Do not use this method, use the class method instead.
  */
  discardEditing: function() {
    return this._endEditing(this._originalValue) ;
  },

  /**
    Invoked whenever the editor loses (or should lose) first responder
    status to commit or discard editing.
  */
  blurEditor: function() {
    if (!this.get('isEditing')) return YES ;
    return this._commitOnBlur ? this.commitEditing() : this.discardEditing();
  },

  /** @private
    Called by commitEditing and discardEditing to actually end editing.

    @returns {Boolean} NO if editing did not exit
  */
  _endEditing: function(finalValue) {
    if (!this.get('isEditing')) return YES ;

    // get permission from the delegate.
    var del = this._delegate ;
    if (!this.invokeDelegateMethod(del, 'inlineEditorShouldEndEditing', this, finalValue)) return NO ;

    // OK, we are allowed to end editing.  Notify delegate of final value
    // and clean up.
    this.invokeDelegateMethod(del, 'inlineEditorDidEndEditing', this, finalValue) ;

    // If the delegate set a class name, let's clean it up:
    if(this._className) this.setClassName(this._className, false);

    // cleanup cached values
    this._originalValue = this._delegate = this._exampleElement =  this._optframe = this._className = null ;
    this.set('isEditing', NO) ;

    // resign first responder if not done already.  This may call us in a
    // loop but since isEditing is already NO, nothing will happen.
    if (this.get('isFirstResponder')) this.resignFirstResponder();
    if (this.get('parentNode')) this.removeFromParent() ;

    return YES ;
  },

  /**
    YES if the editor is currently visible and editing.

    @type {Boolean}
  */
  isEditing: NO,

  /**
    Collects the appropriate style information from the targetView to
    make the inline editor appear similar.
  */

  /**
    Resizes the visible textarea to fix the actual text in the text area.

    This method works by keeping a div positioned immediately beneath the
    text area with an opacity of 0 that contains the same text as the
    input text field itself.  This is then used to calculate the required
    size for the text area.
  */
  resizeToFit: function(newValue)
  {

    // TODO: make this function work for 1.0

    // var sizer  = this.outlet('sizer');
    //     var field  = this.outlet('field');
    //
    //     // XSS attack waiting to happen... escape the form input;
    //     var text = (newValue || '').escapeHTML();
    //
    //     // convert the textarea's newlines into something comparable for the sizer
    //     // div appending a space to give a line with no text a visible height.
    //     text = text.replace((/ {2}/g), "  ").replace(/\n/g, "<br /> ");
    //
    //     // get the text size
    //     sizer.set('innerHTML', text || " ");
    //     sizer.recacheFrames() ;
    //     var h = sizer.get('frame').height;
    //     this.set('frame', { height: h }) ;
  },

  mouseDown: function(e) {
    arguments.callee.base.call(this, e) ;
    return this.get('isEditing');
  },

  keyDown: function(evt) {
    var ret = this.interpretKeyEvents(evt) ;
    if(!ret) this.fieldValueDidChange(true);
    return !ret ? NO : ret ;
  },

  insertText: null,

  //keyUp: function() { return true; },

  // [Safari] if you don't take key focus away from an element before you
  // remove it from the DOM key events are no longer sent to the browser.
  willRemoveFromParent: function() {
    this.$('input')[0].blur();
  },

  // ask owner to end editing.
  willLoseFirstResponder: function(responder) {
    if (responder !== this) return;

    // should have been covered by willRemoveFromParent, but this was needed
    // too.
    this.$('input')[0].blur();
    return this.blurEditor() ;
  },

  // invoked when the user presses escape.  Returns true to ignore
  // keystroke
  cancel: function() {
    this.discardEditing();
    return YES;
  },

  // do it here instead of waiting on the binding to make sure the UI
  // updates immediately.
  fieldValueDidChange: function(partialChange) {
    arguments.callee.base.call(this, partialChange) ;
    //this.resizeToFit(this.getFieldValue()) ;
  },

  // invoked when the user presses return.  If this is a multi-line field,
  // then allow the newine to proceed.  Otherwise, try to commit the
  // edit.
  insertNewline: function(evt) {
    if (this._multiline) {
      return arguments.callee.base.call(this, evt) ;
    } else {
      // TODO : this is a work around. There is a bug where the
      // last character would get dropped
      // if the editing was completed by pressing return
      // needs to be fixed
      if (this.get('value') != this.$input().val()) {
        this.set('value', this.$input().val());
      }

      this.commitEditing() ;
      return YES ;
    }
  },

  // Tries to find the next key view when tabbing.  If the next view is
  // editable, begins editing.

  insertTab: function(evt)
  {
    var next = this.get("owner")._delegate.nextValidKeyView();
    this.commitEditing() ;
    if(next) next.beginEditing();
    return YES ;
  },

  insertBacktab: function(evt)
  {
    var prev = this.get("owner")._delegate.previousValidKeyView();
    this.commitEditing() ;
    if(prev) prev.beginEditing();
    return YES ;
  }

});

SC.InlineTextFieldView.mixin(
/** @static SC.InlineTextFieldView */ {
  /** Call this method to make the inline editor begin editing for your view.

      If the inline editor is already being used for another value it will
      try to dismiss itself from the other editor and attach itself to the
      new view instead.  If this process fails for some reason (for example
      if the other view did not allow the view to end editing) then this
      method will return false.

      You should pass a set of options that at least includes the target
      view.  See class definition for options.

      @params options {Hash} hash of options for editing
      @returns {Boolean} YES if editor began editing, NO if it failed.
  */
  beginEditing: function(options) {
    this._exampleElement = options.exampleElement ;

    // If exampleInlineTextFieldView is set, load this class otherwise use
    // the default, this.
    var klass = (options.exampleInlineTextFieldView)
              ? options.exampleInlineTextFieldView : this;

    var layout = options.delegate.get('layout');
    var s = this.updateViewStyle();

    var str= ".inline-editor input{"+s+"} ";
    str= str+".inline-editor textarea{"+s+"}";
    var pa= document.getElementsByTagName('head')[0] ;
    var el= document.createElement('style');
    el.type= 'text/css';
    el.media= 'screen';
    if(el.styleSheet) el.styleSheet.cssText= str;// IE method
    else el.appendChild(document.createTextNode(str));// others
    pa.appendChild(el);

    this.editor = klass.create({ classNames: 'inline-editor', layout: layout}) ;
    return this.editor.beginEditing(options) ;

  },

  /** Save the current value of the inline editor and exit edit mode.

    If the inline editor is being used it will try to end the editing and
    close.  If the inline editor could not end for some reason (for example
    if the delegate did not allow the editing to end) then this method will
    return NO.

    @returns {Boolean} YES if the inline editor ended or no edit was in
      progress.
  */
  commitEditing: function() {
    return this.editor ? this.editor.commitEditing() : YES ;
  },

  /** Discard the current value of the inline editor and exit edit mode.

    If the inline editor is in use, this method will try to end the editing,
    restoring the original value of the target view.  If the inline editor
    could not end for some reason (for example if the delegate did not
    allow editing to end) then this method will return NO.

    @returns {Boolean} YES if the inline editor ended or no edit was in progress.
  */
  discardEditing: function() {
    return this.editor ? this.editor.discardEditing() : YES ;
  },

  updateViewStyle: function() {
    var el = this._exampleElement[0] ;
    var styles = '';
    var s=SC.getStyle(el,'font-size');
    if(s && s.length>0) styles = styles + "font-size: "+ s + "; ";
    s=SC.getStyle(el,'font-family');
    if(s && s.length>0) styles = styles + "font-family: " + s + "; ";
    s=SC.getStyle(el,'font-weight');
    if(s && s.length>0) styles = styles + "font-weight: " + s + "; ";
    s=SC.getStyle(el,'z-index');
    if(s && s.length>0) styles = styles + "z-index: " + s + "; ";
    s=SC.getStyle(el,'padding-left');
    if(s && s.length>0) styles = styles + "padding-left: " + s + "; ";
    s=SC.getStyle(el,'padding-bottom');
    if(s && s.length>0) styles = styles + "padding-bottom: " + s + "; ";
    s=SC.getStyle(el,'line-height');
    if(s && s.length>0) styles = styles + "line-height: " + s + "; ";
    s=SC.getStyle(el,'text-align');
    if(s && s.length>0) styles = styles + "text-align: " + s + "; ";

    return styles;
  },

  /**
    The current shared inline editor.  This property will often remain NULL
    until you actually begin editing for the first time.

    @type {SC.InlineTextFieldView}
  */
  editor: null

}) ;<commit_msg>insertTab and insertBacktab called owner when it is not needed.<commit_after>// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('views/text_field') ;

/**
  @class

  The inline text editor is used to display an editable area for controls
  that are not always editable such as label views and source list views.

  You generally will not use the inline editor directly but instead will
  invoke beginEditing() and endEditing() on the views yous are
  editing. If you would like to use the inline editor for your own views,
  you can do that also by using the editing API described here.

  h2. Using the Inline Editor in Your Own Views

  If you need to use the inline editor with custom views you have written,
  you will need to work with the class methods to begin, commit, and discard
  editing changes.

  h3. Starting the Editor

  The inline editor works by positioning itself over the top of your view
  with the same offset, width, and font information.  As the user types, the
  field will automatically resize vertically to make room for the user's text.

  To activate the inline editor you must call beginEdition() with at least
  the target view you want the editor to position itself on top of:

  {{{
    SC.InlineTextFieldView.beginEditing({
      target: view, validator: validator
    }) ;
  }}}

  You can pass a variety of options to this method to configure the inline
  editor behavior, including:

  - *frame* The editors initial frame in viewport coordinates (REQ)
  - *exampleElement* A DOM element to use when copying styles.
  - *delegate* Optional delegate to receive update notices.  If not passed, the target view will be treated as the delegate. (REQ)
  - *value* The initial value of the edit field.  If not passed, the value property of the target view will be used instead.
  - *multiline* If YES then the hitting return will add to the value instead of exiting the inline editor.
  - *selectedRange* The range of text that should be selected.  If omitted, then the insertion point will be placed at the end of the value.
  - *commitOnBlur* If YES then bluring will commit the value, otherwise it will discard the current value.  Defaults to YES.
  - *validator* Optional validator will be attached to the field.

  If the inline editor is currently in use elsewhere, it will automatically
  close itself over there and begin editing for your view instead.  The
  editor expects your source view to implement the InlineTextFieldViewDelegate
  protocol.

  h2. Commiting or Discarding Changes

  Normally the editor will automatically commit or discard its changes
  whenever the user exits the edit mode.  If you need to force the editor to
  end editing, you can do so by calling commitEditing() or discardEditing():

  {{{
    SC.InlineTextFieldView.commitEditing();
    SC.InlineTextFieldView.discardEditing();
  }}}

  Both methods will try to end the editing context and will call the
  relevent delegate methods on the delegate you passed to beginEditing().

  Note that it is possible an editor may not be able to commit editing
  changes because either the delegate disallowed it or because its validator
  failed.  In this case commitEditing() will return NO.  If you want to
  end editing anyway, you can discard the editing changes instead by calling
  discardEditing().  This method will generally suceed unless your delegate
  refuses it as well.

  @extends SC.View
  @extends SC.DelegateSupport
  @extends SC.InlineEditorDelegate
  @since SproutCore 1.0
*/
SC.InlineTextFieldView = SC.TextFieldView.extend(SC.DelegateSupport, SC.InlineEditorDelegate,
/** @scope SC.InlineTextFieldView.prototype */ {

  /**
    Invoked by the class method to begin editing on an inline editor.

    You generally should call the class method beginEditing() instead of
    this one since it will make sure to create and use the shared editor
    instance.

    @params options {Hash} hash of options for editing
    @returns {Boolean} YES if editor began editing, NO if it failed.
  */
  beginEditing: function(options) {
    var layout={}, pane;
    // end existing editing if necessary
    this.beginPropertyChanges();
    if (this.get('isEditing') && !this.blurEditor()) {
      this.endPropertyChanges();  return NO ;
    }

    this._optframe = options.frame ;
    this._optIsCollection = options.isCollection;
    this._exampleElement = options.exampleElement ;
    this._delegate = options.delegate ;

    if (!this._optframe || !this._delegate) {
      throw "At least frame and delegate options are required for inline editor";
    }

    this._originalValue = options.value || '' ;
    this._multiline = (options.multiline !== undefined) ? options.multiline : NO ;
    this._commitOnBlur =  (options.commitOnBlur !== undefined) ? options.commitOnBlur : YES ;

    // set field values
    this.set('validator', options.validator) ;
    this.set('value', this._originalValue) ;
    //this.set('selectedRange', options.selectedRange || { start: this._originalValue.length, length: 0 }) ;

    this.set('isEditing', YES) ;

    // add to window.

    pane = this._delegate.pane();

    layout.height = this._optframe.height;
    layout.width=this._optframe.width;
    if(this._optIsCollection && this._delegate.get('layout').left){
      layout.left=this._optframe.x-this._delegate.get('layout').left-pane.$()[0].offsetLeft;
    }else{
      layout.left=this._optframe.x-pane.$()[0].offsetLeft;
    }
    if(this._optIsCollection && this._delegate.get('layout').top){
      layout.top=this._optframe.y-this._delegate.get('layout').top-pane.$()[0].offsetTop;
    }else{
      layout.top=this._optframe.y-pane.$()[0].offsetTop;
    }

    this.set('layout', layout);

    this.set('parentNode', pane);
    // get style for view.

    pane.appendChild(this);

    SC.RunLoop.begin().end();

    var del = this._delegate ;

    this._className = this.getDelegateProperty(del,"inlineEditorClassName");
    if(this._className && !this.hasClassName(this._className)) {
        this.setClassName(this._className,true);
      }

    this.invokeDelegateMethod(del, 'inlineEditorWillBeginEditing', this) ;
    // this.resizeToFit(this.getFieldValue()) ;

    // allow notifications to go
    this.endPropertyChanges() ;

    // and become first responder
    this.becomeFirstResponder() ;

    this.invokeDelegateMethod(del, 'inlineEditorDidBeginEditing', this) ;
  },

  /**
    Tries to commit the current value of the field and end editing.

    Do not use this method, use the class method instead.
  */
  commitEditing: function() {
    // try to validate field.  If it fails, return false.
    if (!SC.$ok(this.validateSubmit())) return NO ;
    return this._endEditing(this.get('value')) ;
  },

  /**
    Tries to discard the current value of the field and end editing.

    Do not use this method, use the class method instead.
  */
  discardEditing: function() {
    return this._endEditing(this._originalValue) ;
  },

  /**
    Invoked whenever the editor loses (or should lose) first responder
    status to commit or discard editing.
  */
  blurEditor: function() {
    if (!this.get('isEditing')) return YES ;
    return this._commitOnBlur ? this.commitEditing() : this.discardEditing();
  },

  /** @private
    Called by commitEditing and discardEditing to actually end editing.

    @returns {Boolean} NO if editing did not exit
  */
  _endEditing: function(finalValue) {
    if (!this.get('isEditing')) return YES ;

    // get permission from the delegate.
    var del = this._delegate ;
    if (!this.invokeDelegateMethod(del, 'inlineEditorShouldEndEditing', this, finalValue)) return NO ;

    // OK, we are allowed to end editing.  Notify delegate of final value
    // and clean up.
    this.invokeDelegateMethod(del, 'inlineEditorDidEndEditing', this, finalValue) ;

    // If the delegate set a class name, let's clean it up:
    if(this._className) this.setClassName(this._className, false);

    // cleanup cached values
    this._originalValue = this._delegate = this._exampleElement =  this._optframe = this._className = null ;
    this.set('isEditing', NO) ;

    // resign first responder if not done already.  This may call us in a
    // loop but since isEditing is already NO, nothing will happen.
    if (this.get('isFirstResponder')) this.resignFirstResponder();
    if (this.get('parentNode')) this.removeFromParent() ;

    return YES ;
  },

  /**
    YES if the editor is currently visible and editing.

    @type {Boolean}
  */
  isEditing: NO,

  /**
    Collects the appropriate style information from the targetView to
    make the inline editor appear similar.
  */

  /**
    Resizes the visible textarea to fix the actual text in the text area.

    This method works by keeping a div positioned immediately beneath the
    text area with an opacity of 0 that contains the same text as the
    input text field itself.  This is then used to calculate the required
    size for the text area.
  */
  resizeToFit: function(newValue)
  {

    // TODO: make this function work for 1.0

    // var sizer  = this.outlet('sizer');
    //     var field  = this.outlet('field');
    //
    //     // XSS attack waiting to happen... escape the form input;
    //     var text = (newValue || '').escapeHTML();
    //
    //     // convert the textarea's newlines into something comparable for the sizer
    //     // div appending a space to give a line with no text a visible height.
    //     text = text.replace((/ {2}/g), "  ").replace(/\n/g, "<br /> ");
    //
    //     // get the text size
    //     sizer.set('innerHTML', text || " ");
    //     sizer.recacheFrames() ;
    //     var h = sizer.get('frame').height;
    //     this.set('frame', { height: h }) ;
  },

  mouseDown: function(e) {
    arguments.callee.base.call(this, e) ;
    return this.get('isEditing');
  },

  keyDown: function(evt) {
    var ret = this.interpretKeyEvents(evt) ;
    if(!ret) this.fieldValueDidChange(true);
    return !ret ? NO : ret ;
  },

  insertText: null,

  //keyUp: function() { return true; },

  // [Safari] if you don't take key focus away from an element before you
  // remove it from the DOM key events are no longer sent to the browser.
  willRemoveFromParent: function() {
    this.$('input')[0].blur();
  },

  // ask owner to end editing.
  willLoseFirstResponder: function(responder) {
    if (responder !== this) return;

    // should have been covered by willRemoveFromParent, but this was needed
    // too.
    this.$('input')[0].blur();
    return this.blurEditor() ;
  },

  // invoked when the user presses escape.  Returns true to ignore
  // keystroke
  cancel: function() {
    this.discardEditing();
    return YES;
  },

  // do it here instead of waiting on the binding to make sure the UI
  // updates immediately.
  fieldValueDidChange: function(partialChange) {
    arguments.callee.base.call(this, partialChange) ;
    //this.resizeToFit(this.getFieldValue()) ;
  },

  // invoked when the user presses return.  If this is a multi-line field,
  // then allow the newine to proceed.  Otherwise, try to commit the
  // edit.
  insertNewline: function(evt) {
    if (this._multiline) {
      return arguments.callee.base.call(this, evt) ;
    } else {
      // TODO : this is a work around. There is a bug where the
      // last character would get dropped
      // if the editing was completed by pressing return
      // needs to be fixed
      if (this.get('value') != this.$input().val()) {
        this.set('value', this.$input().val());
      }

      this.commitEditing() ;
      return YES ;
    }
  },

  // Tries to find the next key view when tabbing.  If the next view is
  // editable, begins editing.

  insertTab: function(evt)
  {
    var next = this._delegate.nextValidKeyView();
    this.commitEditing() ;
    if(next) next.beginEditing();
    return YES ;
  },

  insertBacktab: function(evt)
  {
    var prev = this._delegate.previousValidKeyView();
    this.commitEditing() ;
    if(prev) prev.beginEditing();
    return YES ;
  }

});

SC.InlineTextFieldView.mixin(
/** @static SC.InlineTextFieldView */ {
  /** Call this method to make the inline editor begin editing for your view.

      If the inline editor is already being used for another value it will
      try to dismiss itself from the other editor and attach itself to the
      new view instead.  If this process fails for some reason (for example
      if the other view did not allow the view to end editing) then this
      method will return false.

      You should pass a set of options that at least includes the target
      view.  See class definition for options.

      @params options {Hash} hash of options for editing
      @returns {Boolean} YES if editor began editing, NO if it failed.
  */
  beginEditing: function(options) {
    this._exampleElement = options.exampleElement ;

    // If exampleInlineTextFieldView is set, load this class otherwise use
    // the default, this.
    var klass = (options.exampleInlineTextFieldView)
              ? options.exampleInlineTextFieldView : this;

    var layout = options.delegate.get('layout');
    var s = this.updateViewStyle();

    var str= ".inline-editor input{"+s+"} ";
    str= str+".inline-editor textarea{"+s+"}";
    var pa= document.getElementsByTagName('head')[0] ;
    var el= document.createElement('style');
    el.type= 'text/css';
    el.media= 'screen';
    if(el.styleSheet) el.styleSheet.cssText= str;// IE method
    else el.appendChild(document.createTextNode(str));// others
    pa.appendChild(el);

    this.editor = klass.create({ classNames: 'inline-editor', layout: layout}) ;
    return this.editor.beginEditing(options) ;

  },

  /** Save the current value of the inline editor and exit edit mode.

    If the inline editor is being used it will try to end the editing and
    close.  If the inline editor could not end for some reason (for example
    if the delegate did not allow the editing to end) then this method will
    return NO.

    @returns {Boolean} YES if the inline editor ended or no edit was in
      progress.
  */
  commitEditing: function() {
    return this.editor ? this.editor.commitEditing() : YES ;
  },

  /** Discard the current value of the inline editor and exit edit mode.

    If the inline editor is in use, this method will try to end the editing,
    restoring the original value of the target view.  If the inline editor
    could not end for some reason (for example if the delegate did not
    allow editing to end) then this method will return NO.

    @returns {Boolean} YES if the inline editor ended or no edit was in progress.
  */
  discardEditing: function() {
    return this.editor ? this.editor.discardEditing() : YES ;
  },

  updateViewStyle: function() {
    var el = this._exampleElement[0] ;
    var styles = '';
    var s=SC.getStyle(el,'font-size');
    if(s && s.length>0) styles = styles + "font-size: "+ s + "; ";
    s=SC.getStyle(el,'font-family');
    if(s && s.length>0) styles = styles + "font-family: " + s + "; ";
    s=SC.getStyle(el,'font-weight');
    if(s && s.length>0) styles = styles + "font-weight: " + s + "; ";
    s=SC.getStyle(el,'z-index');
    if(s && s.length>0) styles = styles + "z-index: " + s + "; ";
    s=SC.getStyle(el,'padding-left');
    if(s && s.length>0) styles = styles + "padding-left: " + s + "; ";
    s=SC.getStyle(el,'padding-bottom');
    if(s && s.length>0) styles = styles + "padding-bottom: " + s + "; ";
    s=SC.getStyle(el,'line-height');
    if(s && s.length>0) styles = styles + "line-height: " + s + "; ";
    s=SC.getStyle(el,'text-align');
    if(s && s.length>0) styles = styles + "text-align: " + s + "; ";

    return styles;
  },

  /**
    The current shared inline editor.  This property will often remain NULL
    until you actually begin editing for the first time.

    @type {SC.InlineTextFieldView}
  */
  editor: null

}) ;<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { inject } from 'mobx-react';
import { connect } from 'react-redux';
import { compose } from 'recompose';
import styled from 'react-emotion';
import Transition from 'react-transition-group/Transition';
import IconClose from 'react-icons/lib/md/close';
import { dep } from 'worona-deps';
import * as actions from '../../actions';
import * as selectorCreators from '../../selectorCreators';

class Sticky extends Component {
  static propTypes = {
    Ad: PropTypes.func.isRequired,
    position: PropTypes.string,
    delay: PropTypes.number,
    duration: PropTypes.number,
    rememberClosedByUser: PropTypes.bool,
    format: PropTypes.shape({}),
    type: PropTypes.string.isRequired,
    id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
    isOpen: PropTypes.bool.isRequired,
    timeout: PropTypes.number,
    closedByUser: PropTypes.bool.isRequired,
    stickyHasShown: PropTypes.func.isRequired,
    stickyHasHidden: PropTypes.func.isRequired,
    stickyUpdateTimeout: PropTypes.func.isRequired,
  };

  static defaultProps = {
    timeout: null,
    format: null,
    position: 'bottom',
    delay: 0,
    duration: 0,
    rememberClosedByUser: false,
  };

  constructor() {
    super();

    this.handleShow = this.handleShow.bind(this);
    this.handleClick = this.handleClick.bind(this);

    this.state = {
      shouldMount: true,
    };
  }

  componentDidMount() {
    this.handleShow();
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.type !== nextProps.type || this.props.id !== nextProps.id) {
      this.handleShow();

      this.setState({ shouldMount: false }, () => {
        this.setState({ shouldMount: true });
      });
    }
  }

  handleShow() {
    const {
      isOpen,
      timeout,
      closedByUser,
      format,
      delay,
      duration,
      rememberClosedByUser,
      stickyHasShown,
      stickyHasHidden,
      stickyUpdateTimeout,
    } = this.props;

    if (closedByUser && rememberClosedByUser) return;

    if (timeout) clearTimeout(timeout);

    if (isOpen) {
      if (!format) {
        stickyHasHidden({ closedByUser: false });
      } else if (duration) {
        const newTimeout = setTimeout(() => {
          stickyHasHidden({ closedByUser: false });
        }, duration);
        stickyUpdateTimeout({ timeout: newTimeout });
      }
    } else if (format) {
      setTimeout(() => {
        if (duration) {
          const newTimeout = setTimeout(() => {
            stickyHasHidden({ closedByUser: false });
          }, duration);
          stickyHasShown({ timeout: newTimeout });
        } else {
          stickyHasShown({ timeout: null });
        }
      }, delay);
    }
  }

  handleClick() {
    const { timeout, stickyHasHidden } = this.props;

    if (timeout) clearTimeout(timeout);

    stickyHasHidden({ closedByUser: true });
  }

  render() {
    const { isOpen, position, format, Ad } = this.props;
    const { shouldMount } = this.state;

    return (
      <Transition
        in={isOpen}
        timeout={150}
        mountOnEnter
        unmountOnExit
        onEnter={node => node.scrollTop}
      >
        {status => (
          <Container status={status} stickyHeight={format && format.height} position={position}>
            <CloseButton onClick={this.handleClick} position={position}>
              <IconClose size={20} verticalAlign="none" />
            </CloseButton>
            {format && shouldMount && <Ad active isSticky {...format} />}
          </Container>
        )}
      </Transition>
    );
  }
}

const mapStateToProps = (state, { type }) => {
  const { sticky } = selectorCreators.ads.getOptions(type)(state);

  return {
    Ad: dep('ads', 'components', 'Ad'),
    position: sticky && sticky.position,
    delay: sticky && sticky.delay,
    duration: sticky && sticky.duration,
    rememberClosedByUser: sticky && sticky.rememberClosedByUser,
    format: selectorCreators.ads.getStickyFormat(type)(state),
    isOpen: state.theme.sticky.isOpen,
    timeout: state.theme.sticky.timeout,
    closedByUser: state.theme.sticky.closedByUser,
  };
};

const mapDispatchToProps = dispatch => ({
  stickyHasShown: payload => dispatch(actions.sticky.hasShown(payload)),
  stickyHasHidden: payload => dispatch(actions.sticky.hasHidden(payload)),
  stickyUpdateTimeout: payload => dispatch(actions.sticky.updateTimeout(payload)),
});

export default compose(
  inject(({ connection }) => ({
    type: connection.selectedItem.type,
    id: connection.selectedItem.id,
  })),
  connect(mapStateToProps, mapDispatchToProps),
)(Sticky);

const Container = styled.div`
  box-sizing: border-box;
  position: fixed;
  ${({ position }) => (position === 'bottom' ? 'bottom: 0' : 'top: 0')};
  width: 100vw;
  height: auto;
  padding: 2px 0;
  background-color: ${({ theme }) => theme.colors.background};
  z-index: 2147483646;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  transform: ${({ status, position }) =>
    status.startsWith('enter')
      ? 'translateY(0%)'
      : `translateY(${position === 'bottom' ? '100%' : '-100%'})`};
  transition: transform 150ms ease-out;
  min-height: 50px;
`;

const CloseButton = styled.div`
  box-sizing: border-box;
  position: absolute;
  ${({ position }) => (position === 'bottom' ? 'top: -20px' : 'bottom: -20px')};
  padding-top: 3px;
  right: 5px;
  height: 20px;
  width: 40px;
  display: flex;
  justify-content: center;
  align-items: center;
  ${({ position }) =>
    position === 'bottom' ? 'border-top-left-radius: 20px' : 'border-bottom-left-radius: 20px'};
  ${({ position }) =>
    position === 'bottom' ? 'border-top-right-radius: 20px' : 'border-bottom-right-radius: 20px'};
  background-color: ${({ theme }) => theme.colors.background};
  color: ${({ theme }) => theme.colors.text};
`;
<commit_msg>Get some props from mobx stores<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { inject } from 'mobx-react';
import { connect } from 'react-redux';
import { compose } from 'recompose';
import styled from 'react-emotion';
import Transition from 'react-transition-group/Transition';
import IconClose from 'react-icons/lib/md/close';
import { dep } from 'worona-deps';
import * as actions from '../../actions';
import * as selectorCreators from '../../selectorCreators';

class Sticky extends Component {
  static propTypes = {
    Ad: PropTypes.func.isRequired,
    position: PropTypes.string,
    delay: PropTypes.number,
    duration: PropTypes.number,
    rememberClosedByUser: PropTypes.bool,
    format: PropTypes.shape({}),
    type: PropTypes.string.isRequired,
    id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
    isOpen: PropTypes.bool.isRequired,
    timeout: PropTypes.number,
    closedByUser: PropTypes.bool.isRequired,
    stickyHasShown: PropTypes.func.isRequired,
    stickyHasHidden: PropTypes.func.isRequired,
    stickyUpdateTimeout: PropTypes.func.isRequired,
  };

  static defaultProps = {
    timeout: null,
    format: null,
    position: 'bottom',
    delay: 0,
    duration: 0,
    rememberClosedByUser: false,
  };

  constructor() {
    super();

    this.handleShow = this.handleShow.bind(this);
    this.handleClick = this.handleClick.bind(this);

    this.state = {
      shouldMount: true,
    };
  }

  componentDidMount() {
    this.handleShow();
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.type !== nextProps.type || this.props.id !== nextProps.id) {
      this.handleShow();

      this.setState({ shouldMount: false }, () => {
        this.setState({ shouldMount: true });
      });
    }
  }

  handleShow() {
    const {
      isOpen,
      timeout,
      closedByUser,
      format,
      delay,
      duration,
      rememberClosedByUser,
      stickyHasShown,
      stickyHasHidden,
      stickyUpdateTimeout,
    } = this.props;

    if (closedByUser && rememberClosedByUser) return;

    if (timeout) clearTimeout(timeout);

    if (isOpen) {
      if (!format) {
        stickyHasHidden({ closedByUser: false });
      } else if (duration) {
        const newTimeout = setTimeout(() => {
          stickyHasHidden({ closedByUser: false });
        }, duration);
        stickyUpdateTimeout({ timeout: newTimeout });
      }
    } else if (format) {
      setTimeout(() => {
        if (duration) {
          const newTimeout = setTimeout(() => {
            stickyHasHidden({ closedByUser: false });
          }, duration);
          stickyHasShown({ timeout: newTimeout });
        } else {
          stickyHasShown({ timeout: null });
        }
      }, delay);
    }
  }

  handleClick() {
    const { timeout, stickyHasHidden } = this.props;

    if (timeout) clearTimeout(timeout);

    stickyHasHidden({ closedByUser: true });
  }

  render() {
    const { isOpen, position, format, Ad } = this.props;
    const { shouldMount } = this.state;

    return (
      <Transition
        in={isOpen}
        timeout={150}
        mountOnEnter
        unmountOnExit
        onEnter={node => node.scrollTop}
      >
        {status => (
          <Container status={status} stickyHeight={format && format.height} position={position}>
            <CloseButton onClick={this.handleClick} position={position}>
              <IconClose size={20} verticalAlign="none" />
            </CloseButton>
            {format && shouldMount && <Ad active isSticky {...format} />}
          </Container>
        )}
      </Transition>
    );
  }
}

const mapStateToProps = (state, { type }) => {
  const { sticky } = selectorCreators.ads.getOptions(type)(state);

  return {
    Ad: dep('ads', 'components', 'Ad'),
    position: sticky && sticky.position,
    delay: sticky && sticky.delay,
    duration: sticky && sticky.duration,
    rememberClosedByUser: sticky && sticky.rememberClosedByUser,
    format: selectorCreators.ads.getStickyFormat(type)(state),
  };
};

const mapDispatchToProps = dispatch => ({
  stickyHasShown: payload => dispatch(actions.sticky.hasShown(payload)),
  stickyHasHidden: payload => dispatch(actions.sticky.hasHidden(payload)),
  stickyUpdateTimeout: payload => dispatch(actions.sticky.updateTimeout(payload)),
});

export default compose(
  inject(({ connection, ads }) => ({
    type: connection.selectedItem.type,
    id: connection.selectedItem.id,
    isOpen: ads.sticky.isOpen,
    timeout: ads.sticky.timeout,
    closedByUser: ads.sticky.closedByUser,
  })),
  connect(mapStateToProps, mapDispatchToProps),
)(Sticky);

const Container = styled.div`
  box-sizing: border-box;
  position: fixed;
  ${({ position }) => (position === 'bottom' ? 'bottom: 0' : 'top: 0')};
  width: 100vw;
  height: auto;
  padding: 2px 0;
  background-color: ${({ theme }) => theme.colors.background};
  z-index: 2147483646;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  transform: ${({ status, position }) =>
    status.startsWith('enter')
      ? 'translateY(0%)'
      : `translateY(${position === 'bottom' ? '100%' : '-100%'})`};
  transition: transform 150ms ease-out;
  min-height: 50px;
`;

const CloseButton = styled.div`
  box-sizing: border-box;
  position: absolute;
  ${({ position }) => (position === 'bottom' ? 'top: -20px' : 'bottom: -20px')};
  padding-top: 3px;
  right: 5px;
  height: 20px;
  width: 40px;
  display: flex;
  justify-content: center;
  align-items: center;
  ${({ position }) =>
    position === 'bottom' ? 'border-top-left-radius: 20px' : 'border-bottom-left-radius: 20px'};
  ${({ position }) =>
    position === 'bottom' ? 'border-top-right-radius: 20px' : 'border-bottom-right-radius: 20px'};
  background-color: ${({ theme }) => theme.colors.background};
  color: ${({ theme }) => theme.colors.text};
`;
<|endoftext|>


<commit_before>var crypto = require('crypto');
var config = require('cloud/config/weixin.js');
var debug = require('debug')('AV:weixin');

exports.exec = function(params, cb) {
  if (params.signature) {
    checkSignature(params.signature, params.timestamp, params.nonce, params.echostr, cb);
  } else {
    receiveMessage(params, cb)
  }
}

// 验证签名
var checkSignature = function(signature, timestamp, nonce, echostr, cb) {
  var oriStr = [config.token, timestamp, nonce].sort().join('')
  var code = crypto.createHash('sha1').update(oriStr).digest('hex');
  debug('code:', code)
  if (code == signature) {
    cb(null, echostr);
  } else {
    var err = new Error('Unauthorized');
    err.code = 401;
    cb(err);
  }
}

// 接收普通消息
var receiveMessage = function(msg, cb) {

    //console.log('weixin Event:', msg.xml.Event);

    var content = '';
    if(msg.xml.MsgType == 'event')//操作事件
    {
        if (msg.xml.Event == 'CLICK')
        {
            if(msg.xml.EventKey == 'KEY_I_LIKE')
            {
                content = '谢谢点赞！'

            }else if(msg.xml.EventKey == 'KEY_USER_GUID')
            {
                content = '爆料方式简单、迅速，全程使用微信操作。\n\n第一步，拍摄实物照片（可以拍摄多张照片，至少1张，最多5张）\n\n第二步，发送当前门店所在的位置信息，以便准确前往。\n\n第三步，用简短的文字描述一下优惠内容。如（蛇口沃尔玛旁氏洗面奶满50减20活动，速来。）'

            }else if(msg.xml.EventKey == 'KEY_INPUT_TEXT')
            {
                content = '↙点击左下角的键盘图标可切换输入模式 :)';
            }

        }
    }else if(msg.xml.MsgType == 'image')
    {
        var fs = require('fs');
        var file = AV.File.withURL(msg.xml.FromUserName, msg.xml.PicUrl);
        file.save();

        content = '(1/3) 收到您的照片，如果有多个，请继续拍摄，最多不超过5张。发送当前门店地理位置进行下一步。';

    }else if(msg.xml.MsgType == 'text')
    {
        content = '(2/3) 爆料成功！你可以在菜单[我自己->我的爆料]查看，审核通过后即出现在优惠榜单上。';

    }else if(msg.xml.MsgType == 'location')
    {
        content = '(3/3) 请用简短的文字描述一下优惠内容。如（蛇口沃尔玛洗面奶满50减20活动，速来。）';
    }

  var result = {
    xml: {
      ToUserName: msg.xml.FromUserName[0],
      FromUserName: '' + msg.xml.ToUserName + '',
      CreateTime: new Date().getTime(),
      MsgType: 'text',
      Content: content
    }
  }
  cb(null, result);
}
<commit_msg>debug file<commit_after>var crypto = require('crypto');
var config = require('cloud/config/weixin.js');
var debug = require('debug')('AV:weixin');

exports.exec = function(params, cb) {
  if (params.signature) {
    checkSignature(params.signature, params.timestamp, params.nonce, params.echostr, cb);
  } else {
    receiveMessage(params, cb)
  }
}

// 验证签名
var checkSignature = function(signature, timestamp, nonce, echostr, cb) {
  var oriStr = [config.token, timestamp, nonce].sort().join('')
  var code = crypto.createHash('sha1').update(oriStr).digest('hex');
  debug('code:', code)
  if (code == signature) {
    cb(null, echostr);
  } else {
    var err = new Error('Unauthorized');
    err.code = 401;
    cb(err);
  }
}

// 接收普通消息
var receiveMessage = function(msg, cb) {

    //console.log('weixin Event:', msg.xml.Event);

    var content = '';
    if(msg.xml.MsgType == 'event')//操作事件
    {
        if (msg.xml.Event == 'CLICK')
        {
            if(msg.xml.EventKey == 'KEY_I_LIKE')
            {
                content = '谢谢点赞！'

            }else if(msg.xml.EventKey == 'KEY_USER_GUID')
            {
                content = '爆料方式简单、迅速，全程使用微信操作。\n\n第一步，拍摄实物照片（可以拍摄多张照片，至少1张，最多5张）\n\n第二步，发送当前门店所在的位置信息，以便准确前往。\n\n第三步，用简短的文字描述一下优惠内容。如（蛇口沃尔玛旁氏洗面奶满50减20活动，速来。）'

            }else if(msg.xml.EventKey == 'KEY_INPUT_TEXT')
            {
                content = '↙点击左下角的键盘图标可切换输入模式 :)';
            }

        }
    }else if(msg.xml.MsgType == 'image')
    {
        var fs = require('fs');
        console.log(msg.xml.FromUserName, msg.xml.PicUrl);
        var file = AV.File.withURL(msg.xml.FromUserName, msg.xml.PicUrl);
        console.log(file);
        file.save().then(function(result){console.log(result)},function(error){console.log(error)})

        content = '(1/3) 收到您的照片，如果有多个，请继续拍摄，最多不超过5张。发送当前门店地理位置进行下一步。';

    }else if(msg.xml.MsgType == 'text')
    {
        content = '(2/3) 爆料成功！你可以在菜单[我自己->我的爆料]查看，审核通过后即出现在优惠榜单上。';

    }else if(msg.xml.MsgType == 'location')
    {
        content = '(3/3) 请用简短的文字描述一下优惠内容。如（蛇口沃尔玛洗面奶满50减20活动，速来。）';
    }

  var result = {
    xml: {
      ToUserName: msg.xml.FromUserName[0],
      FromUserName: '' + msg.xml.ToUserName + '',
      CreateTime: new Date().getTime(),
      MsgType: 'text',
      Content: content
    }
  }
  cb(null, result);
}
<|endoftext|>


<commit_before>/**********************************************************************
 Freeciv - Copyright (C) 2009 - Andreas Røsdal   andrearo@pvv.ntnu.no
   This program 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 2, or (at your option)
   any later version.

   This program 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.
***********************************************************************/

var touch_start_x;
var touch_start_y;

/****************************************************************************
  Triggered when the mouse button is clicked UP on the mapview canvas.
****************************************************************************/
function mapview_mouse_click(e)
{
  var rightclick = false;
  var middleclick = false;

  if (!e) var e = window.event;
  if (e.which) {
    rightclick = (e.which == 3);
    middleclick = (e.which == 2);
  } else if (e.button) {
    rightclick = (e.button == 2);
    middleclick = (e.button == 1 || e.button == 4);
  }

  if (!rightclick && !middleclick) {
    /* Left mouse button*/
    action_button_pressed(mouse_x, mouse_y, SELECT_POPUP);
  }

}

/****************************************************************************
  Triggered when the mouse button is clicked DOWN on the mapview canvas.
****************************************************************************/
function mapview_mouse_down(e)
{
  var rightclick = false;
  var middleclick = false;

  if (!e) var e = window.event;
  if (e.which) {
    rightclick = (e.which == 3);
    middleclick = (e.which == 2);
  } else if (e.button) {
    rightclick = (e.button == 2);
    middleclick = (e.button == 1 || e.button == 4);
  }

  if (!rightclick && !middleclick) {
    /* Left mouse button is down */
    if (goto_active) return;

    setTimeout("check_mouse_drag_unit(" + mouse_x + "," + mouse_y + ");", 200);
  } else if (middleclick) {
    popit();
  } else {
    release_right_button(mouse_x, mouse_y);
  }

}
/****************************************************************************
  This function is triggered when beginning a touch event on a touch device,
  eg. finger down on screen.
****************************************************************************/
function mapview_touch_start(e)
{
  e.preventDefault();

  touch_start_x = e.originalEvent.touches[0].pageX - $('#canvas').position().left;
  touch_start_y = e.originalEvent.touches[0].pageY - $('#canvas').position().top;

  setTimeout("check_mouse_drag_unit(" + touch_start_x + "," + touch_start_y + ");", 200);

}

/****************************************************************************
  This function is triggered when ending a touch event on a touch device,
  eg finger up from screen.
****************************************************************************/
function mapview_touch_end(e)
{
  action_button_pressed(touch_start_x, touch_start_y, SELECT_POPUP);
}

/****************************************************************************
  This function is triggered on a touch move event on a touch device.
****************************************************************************/
function mapview_touch_move(e)
{
  mouse_x = e.originalEvent.touches[0].pageX - $('#canvas').position().left;
  mouse_y = e.originalEvent.touches[0].pageY - $('#canvas').position().top;

  var diff_x = (touch_start_x - mouse_x) * 2;
  var diff_y = (touch_start_y - mouse_y) * 2;

  touch_start_x = mouse_x;
  touch_start_y = mouse_y;

  check_mouse_drag_unit(mouse_x, mouse_y);

  if (!goto_active) {
    set_mapview_origin(mapview['gui_x0'] + diff_x, mapview['gui_y0'] + diff_y);
  }

  if (client.conn.playing == null) return;

  /* Request preview goto path */
  goto_preview_active = true;
  if (goto_active && current_focus.length > 0) {
    var ptile = canvas_pos_to_tile(mouse_x, mouse_y);
    if (ptile != null && goto_request_map[ptile['x'] + "," + ptile['y']] == null) {
      preview_goto_path(current_focus[0]['id'], ptile['x'], ptile['y']);
    }
  }

}

/****************************************************************************
  This function is triggered when the mouse is clicked on the city canvas.
****************************************************************************/
function city_mapview_mouse_click(e)
{
  var rightclick;
  if (!e) var e = window.event;
  if (e.which) {
    rightclick = (e.which == 3);
  } else if (e.button) {
    rightclick = (e.button == 2);
  }

  if (!rightclick) {
    city_action_button_pressed(mouse_x, mouse_y);
  }

}

/****************************************************************************
...
****************************************************************************/
function check_mouse_drag_unit(canvas_x, canvas_y)
{
  var ptile = canvas_pos_to_tile(canvas_x, canvas_y);
  if (ptile == null) return;

  var sunit = find_visible_unit(ptile);

  if (sunit != null) {
    if (client.conn.playing != null && sunit['owner'] == client.conn.playing.playerno) {
      set_unit_focus(sunit);
      if (is_touch_device()) activate_goto();
    }
  }

  var ptile_units = tile_units(ptile);
  if (ptile_units.length > 1) {
     update_select_unit_dialog(ptile_units);
  }

}

/**************************************************************************
  Do some appropriate action when the "main" mouse button (usually
  left-click) is pressed.  For more sophisticated user control use (or
  write) a different xxx_button_pressed function.
**************************************************************************/
function action_button_pressed(canvas_x, canvas_y, qtype)
{
  var ptile = canvas_pos_to_tile(canvas_x, canvas_y);

  if (can_client_change_view() && ptile != null) {
    /* FIXME: Some actions here will need to check can_client_issue_orders.
     * But all we can check is the lowest common requirement. */
    do_map_click(ptile, qtype);
  }
}

/**************************************************************************
  Do some appropriate action when the "main" mouse button (usually
  left-click) is pressed.  For more sophisticated user control use (or
  write) a different xxx_button_pressed function.
**************************************************************************/
function city_action_button_pressed(canvas_x, canvas_y)
{
  var ptile = canvas_pos_to_tile(canvas_x, canvas_y);

  if (can_client_change_view() && ptile != null) {
    /* FIXME: Some actions here will need to check can_client_issue_orders.
     * But all we can check is the lowest common requirement. */
    do_city_map_click(ptile);
  }
}

/**************************************************************************
 Action depends on whether the mouse pointer moved
 a tile between press and release.
**************************************************************************/
function release_right_button(mouse_x, mouse_y)
{
  recenter_button_pressed(mouse_x, mouse_y);
}

/**************************************************************************
  Recenter the map on the canvas location, on user request.  Usually this
  is done with a right-click.
**************************************************************************/
function recenter_button_pressed(canvas_x, canvas_y)
{
  var ptile = canvas_pos_to_tile(canvas_x, canvas_y);

  if (can_client_change_view() && ptile != null) {
    /* FIXME: Some actions here will need to check can_client_issue_orders.
     * But all we can check is the lowest common requirement. */
    enable_mapview_slide(ptile);
    center_tile_mapcanvas(ptile);
  }
}

/**************************************************************************
...
**************************************************************************/
function popit()
{
  var ptile = canvas_pos_to_tile(mouse_x, mouse_y);
  if (ptile == null) return;

  var punit_id = 0;
  var punit = find_visible_unit(ptile);
  if (punit != null) punit_id = punit['id'];

  var packet = {"type" : packet_info_text_req, "visible_unit" : punit_id,
                "loc" : ptile['index']};
  send_request (JSON.stringify(packet));

}

/**************************************************************************
...
**************************************************************************/
function handle_info_text_message(packet)
{
  var message = decodeURIComponent(packet['message']);
  var regxp = /\n/gi;
  message = message.replace(regxp, "
\n");

  show_dialog_message("Tile Information", message);

}
<commit_msg>Disable window scrolling on middle mouse button click.<commit_after>/**********************************************************************
 Freeciv - Copyright (C) 2009 - Andreas Røsdal   andrearo@pvv.ntnu.no
   This program 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 2, or (at your option)
   any later version.

   This program 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.
***********************************************************************/

var touch_start_x;
var touch_start_y;

/****************************************************************************
  Triggered when the mouse button is clicked UP on the mapview canvas.
****************************************************************************/
function mapview_mouse_click(e)
{
  var rightclick = false;
  var middleclick = false;

  if (!e) var e = window.event;
  if (e.which) {
    rightclick = (e.which == 3);
    middleclick = (e.which == 2);
  } else if (e.button) {
    rightclick = (e.button == 2);
    middleclick = (e.button == 1 || e.button == 4);
  }

  if (!rightclick && !middleclick) {
    /* Left mouse button*/
    action_button_pressed(mouse_x, mouse_y, SELECT_POPUP);
  }

}

/****************************************************************************
  Triggered when the mouse button is clicked DOWN on the mapview canvas.
****************************************************************************/
function mapview_mouse_down(e)
{
  var rightclick = false;
  var middleclick = false;

  if (!e) var e = window.event;
  if (e.which) {
    rightclick = (e.which == 3);
    middleclick = (e.which == 2);
  } else if (e.button) {
    rightclick = (e.button == 2);
    middleclick = (e.button == 1 || e.button == 4);
  }

  if (!rightclick && !middleclick) {
    /* Left mouse button is down */
    if (goto_active) return;

    setTimeout("check_mouse_drag_unit(" + mouse_x + "," + mouse_y + ");", 200);
  } else if (middleclick) {
    popit();
    return false;
  } else {
    release_right_button(mouse_x, mouse_y);
  }

}
/****************************************************************************
  This function is triggered when beginning a touch event on a touch device,
  eg. finger down on screen.
****************************************************************************/
function mapview_touch_start(e)
{
  e.preventDefault();

  touch_start_x = e.originalEvent.touches[0].pageX - $('#canvas').position().left;
  touch_start_y = e.originalEvent.touches[0].pageY - $('#canvas').position().top;

  setTimeout("check_mouse_drag_unit(" + touch_start_x + "," + touch_start_y + ");", 200);

}

/****************************************************************************
  This function is triggered when ending a touch event on a touch device,
  eg finger up from screen.
****************************************************************************/
function mapview_touch_end(e)
{
  action_button_pressed(touch_start_x, touch_start_y, SELECT_POPUP);
}

/****************************************************************************
  This function is triggered on a touch move event on a touch device.
****************************************************************************/
function mapview_touch_move(e)
{
  mouse_x = e.originalEvent.touches[0].pageX - $('#canvas').position().left;
  mouse_y = e.originalEvent.touches[0].pageY - $('#canvas').position().top;

  var diff_x = (touch_start_x - mouse_x) * 2;
  var diff_y = (touch_start_y - mouse_y) * 2;

  touch_start_x = mouse_x;
  touch_start_y = mouse_y;

  check_mouse_drag_unit(mouse_x, mouse_y);

  if (!goto_active) {
    set_mapview_origin(mapview['gui_x0'] + diff_x, mapview['gui_y0'] + diff_y);
  }

  if (client.conn.playing == null) return;

  /* Request preview goto path */
  goto_preview_active = true;
  if (goto_active && current_focus.length > 0) {
    var ptile = canvas_pos_to_tile(mouse_x, mouse_y);
    if (ptile != null && goto_request_map[ptile['x'] + "," + ptile['y']] == null) {
      preview_goto_path(current_focus[0]['id'], ptile['x'], ptile['y']);
    }
  }

}

/****************************************************************************
  This function is triggered when the mouse is clicked on the city canvas.
****************************************************************************/
function city_mapview_mouse_click(e)
{
  var rightclick;
  if (!e) var e = window.event;
  if (e.which) {
    rightclick = (e.which == 3);
  } else if (e.button) {
    rightclick = (e.button == 2);
  }

  if (!rightclick) {
    city_action_button_pressed(mouse_x, mouse_y);
  }

}

/****************************************************************************
...
****************************************************************************/
function check_mouse_drag_unit(canvas_x, canvas_y)
{
  var ptile = canvas_pos_to_tile(canvas_x, canvas_y);
  if (ptile == null) return;

  var sunit = find_visible_unit(ptile);

  if (sunit != null) {
    if (client.conn.playing != null && sunit['owner'] == client.conn.playing.playerno) {
      set_unit_focus(sunit);
      if (is_touch_device()) activate_goto();
    }
  }

  var ptile_units = tile_units(ptile);
  if (ptile_units.length > 1) {
     update_select_unit_dialog(ptile_units);
  }

}

/**************************************************************************
  Do some appropriate action when the "main" mouse button (usually
  left-click) is pressed.  For more sophisticated user control use (or
  write) a different xxx_button_pressed function.
**************************************************************************/
function action_button_pressed(canvas_x, canvas_y, qtype)
{
  var ptile = canvas_pos_to_tile(canvas_x, canvas_y);

  if (can_client_change_view() && ptile != null) {
    /* FIXME: Some actions here will need to check can_client_issue_orders.
     * But all we can check is the lowest common requirement. */
    do_map_click(ptile, qtype);
  }
}

/**************************************************************************
  Do some appropriate action when the "main" mouse button (usually
  left-click) is pressed.  For more sophisticated user control use (or
  write) a different xxx_button_pressed function.
**************************************************************************/
function city_action_button_pressed(canvas_x, canvas_y)
{
  var ptile = canvas_pos_to_tile(canvas_x, canvas_y);

  if (can_client_change_view() && ptile != null) {
    /* FIXME: Some actions here will need to check can_client_issue_orders.
     * But all we can check is the lowest common requirement. */
    do_city_map_click(ptile);
  }
}

/**************************************************************************
 Action depends on whether the mouse pointer moved
 a tile between press and release.
**************************************************************************/
function release_right_button(mouse_x, mouse_y)
{
  recenter_button_pressed(mouse_x, mouse_y);
}

/**************************************************************************
  Recenter the map on the canvas location, on user request.  Usually this
  is done with a right-click.
**************************************************************************/
function recenter_button_pressed(canvas_x, canvas_y)
{
  var ptile = canvas_pos_to_tile(canvas_x, canvas_y);

  if (can_client_change_view() && ptile != null) {
    /* FIXME: Some actions here will need to check can_client_issue_orders.
     * But all we can check is the lowest common requirement. */
    enable_mapview_slide(ptile);
    center_tile_mapcanvas(ptile);
  }
}

/**************************************************************************
...
**************************************************************************/
function popit()
{
  var ptile = canvas_pos_to_tile(mouse_x, mouse_y);
  if (ptile == null) return;

  var punit_id = 0;
  var punit = find_visible_unit(ptile);
  if (punit != null) punit_id = punit['id'];

  var packet = {"type" : packet_info_text_req, "visible_unit" : punit_id,
                "loc" : ptile['index']};
  send_request (JSON.stringify(packet));

}

/**************************************************************************
...
**************************************************************************/
function handle_info_text_message(packet)
{
  var message = decodeURIComponent(packet['message']);
  var regxp = /\n/gi;
  message = message.replace(regxp, "
\n");

  show_dialog_message("Tile Information", message);

}
<|endoftext|>


<commit_before>/* globals describe it expect */
import * as subject from "../BuildStepOutput.es6";
import {shallow} from "enzyme";

describe("Output presentation", () => {
  it("should be hidden when output is false", () => {
    const input = {showOutput: false};

    expect(subject.BuildStepOutput(input)).toBe(null);
  });

  it("should display output of step if not hidden", () => {
    const input = {showOutput: true, buildId: 1, stepName: "meinStep", output: "hierTestOutput"};

    const component = shallow(subject.BuildStepOutput(input));

    expect(component.find("#outputHeader__buildId").text()).toBe("1");
    expect(component.find("#outputHeader__stepName").text()).toBe("meinStep");
    expect(component.find("#outputContent").text()).toBe("hierTestOutput");

  });
});

describe("Output redux", () => {
  it("should not output render props if hidden", () => {
    expect(subject.mapStateToProps({output: {showOutput: false}},{})).toEqual({showOutput:false});
  });

  it("should get output from buildstep", () => {
    const state = {
      buildDetails: {1: {steps: [{stepId: "1", name: "myStep", output:["line1"]}]}},
      output: {showOutput:true, buildId: 1, stepId: "1"}
    };
    const expected = {buildId: 1, stepId: "1", stepName: "myStep", output: ["line1"], showOutput: true};

    expect(subject.mapStateToProps(state)).toEqual(expected);
  });

});
<commit_msg>add test<commit_after>/* globals describe it expect */
import * as subject from "../BuildStepOutput.es6";
import {shallow} from "enzyme";

describe("Output presentation", () => {
  it("should be hidden when output is false", () => {
    const input = {showOutput: false};

    expect(subject.BuildStepOutput(input)).toBe(null);
  });

  it("should display output of step if not hidden", () => {
    const input = {showOutput: true, buildId: 1, stepName: "meinStep", output: "hierTestOutput"};

    const component = shallow(subject.BuildStepOutput(input));

    expect(component.find("#outputHeader__buildId").text()).toBe("1");
    expect(component.find("#outputHeader__stepName").text()).toBe("meinStep");
    expect(component.find("#outputContent").text()).toBe("hierTestOutput");

  });
});

describe("Output redux", () => {
  it("should not output render props if hidden", () => {
    expect(subject.mapStateToProps({output: {showOutput: false}},{})).toEqual({showOutput:false});
  });

  it("should get output from buildstep", () => {
    const state = {
      buildDetails: {1: {steps: [{stepId: "1", name: "myStep", output:["line1"]}]}},
      output: {showOutput:true, buildId: 1, stepId: "1"}
    };
    const expected = {buildId: 1, stepId: "1", stepName: "myStep", output: ["line1"], showOutput: true};

    expect(subject.mapStateToProps(state)).toEqual(expected);
  });

  it("should get undefined from buildstep if no output exists", () => {
    const state = {
      buildDetails: {1: {steps: [{stepId: "1", name: "myStep"}]}},
      output: {showOutput:true, buildId: 1, stepId: "1"}
    };
    const expected = {buildId: 1, stepId: "1", stepName: "myStep", showOutput: true};

    expect(subject.mapStateToProps(state)).toEqual(expected);
  });

});
<|endoftext|>


<commit_before><commit_msg>Fix panning issue<commit_after><|endoftext|>


<commit_before> // Renders the navigation sidebar for chapters and annotations.
_.extend(DV.Schema.helpers, {
  renderViewer: function(){
    var doc         = this.viewer.schema.document;
    var pagesHTML   = this.constructPages();
    var description = (doc.description) ? doc.description : null;
    var storyURL = doc.resources.related_article;

    var headerHTML  = JST.header({
      options     : this.viewer.options,
      id          : doc.id,
      story_url   : storyURL,
      title       : doc.title || ''
    });
    var footerHTML = JST.footer({options : this.viewer.options});

    var pdfURL   = doc.resources.pdf;
    pdfURL       = pdfURL && this.viewer.options.pdf !== false ? 'Original Document (PDF)' : '';

    var viewerOptions = {
      options : this.viewer.options,
      pages: pagesHTML,
      header: headerHTML,
      footer: footerHTML,
      pdf_url: pdfURL,
      story_url: storyURL,
      descriptionContainer: JST.descriptionContainer({ description: description}),
      autoZoom: this.viewer.options.zoom == 'auto'
    };

    if (this.viewer.options.width && this.viewer.options.height) {
      DV.jQuery(this.viewer.options.container).css({
        position: 'relative',
        width: this.viewer.options.width,
        height: this.viewer.options.height
      });
    }

    var container = this.viewer.options.container;
    var containerEl = DV.jQuery(container);
    if (!containerEl.length) throw "Document Viewer container element not found: " + container;
    containerEl.html(JST.viewer(viewerOptions));
  },

  // If there is no description, no navigation, and no sections, tighten up
  // the sidebar.
  displayNavigation : function() {
    var doc = this.viewer.schema.document;
    var missing = (!doc.description && !_.size(this.viewer.schema.data.annotationsById) && !this.viewer.schema.data.sections.length);
    this.viewer.$('.DV-supplemental').toggleClass('DV-noNavigation', missing);
  },

  renderSpecificPageCss : function() {
    var classes = [];
    for (var i = 1, l = this.models.document.totalPages; i <= l; i++) {
      classes.push('.DV-page-' + i + ' .DV-pageSpecific-' + i);
    }
    var css = classes.join(', ') + ' { display: block; }';
    var stylesheet = '';
    DV.jQuery("head").append(stylesheet);
  },

  renderNavigation : function() {
    var me = this;
    var chapterViews = [], bolds = [], expandIcons = [], expanded = [], navigationExpander = JST.navigationExpander({}),nav=[],notes = [],chapters = [];
    var boldsId = this.viewer.models.boldsId || (this.viewer.models.boldsId = _.uniqueId());

    /* ---------------------------------------------------- start the nav helper methods */
    var getAnnotionsByRange = function(rangeStart, rangeEnd){
      var annotations = [];
      for(var i = rangeStart, len = rangeEnd; i < len; i++){
        if(notes[i]){
          annotations.push(notes[i]);
          nav[i] = '';
        }
      }
      return annotations.join('');
    };

    var createChapter = function(chapter){
      var selectionRule = "#DV-selectedChapter-" + chapter.id + " #DV-chapter-" + chapter.id;

      bolds.push(selectionRule+" .DV-navChapterTitle");
      return (JST.chapterNav(chapter));
    };

    var createNavAnnotations = function(annotationIndex){
      var renderedAnnotations = [];
      var annotations = me.viewer.schema.data.annotationsByPage[annotationIndex];

      for (var j=0; j<annotations.length; j++) {
        var annotation = annotations[j];
        renderedAnnotations.push(JST.annotationNav(annotation));
        bolds.push("#DV-selectedAnnotation-" + annotation.id + " #DV-annotationMarker-" + annotation.id + " .DV-navAnnotationTitle");
      }
      return renderedAnnotations.join('');
    };
    /* ---------------------------------------------------- end the nav helper methods */

    if ( _.isUndefined(this.viewer.options.showAnnotations) ||
        (!_.isUndefined(this.viewer.options.showAnnotations) && this.viewer.options.showAnnotations !== false)) {
      for(var i = 0,len = this.models.document.totalPages; i < len;i++){
        if(this.viewer.schema.data.annotationsByPage[i]){
          nav[i]   = createNavAnnotations(i);
          notes[i] = nav[i];
        }
      }
    }

    var sections = this.viewer.schema.data.sections;
    if (sections.length) {
      for (var i = 0; i < sections.length; i++) {
        var section        = sections[i];
        var nextSection    = sections[i + 1];
        section.id         = section.id || _.uniqueId();
        section.pageNumber = section.page;
        section.endPage    = nextSection ? nextSection.page - 1 : this.viewer.schema.data.totalPages;
        var annotations    = getAnnotionsByRange(section.pageNumber - 1, section.endPage);

        if(annotations != '') {
          section.navigationExpander       = navigationExpander;
          section.navigationExpanderClass  = 'DV-hasChildren';
          section.noteViews                = annotations;
          nav[section.pageNumber - 1]      = createChapter(section);
        } else {
          section.navigationExpanderClass  = 'DV-noChildren';
          section.noteViews                = '';
          section.navigationExpander       = '';
          nav[section.pageNumber - 1]      = createChapter(section);
        }
      }
    }

    // insert and observe the nav
    var navigationView = nav.join('');

    var chaptersContainer = this.viewer.$('div.DV-chaptersContainer');
    chaptersContainer.html(navigationView);
    chaptersContainer.unbind('click').bind('click',this.events.compile('handleNavigation'));
    this.viewer.schema.data.sections.length || _.size(this.viewer.schema.data.annotationsById) ?
       chaptersContainer.show() : chaptersContainer.hide();
    this.displayNavigation();

    DV.jQuery('#DV-navigationBolds-' + boldsId, DV.jQuery("head")).remove();
    var boldsContents = bolds.join(", ") + ' { font-weight:bold; color:#000 !important; }';
    var navStylesheet = '';
    DV.jQuery("head").append(navStylesheet);
    chaptersContainer = null;
  },

  // Hide or show all of the components on the page that may or may not be
  // present, depending on what the document provides.
  renderComponents : function() {
    // Hide the overflow of the body, unless we're positioned.
    var containerEl = DV.jQuery(this.viewer.options.container);
    var position = containerEl.css('position');
    if (position != 'relative' && position != 'absolute' && !this.viewer.options.fixedSize) {
      DV.jQuery("html, body").css({overflow : 'hidden'});
      // Hide the border, if we're a full-screen viewer in the body tag.
      if (containerEl.offset().top == 0) {
        this.viewer.elements.viewer.css({border: 0});
      }
    }

    // Hide and show navigation flags:
    var showAnnotations = _.any(this.models.annotations.byId) &&
                          (!_.isUndefined(this.viewer.options.showAnnotations)
                          && this.viewer.options.showAnnotations !== false);
    var showPages       = this.models.document.totalPages > 1;
    var showSearch      = (this.viewer.options.search !== false) &&
                          (this.viewer.options.text !== false) &&
                          (!this.viewer.options.width || this.viewer.options.width >= 540);
    var noFooter = (!showAnnotations && !showPages && !showSearch && !this.viewer.options.sidebar);

    // Hide annotations, if there are none:
    var $annotationsView = this.viewer.$('.DV-annotationView');
    $annotationsView[showAnnotations ? 'show' : 'hide']();

    // Hide the text tab, if it's disabled.
    if (showSearch) {
      this.elements.viewer.addClass('DV-searchable');
      this.viewer.$('input.DV-searchInput', containerEl).placeholder({
        message: 'Search',
        clearClassName: 'DV-searchInput-show-search-cancel'
      });
    } else {
      this.viewer.$('.DV-textView').hide();
    }

    // Hide the Pages tab if there is only 1 page in the document.
    if (!showPages) {
      this.viewer.$('.DV-thumbnailsView').hide();
    }

    // Hide the Documents tab if it's the only tab left.
    if (!showAnnotations && !showPages && !showSearch) {
      this.viewer.$('.DV-views').hide();
    }

    this.viewer.api.roundTabCorners();

    // Hide the entire sidebar, if there are no annotations or sections.
    var showChapters = this.models.chapters.chapters.length > 0;

    // Remove and re-render the nav controls.
    // Don't show the nav controls if there's no sidebar, and it's a one-page doc.
    this.viewer.$('.DV-navControls').remove();
    if (showPages || this.viewer.options.sidebar) {
      var navControls = JST.navControls({
        totalPages: this.viewer.schema.data.totalPages,
        totalAnnotations: this.viewer.schema.data.totalAnnotations
      });
      this.viewer.$('.DV-navControlsContainer').html(navControls);
    }

    this.viewer.$('.DV-fullscreenControl').remove();
    if (this.viewer.schema.document.canonicalURL) {
      var fullscreenControl = JST.fullscreenControl({});
      if (noFooter) {
        this.viewer.$('.DV-collapsibleControls').prepend(fullscreenControl);
        this.elements.viewer.addClass('DV-hideFooter');
      } else {
        this.viewer.$('.DV-fullscreenContainer').html(fullscreenControl);
      }
    }

    if (this.viewer.options.sidebar) {
      this.viewer.$('.DV-sidebar').show();
    }

    // Check if the zoom is showing, and if not, shorten the width of search
    _.defer(_.bind(function() {
      if ((this.elements.viewer.width() <= 700) && (showAnnotations || showPages || showSearch)) {
        this.viewer.$('.DV-controls').addClass('DV-narrowControls');
      }
    }, this));

    // Set the currentPage element reference.
    this.elements.currentPage = this.viewer.$('span.DV-currentPage');
    this.models.document.setPageIndex(this.models.document.currentIndex());
  },

  // Reset the view state to a baseline, when transitioning between views.
  reset : function() {
    this.resetNavigationState();
    this.cleanUpSearch();
    this.viewer.pageSet.cleanUp();
    this.removeObserver('drawPages');
    this.viewer.dragReporter.unBind();
    this.elements.window.scrollTop(0);
  }

});<commit_msg>make showAnnotations a helper<commit_after> // Renders the navigation sidebar for chapters and annotations.
_.extend(DV.Schema.helpers, {
  showAnnotations : function() {
    if ((!_.isUndefined(this.viewer.options.showAnnotations)
         && this.viewer.options.showAnnotations === false)) return false;
    return _.any(this.models.annotations.byId);
  },

  renderViewer: function(){
    var doc         = this.viewer.schema.document;
    var pagesHTML   = this.constructPages();
    var description = (doc.description) ? doc.description : null;
    var storyURL = doc.resources.related_article;

    var headerHTML  = JST.header({
      options     : this.viewer.options,
      id          : doc.id,
      story_url   : storyURL,
      title       : doc.title || ''
    });
    var footerHTML = JST.footer({options : this.viewer.options});

    var pdfURL   = doc.resources.pdf;
    pdfURL       = pdfURL && this.viewer.options.pdf !== false ? 'Original Document (PDF)' : '';

    var viewerOptions = {
      options : this.viewer.options,
      pages: pagesHTML,
      header: headerHTML,
      footer: footerHTML,
      pdf_url: pdfURL,
      story_url: storyURL,
      descriptionContainer: JST.descriptionContainer({ description: description}),
      autoZoom: this.viewer.options.zoom == 'auto'
    };

    if (this.viewer.options.width && this.viewer.options.height) {
      DV.jQuery(this.viewer.options.container).css({
        position: 'relative',
        width: this.viewer.options.width,
        height: this.viewer.options.height
      });
    }

    var container = this.viewer.options.container;
    var containerEl = DV.jQuery(container);
    if (!containerEl.length) throw "Document Viewer container element not found: " + container;
    containerEl.html(JST.viewer(viewerOptions));
  },

  // If there is no description, no navigation, and no sections, tighten up
  // the sidebar.
  displayNavigation : function() {
    var doc = this.viewer.schema.document;
    var missing = (!doc.description && !_.size(this.viewer.schema.data.annotationsById) && !this.viewer.schema.data.sections.length);
    this.viewer.$('.DV-supplemental').toggleClass('DV-noNavigation', missing);
  },

  renderSpecificPageCss : function() {
    var classes = [];
    for (var i = 1, l = this.models.document.totalPages; i <= l; i++) {
      classes.push('.DV-page-' + i + ' .DV-pageSpecific-' + i);
    }
    var css = classes.join(', ') + ' { display: block; }';
    var stylesheet = '';
    DV.jQuery("head").append(stylesheet);
  },

  renderNavigation : function() {
    var me = this;
    var chapterViews = [], bolds = [], expandIcons = [], expanded = [], navigationExpander = JST.navigationExpander({}),nav=[],notes = [],chapters = [];
    var boldsId = this.viewer.models.boldsId || (this.viewer.models.boldsId = _.uniqueId());

    /* ---------------------------------------------------- start the nav helper methods */
    var getAnnotionsByRange = function(rangeStart, rangeEnd){
      var annotations = [];
      for(var i = rangeStart, len = rangeEnd; i < len; i++){
        if(notes[i]){
          annotations.push(notes[i]);
          nav[i] = '';
        }
      }
      return annotations.join('');
    };

    var createChapter = function(chapter){
      var selectionRule = "#DV-selectedChapter-" + chapter.id + " #DV-chapter-" + chapter.id;

      bolds.push(selectionRule+" .DV-navChapterTitle");
      return (JST.chapterNav(chapter));
    };

    var createNavAnnotations = function(annotationIndex){
      var renderedAnnotations = [];
      var annotations = me.viewer.schema.data.annotationsByPage[annotationIndex];

      for (var j=0; j<annotations.length; j++) {
        var annotation = annotations[j];
        renderedAnnotations.push(JST.annotationNav(annotation));
        bolds.push("#DV-selectedAnnotation-" + annotation.id + " #DV-annotationMarker-" + annotation.id + " .DV-navAnnotationTitle");
      }
      return renderedAnnotations.join('');
    };
    /* ---------------------------------------------------- end the nav helper methods */

    if (this.showAnnotations()) {
      for(var i = 0,len = this.models.document.totalPages; i < len;i++){
        if(this.viewer.schema.data.annotationsByPage[i]){
          nav[i]   = createNavAnnotations(i);
          notes[i] = nav[i];
        }
      }
    }

    var sections = this.viewer.schema.data.sections;
    if (sections.length) {
      for (var i = 0; i < sections.length; i++) {
        var section        = sections[i];
        var nextSection    = sections[i + 1];
        section.id         = section.id || _.uniqueId();
        section.pageNumber = section.page;
        section.endPage    = nextSection ? nextSection.page - 1 : this.viewer.schema.data.totalPages;
        var annotations    = getAnnotionsByRange(section.pageNumber - 1, section.endPage);

        if(annotations != '') {
          section.navigationExpander       = navigationExpander;
          section.navigationExpanderClass  = 'DV-hasChildren';
          section.noteViews                = annotations;
          nav[section.pageNumber - 1]      = createChapter(section);
        } else {
          section.navigationExpanderClass  = 'DV-noChildren';
          section.noteViews                = '';
          section.navigationExpander       = '';
          nav[section.pageNumber - 1]      = createChapter(section);
        }
      }
    }

    // insert and observe the nav
    var navigationView = nav.join('');

    var chaptersContainer = this.viewer.$('div.DV-chaptersContainer');
    chaptersContainer.html(navigationView);
    chaptersContainer.unbind('click').bind('click',this.events.compile('handleNavigation'));
    this.viewer.schema.data.sections.length || _.size(this.viewer.schema.data.annotationsById) ?
       chaptersContainer.show() : chaptersContainer.hide();
    this.displayNavigation();

    DV.jQuery('#DV-navigationBolds-' + boldsId, DV.jQuery("head")).remove();
    var boldsContents = bolds.join(", ") + ' { font-weight:bold; color:#000 !important; }';
    var navStylesheet = '';
    DV.jQuery("head").append(navStylesheet);
    chaptersContainer = null;
  },

  // Hide or show all of the components on the page that may or may not be
  // present, depending on what the document provides.
  renderComponents : function() {
    // Hide the overflow of the body, unless we're positioned.
    var containerEl = DV.jQuery(this.viewer.options.container);
    var position = containerEl.css('position');
    if (position != 'relative' && position != 'absolute' && !this.viewer.options.fixedSize) {
      DV.jQuery("html, body").css({overflow : 'hidden'});
      // Hide the border, if we're a full-screen viewer in the body tag.
      if (containerEl.offset().top == 0) {
        this.viewer.elements.viewer.css({border: 0});
      }
    }

    // Hide and show navigation flags:
    var showPages       = this.models.document.totalPages > 1;
    var showSearch      = (this.viewer.options.search !== false) &&
                          (this.viewer.options.text !== false) &&
                          (!this.viewer.options.width || this.viewer.options.width >= 540);
    var noFooter = (!this.showAnnotations() && !showPages && !showSearch && !this.viewer.options.sidebar);

    // Hide annotations, if there are none:
    var $annotationsView = this.viewer.$('.DV-annotationView');
    $annotationsView[this.showAnnotations() ? 'show' : 'hide']();

    // Hide the text tab, if it's disabled.
    if (showSearch) {
      this.elements.viewer.addClass('DV-searchable');
      this.viewer.$('input.DV-searchInput', containerEl).placeholder({
        message: 'Search',
        clearClassName: 'DV-searchInput-show-search-cancel'
      });
    } else {
      this.viewer.$('.DV-textView').hide();
    }

    // Hide the Pages tab if there is only 1 page in the document.
    if (!showPages) {
      this.viewer.$('.DV-thumbnailsView').hide();
    }

    // Hide the Documents tab if it's the only tab left.
    if (!this.showAnnotations() && !showPages && !showSearch) {
      this.viewer.$('.DV-views').hide();
    }

    this.viewer.api.roundTabCorners();

    // Hide the entire sidebar, if there are no annotations or sections.
    var showChapters = this.models.chapters.chapters.length > 0;

    // Remove and re-render the nav controls.
    // Don't show the nav controls if there's no sidebar, and it's a one-page doc.
    this.viewer.$('.DV-navControls').remove();
    if (showPages || this.viewer.options.sidebar) {
      var navControls = JST.navControls({
        totalPages: this.viewer.schema.data.totalPages,
        totalAnnotations: this.viewer.schema.data.totalAnnotations
      });
      this.viewer.$('.DV-navControlsContainer').html(navControls);
    }

    this.viewer.$('.DV-fullscreenControl').remove();
    if (this.viewer.schema.document.canonicalURL) {
      var fullscreenControl = JST.fullscreenControl({});
      if (noFooter) {
        this.viewer.$('.DV-collapsibleControls').prepend(fullscreenControl);
        this.elements.viewer.addClass('DV-hideFooter');
      } else {
        this.viewer.$('.DV-fullscreenContainer').html(fullscreenControl);
      }
    }

    if (this.viewer.options.sidebar) {
      this.viewer.$('.DV-sidebar').show();
    }

    // Check if the zoom is showing, and if not, shorten the width of search
    _.defer(_.bind(function() {
      if ((this.elements.viewer.width() <= 700) && (this.showAnnotations() || showPages || showSearch)) {
        this.viewer.$('.DV-controls').addClass('DV-narrowControls');
      }
    }, this));

    // Set the currentPage element reference.
    this.elements.currentPage = this.viewer.$('span.DV-currentPage');
    this.models.document.setPageIndex(this.models.document.currentIndex());
  },

  // Reset the view state to a baseline, when transitioning between views.
  reset : function() {
    this.resetNavigationState();
    this.cleanUpSearch();
    this.viewer.pageSet.cleanUp();
    this.removeObserver('drawPages');
    this.viewer.dragReporter.unBind();
    this.elements.window.scrollTop(0);
  }

});<|endoftext|>