<commit_before>// Turn off AJAX for local file browsing
if ( location.protocol.substr(0,4)  === 'file' ||
     location.protocol.substr(0,11) === '*-extension' ||
     location.protocol.substr(0,6)  === 'widget' ) {

	// Start with links with only the trailing slash and that aren't external links
	var fixLinks = function() {
		$( "a[href$='/'], a[href='.'], a[href='..']" ).not( "[rel='external']" ).each( function() {
			this.href = $( this ).attr( "href" ).replace( /\/$/, "" ) + "/index.html";
		});
	};

	// Fix the links for the initial page
	$( fixLinks );

	// Fix the links for subsequent ajax page loads
	$( document ).on( 'pagecreate', fixLinks );

	// Check to see if ajax can be used. This does a quick ajax request and blocks the page until its done
	$.ajax({
		url: '.',
		async: false,
		isLocal: true
	}).error(function() {
		// Ajax doesn't work so turn it off
		$( document ).on( "mobileinit", function() {
			$.mobile.ajaxEnabled = false;

			var message = $( '
' , {
				'class': "ui-footer ui-bar-e",
				style: "overflow: auto; padding:10px 15px;",
				'data-ajax-warning': true
			});

			message
			.append( "
Note: Navigation may not work if viewed locally
" )
			.append( "
The AJAX-based navigation used throughout the jQuery Mobile docs may need to be viewed on a web server to work in certain browsers. If you see an error message when you click a link, try a different browser or view help.
" );

			$( document ).on( "pagecreate", function( event ) {
				$( event.target ).append( message );
			});
		});
	});
}

// display the version of jQM
$( document ).on( "pageinit", function() {
	var version = $.mobile.version || "dev",
		words = version.split( "-" ),
		ver = words[0],
		str = (words[1] || "Final"),
		html = "Version " + ver;

	if( str.indexOf( "rc" ) == -1 ){
		str = str.charAt( 0 ).toUpperCase() + str.slice( 1 );
	} else {
		str = str.toUpperCase().replace(".", "");
	}

	if ( $.mobile.version && str ) {
		html += " " + str;
	}

	$( ".jqm-version" ).html( html );
});

// global search
$( document ).on( "pageinit", ".jqm-demos", function() {
	$( this ).find( ".jqm-search ul" ).listview({
		globalNav: "docs",
		inset: true,
		theme: "d",
		dividerTheme: "d",
		icon: false,
		filter: true,
		filterReveal: true,
		filterPlaceholder: "Search...",
		autodividers: true,
		autodividersSelector: function ( li ) {
    		return "";
  		}
	});

	$( this ).find( ".jqm-header .jqm-search-link" ).on( "click", function() {
		$( this ).parent( ".jqm-header" ).toggleClass( "jqm-search-toggle" );

		var type = $( this ).parent( ".jqm-header" ).hasClass( "jqm-search-toggle" ) ? "searchshow" : "searchhide";

		$( this ).parent( ".jqm-header" ).find( ".jqm-search" ).trigger( type );
	});

	$( this ).find( ".jqm-header .jqm-search" )
		.on( "searchshow searchhide", function( event ) {
			if ( event.type === "searchshow" ) {
				$( this ).find( ".ui-input-text" ).focus();
			} else {
				$( this )
					.find( ".ui-input-clear" ).trigger( "click" )
					.end()
					.find( ".ui-input-text" ).blur();
			}
		});

	$( this ).on( "pagehide", function() {
		$( this ).find( ".ui-input-clear" ).trigger( "click" );
	});

	$( document ).one( "pageshow", ".jqm-demos", function() {
		$( ".ui-page-active .jqm-search form" )
			.attr( "method", "get" )
			.attr( "action", "search-results.php" )
			.append("<button type='submit' data-icon='arrow-r' data-inline='true' class='ui-hidden-accessible' data-iconpos='notext'>Submit</button>")
				.parent()
				.trigger("create");

		$( ".jqm-search form" ).children( ".ui-btn" ).addClass( "ui-hidden-accessible" );

		$( ".ui-page-active .jqm-search form" ).on( "submit", function() {
			if ( $( ".ui-page-active .jqm-search li.ui-btn-up-b" ).length !== 0 ) {
				$( ".ui-page-active .jqm-search li.ui-btn-up-b a" ).click();
				return false;
			}
			var url, base = $( "base" ).attr( "href" ).split('docs')[0];
				base = base.split('index.html')[0] + "docs" + "/";
				url = base +$( this ).attr( "action" ) + "?search=" + $( this ).find( "input" ).val();

			$.mobile.changePage( url );
		});
	});
});

$( document ).on( "change", ".jqm-search input", function( e ) {
	if ( typeof e.which !== "undefined" ) {
		$( ".ui-page-active .jqm-search li.ui-btn-active" ).removeClass( "ui-btn-active" );
	}
});

$( document ).on( "keyup", ".jqm-search input", function( e ) {
			if ( e.which === $.mobile.keyCode.DOWN ) {
				if ( $( ".ui-page-active .jqm-search li.ui-btn-active" ).length == 0 ) {
					$( ".ui-page-active .jqm-search li:first" ).toggleClass( "ui-btn-active" );
				} else {
					$( ".ui-page-active .jqm-search li.ui-btn-active" ).toggleClass( "ui-btn-active" ).next().toggleClass("ui-btn-active" );
				}

				highlight();

				function highlight() {
					if ( $( ".ui-page-active .jqm-search li.ui-btn-active" ).hasClass( "ui-screen-hidden" ) ) {
						$( ".ui-page-active .jqm-search li.ui-btn-active" ).toggleClass( "ui-btn-active" ).next().toggleClass( "ui-btn-active" );

						highlight();
					}
					return;
				}
			}

			if( e.which === $.mobile.keyCode.UP ) {
				if( $( ".ui-page-active .jqm-search li.ui-btn-active" ).length !== 0 ) {
					$( ".ui-page-active .jqm-search li.ui-btn-active" ).toggleClass( "ui-btn-active" ).prev().toggleClass("ui-btn-active" );

					highlightup();
				} else {
					$( ".ui-page-active .jqm-search li:last" ).toggleClass( "ui-btn-up-d" ).toggleClass( "ui-btn-active" );
				}

				function highlightup() {
					if( $( ".ui-page-active .jqm-search li.ui-btn-active" ).hasClass( "ui-screen-hidden" ) ) {
						$( ".ui-page-active .jqm-search li.ui-btn-active" ).toggleClass( "ui-btn-active" ).prev().toggleClass("ui-btn-active" );

						highlightup();
					}
					return;
				}
			}
		});

$( document ).on( "pageinit", ".jqm-demos-search-results", function() {
	$( this ).find( ".jqm-content ul" ).listview({
		globalNav: "docs",
		inset: true,
		theme: "d",
		dividerTheme: "d",
		icon: false,
		filter: true,
		filterReveal: true,
		filterPlaceholder: "Search...",
		autodividers: true,
		autodividersSelector: function ( li ) {
    		return "";
  		}
	});
});

$( document ).on( "pageshow", ".jqm-demos-search-results", function(){
	$( this ).find( ".jqm-content input").val( $.mobile.path.parseUrl( window.location.href ).search.split( "=" )[1] ).trigger( "change" );
});<commit_msg>Demos: change class for arrow key nav on search fixes issue with enter key not working properly<commit_after>// Turn off AJAX for local file browsing
if ( location.protocol.substr(0,4)  === 'file' ||
     location.protocol.substr(0,11) === '*-extension' ||
     location.protocol.substr(0,6)  === 'widget' ) {

	// Start with links with only the trailing slash and that aren't external links
	var fixLinks = function() {
		$( "a[href$='/'], a[href='.'], a[href='..']" ).not( "[rel='external']" ).each( function() {
			this.href = $( this ).attr( "href" ).replace( /\/$/, "" ) + "/index.html";
		});
	};

	// Fix the links for the initial page
	$( fixLinks );

	// Fix the links for subsequent ajax page loads
	$( document ).on( 'pagecreate', fixLinks );

	// Check to see if ajax can be used. This does a quick ajax request and blocks the page until its done
	$.ajax({
		url: '.',
		async: false,
		isLocal: true
	}).error(function() {
		// Ajax doesn't work so turn it off
		$( document ).on( "mobileinit", function() {
			$.mobile.ajaxEnabled = false;

			var message = $( '
' , {
				'class': "ui-footer ui-bar-e",
				style: "overflow: auto; padding:10px 15px;",
				'data-ajax-warning': true
			});

			message
			.append( "
Note: Navigation may not work if viewed locally
" )
			.append( "
The AJAX-based navigation used throughout the jQuery Mobile docs may need to be viewed on a web server to work in certain browsers. If you see an error message when you click a link, try a different browser or view help.
" );

			$( document ).on( "pagecreate", function( event ) {
				$( event.target ).append( message );
			});
		});
	});
}

// display the version of jQM
$( document ).on( "pageinit", function() {
	var version = $.mobile.version || "dev",
		words = version.split( "-" ),
		ver = words[0],
		str = (words[1] || "Final"),
		html = "Version " + ver;

	if( str.indexOf( "rc" ) == -1 ){
		str = str.charAt( 0 ).toUpperCase() + str.slice( 1 );
	} else {
		str = str.toUpperCase().replace(".", "");
	}

	if ( $.mobile.version && str ) {
		html += " " + str;
	}

	$( ".jqm-version" ).html( html );
});

// global search
$( document ).on( "pageinit", ".jqm-demos", function() {
	$( this ).find( ".jqm-search ul" ).listview({
		globalNav: "docs",
		inset: true,
		theme: "d",
		dividerTheme: "d",
		icon: false,
		filter: true,
		filterReveal: true,
		filterPlaceholder: "Search...",
		autodividers: true,
		autodividersSelector: function ( li ) {
    		return "";
  		}
	});

	$( this ).find( ".jqm-header .jqm-search-link" ).on( "click", function() {
		$( this ).parent( ".jqm-header" ).toggleClass( "jqm-search-toggle" );

		var type = $( this ).parent( ".jqm-header" ).hasClass( "jqm-search-toggle" ) ? "searchshow" : "searchhide";

		$( this ).parent( ".jqm-header" ).find( ".jqm-search" ).trigger( type );
	});

	$( this ).find( ".jqm-header .jqm-search" )
		.on( "searchshow searchhide", function( event ) {
			if ( event.type === "searchshow" ) {
				$( this ).find( ".ui-input-text" ).focus();
			} else {
				$( this )
					.find( ".ui-input-clear" ).trigger( "click" )
					.end()
					.find( ".ui-input-text" ).blur();
			}
		});

	$( this ).on( "pagehide", function() {
		$( this ).find( ".ui-input-clear" ).trigger( "click" );
	});

	$( document ).one( "pageshow", ".jqm-demos", function() {
		$( ".ui-page-active .jqm-search form" )
			.attr( "method", "get" )
			.attr( "action", "search-results.php" )
			.append("<button type='submit' data-icon='arrow-r' data-inline='true' class='ui-hidden-accessible' data-iconpos='notext'>Submit</button>")
				.parent()
				.trigger("create");

		$( ".jqm-search form" ).children( ".ui-btn" ).addClass( "ui-hidden-accessible" );

		$( ".ui-page-active .jqm-search form" ).on( "submit", function() {
			if ( $( ".ui-page-active .jqm-search li.ui-btn-active" ).length !== 0 ) {
				$( ".ui-page-active .jqm-search li.ui-btn-active a" ).click();
				return false;
			}
			var url, base = $( "base" ).attr( "href" ).split('docs')[0];
				base = base.split('index.html')[0] + "docs" + "/";
				url = base +$( this ).attr( "action" ) + "?search=" + $( this ).find( "input" ).val();

			$.mobile.changePage( url );
		});
	});
});

$( document ).on( "change", ".jqm-search input", function( e ) {
	if ( typeof e.which !== "undefined" ) {
		$( ".ui-page-active .jqm-search li.ui-btn-active" ).removeClass( "ui-btn-active" );
	}
});

$( document ).on( "keyup", ".jqm-search input", function( e ) {
			if ( e.which === $.mobile.keyCode.DOWN ) {
				if ( $( ".ui-page-active .jqm-search li.ui-btn-active" ).length == 0 ) {
					$( ".ui-page-active .jqm-search li:first" ).toggleClass( "ui-btn-active" );
				} else {
					$( ".ui-page-active .jqm-search li.ui-btn-active" ).toggleClass( "ui-btn-active" ).next().toggleClass("ui-btn-active" );
				}

				highlight();

				function highlight() {
					if ( $( ".ui-page-active .jqm-search li.ui-btn-active" ).hasClass( "ui-screen-hidden" ) ) {
						$( ".ui-page-active .jqm-search li.ui-btn-active" ).toggleClass( "ui-btn-active" ).next().toggleClass( "ui-btn-active" );

						highlight();
					}
					return;
				}
			}

			if( e.which === $.mobile.keyCode.UP ) {
				if( $( ".ui-page-active .jqm-search li.ui-btn-active" ).length !== 0 ) {
					$( ".ui-page-active .jqm-search li.ui-btn-active" ).toggleClass( "ui-btn-active" ).prev().toggleClass("ui-btn-active" );

					highlightup();
				} else {
					$( ".ui-page-active .jqm-search li:last" ).toggleClass( "ui-btn-up-d" ).toggleClass( "ui-btn-active" );
				}

				function highlightup() {
					if( $( ".ui-page-active .jqm-search li.ui-btn-active" ).hasClass( "ui-screen-hidden" ) ) {
						$( ".ui-page-active .jqm-search li.ui-btn-active" ).toggleClass( "ui-btn-active" ).prev().toggleClass("ui-btn-active" );

						highlightup();
					}
					return;
				}
			}
		});

$( document ).on( "pageinit", ".jqm-demos-search-results", function() {
	$( this ).find( ".jqm-content ul" ).listview({
		globalNav: "docs",
		inset: true,
		theme: "d",
		dividerTheme: "d",
		icon: false,
		filter: true,
		filterReveal: true,
		filterPlaceholder: "Search...",
		autodividers: true,
		autodividersSelector: function ( li ) {
    		return "";
  		}
	});
});

$( document ).on( "pageshow", ".jqm-demos-search-results", function(){
	$( this ).find( ".jqm-content input").val( $.mobile.path.parseUrl( window.location.href ).search.split( "=" )[1] ).trigger( "change" );
});<|endoftext|>


<commit_before>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Navigation from '../../../components/Navigation/Navigation.js'
import CalibrateCanvas from './Calibrate'

const formats = ['A3', 'A4', 'A5', undefined] // Last item is undefined for return button

class CalibrateMenu extends Component {
  constructor(props) {
    super(props)
    this.state = {
      calibrateMode: false,
      totemMarker: null,
      defaultFormat: props.config.format,
      index: formats.indexOf(props.config.format)
    }
  }

  componentDidMount() {
    this.read(`Veuillez sélectionner le format utilisé pour calibrer l\'écran. Format ${this.props.config.format}`)
  }

  setTotem(o) {
    this.setState({totemMarker: o})
  }

  handleAction() {
    const format = this.props.config.format
    const index = formats.indexOf(format)
    let path = this.props.route.childRoutes[index].path

    if (path === 'back') {
      this.props.actions.toggleMenu('menu', 'Fermeture du menu')
    }
    else if (format) {
      this.setState({calibrateMode: true})
      switch(format) {
      case 'A3':
        this.setTotem({w:42.0/2.54,h:29.7/2.54, name: 'A3'})
        break
      case 'A4':
        this.setTotem({w:29.7/2.54,h:21.0/2.54, name: 'A4'})
        break
      case 'A5':
        this.setTotem({w:21.0/2.54,h:14.8/2.54, name: 'A5'})
        break
      default:
        break
      }
    } else {
      this.context.router.push('menu/calibrate/' + path)
    }
  }

  changeActiveMenu(index) {
    const format = this.props.route.childRoutes[index].format
    if (format) {
      this.props.actions.setOptionFormat(format)
    } else {
      this.props.actions.setOptionFormat(this.state.defaultFormat)
    }
    this.setState({index: index})
  }

  read(text) {
    this.props.config.tts.speak(text)
  }

  render() {
    if (this.state.calibrateMode) {
      return (
        <CalibrateCanvas totemMarker={this.state.totemMarker}></CalibrateCanvas>
      )
    }
    return (
      <Navigation
        action={() => this.handleAction()}
        index={this.state.index}
        items={this.props.route.childRoutes}
        changeIndex={index => this.changeActiveMenu(index)}
        read={text => this.read(text)}
        >
      </Navigation>
    )
  }
}

CalibrateMenu.contextTypes = {
  router: PropTypes.object.isRequired
}

export default CalibrateMenu<commit_msg>Remove welcome speech for calibrate menu #74<commit_after>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Navigation from '../../../components/Navigation/Navigation.js'
import CalibrateCanvas from './Calibrate'

const formats = ['A3', 'A4', 'A5', undefined] // Last item is undefined for return button

class CalibrateMenu extends Component {
  constructor(props) {
    super(props)
    this.state = {
      calibrateMode: false,
      totemMarker: null,
      defaultFormat: props.config.format,
      index: formats.indexOf(props.config.format)
    }
  }

  setTotem(o) {
    this.setState({totemMarker: o})
  }

  handleAction() {
    const format = this.props.config.format
    const index = formats.indexOf(format)
    let path = this.props.route.childRoutes[index].path

    if (path === 'back') {
      this.props.actions.toggleMenu('menu', 'Fermeture du menu')
    }
    else if (format) {
      this.setState({calibrateMode: true})
      switch(format) {
      case 'A3':
        this.setTotem({w:42.0/2.54,h:29.7/2.54, name: 'A3'})
        break
      case 'A4':
        this.setTotem({w:29.7/2.54,h:21.0/2.54, name: 'A4'})
        break
      case 'A5':
        this.setTotem({w:21.0/2.54,h:14.8/2.54, name: 'A5'})
        break
      default:
        break
      }
    } else {
      this.context.router.push('menu/calibrate/' + path)
    }
  }

  changeActiveMenu(index) {
    const format = this.props.route.childRoutes[index].format
    if (format) {
      this.props.actions.setOptionFormat(format)
    } else {
      this.props.actions.setOptionFormat(this.state.defaultFormat)
    }
    this.setState({index: index})
  }

  read(text) {
    this.props.config.tts.speak(text)
  }

  render() {
    if (this.state.calibrateMode) {
      return (
        <CalibrateCanvas totemMarker={this.state.totemMarker}></CalibrateCanvas>
      )
    }
    return (
      <Navigation
        action={() => this.handleAction()}
        index={this.state.index}
        items={this.props.route.childRoutes}
        changeIndex={index => this.changeActiveMenu(index)}
        read={text => this.read(text)}
        >
      </Navigation>
    )
  }
}

CalibrateMenu.contextTypes = {
  router: PropTypes.object.isRequired
}

export default CalibrateMenu<|endoftext|>


<commit_before>CMS.Views.Alert = Backbone.View.extend({
    options: {
        type: "alert",
        shown: true,  // is this view currently being shown?
        closeIcon: true,  // should we render a close button in the top right corner?
        minShown: 0,  // length of time after this view has been shown before it can be hidden (milliseconds)
        maxShown: Infinity  // length of time after this view has been shown before it will be automatically hidden (milliseconds)
    },
    initialize: function() {
        var tpl = $("#"+this.options.type+"-tpl").text();
        if(!tpl) {
            console.error("Couldn't load template from ID "+this.options.type+"-tpl");
        }
        this.template = _.template(tpl);
        this.setElement($("#page-"+this.options.type));
        this.listenTo(this.model, 'change', this.render);
        return this.show();
    },
    render: function() {
        var attrs = $.extend({}, this.options, this.model.attributes);
        this.$el.html(this.template(attrs));
        return this;
    },
    events: {
        "click .action-close": "hide",
        "click .action-primary": "primaryClick",
        "click .action-secondary": "secondaryClick"
    },
    show: function() {
        clearTimeout(this.hideTimeout);
        this.options.shown = true;
        this.shownAt = new Date();
        this.render();
        if($.isNumeric(this.options.maxShown)) {
            this.hideTimeout = setTimeout($.proxy(this.hide, this),
                this.options.maxShown);
        }
        return this;
    },
    hide: function() {
        if(this.shownAt && $.isNumeric(this.options.minShown) &&
           this.options.minShown > new Date() - this.shownAt)
        {
            clearTimeout(this.hideTimeout);
            this.hideTimeout = setTimeout($.proxy(this.hide, this),
                this.options.minShown - (new Date() - this.shownAt));
        } else {
            this.options.shown = false;
            delete this.shownAt;
            this.render();
        }
        return this;
    },
    primaryClick: function() {
        var actions = this.model.get("actions");
        if(!actions) { return; }
        var primary = actions.primary;
        if(!primary) { return; }
        if(primary.click) {
            primary.click.call(this.model, this);
        }
    },
    secondaryClick: function(e) {
        var actions = this.model.get("actions");
        if(!actions) { return; }
        var secondaryList = actions.secondary;
        if(!secondaryList) { return; }
        // which secondary action was clicked?
        var i;
        if(e && e.target) {
            i = _.indexOf(this.$(".action-secondary"), e.target);
        } else {
            i = 0;
        }
        var secondary = this.model.get("actions").secondary[i];
        if(secondary.click) {
            secondary.click.call(this.model, this);
        }
    }
});

CMS.Views.Notification = CMS.Views.Alert.extend({
    options: $.extend({}, CMS.Views.Alert.prototype.options, {
        type: "notification",
        closeIcon: false
    })
});
CMS.Views.Prompt = CMS.Views.Alert.extend({
    options: $.extend({}, CMS.Views.Alert.prototype.options, {
        type: "prompt",
        closeIcon: false
    }),
    render: function() {
        if(!window.$body) { window.$body = $(document.body); }
        if(this.options.shown) {
            $body.addClass('prompt-is-shown');
        } else {
            $body.removeClass('prompt-is-shown');
        }
        // super() in Javascript has awkward syntax :(
        return CMS.Views.Alert.prototype.render.apply(this, arguments);
    }
});
<commit_msg>Shorten secondary action detection code<commit_after>CMS.Views.Alert = Backbone.View.extend({
    options: {
        type: "alert",
        shown: true,  // is this view currently being shown?
        closeIcon: true,  // should we render a close button in the top right corner?
        minShown: 0,  // length of time after this view has been shown before it can be hidden (milliseconds)
        maxShown: Infinity  // length of time after this view has been shown before it will be automatically hidden (milliseconds)
    },
    initialize: function() {
        var tpl = $("#"+this.options.type+"-tpl").text();
        if(!tpl) {
            console.error("Couldn't load template from ID "+this.options.type+"-tpl");
        }
        this.template = _.template(tpl);
        this.setElement($("#page-"+this.options.type));
        this.listenTo(this.model, 'change', this.render);
        return this.show();
    },
    render: function() {
        var attrs = $.extend({}, this.options, this.model.attributes);
        this.$el.html(this.template(attrs));
        return this;
    },
    events: {
        "click .action-close": "hide",
        "click .action-primary": "primaryClick",
        "click .action-secondary": "secondaryClick"
    },
    show: function() {
        clearTimeout(this.hideTimeout);
        this.options.shown = true;
        this.shownAt = new Date();
        this.render();
        if($.isNumeric(this.options.maxShown)) {
            this.hideTimeout = setTimeout($.proxy(this.hide, this),
                this.options.maxShown);
        }
        return this;
    },
    hide: function() {
        if(this.shownAt && $.isNumeric(this.options.minShown) &&
           this.options.minShown > new Date() - this.shownAt)
        {
            clearTimeout(this.hideTimeout);
            this.hideTimeout = setTimeout($.proxy(this.hide, this),
                this.options.minShown - (new Date() - this.shownAt));
        } else {
            this.options.shown = false;
            delete this.shownAt;
            this.render();
        }
        return this;
    },
    primaryClick: function() {
        var actions = this.model.get("actions");
        if(!actions) { return; }
        var primary = actions.primary;
        if(!primary) { return; }
        if(primary.click) {
            primary.click.call(this.model, this);
        }
    },
    secondaryClick: function(e) {
        var actions = this.model.get("actions");
        if(!actions) { return; }
        var secondaryList = actions.secondary;
        if(!secondaryList) { return; }
        // which secondary action was clicked?
        var i = 0;  // default to the first secondary action (easier for testing)
        if(e && e.target) {
            i = _.indexOf(this.$(".action-secondary"), e.target);
        }
        var secondary = this.model.get("actions").secondary[i];
        if(secondary.click) {
            secondary.click.call(this.model, this);
        }
    }
});

CMS.Views.Notification = CMS.Views.Alert.extend({
    options: $.extend({}, CMS.Views.Alert.prototype.options, {
        type: "notification",
        closeIcon: false
    })
});
CMS.Views.Prompt = CMS.Views.Alert.extend({
    options: $.extend({}, CMS.Views.Alert.prototype.options, {
        type: "prompt",
        closeIcon: false
    }),
    render: function() {
        if(!window.$body) { window.$body = $(document.body); }
        if(this.options.shown) {
            $body.addClass('prompt-is-shown');
        } else {
            $body.removeClass('prompt-is-shown');
        }
        // super() in Javascript has awkward syntax :(
        return CMS.Views.Alert.prototype.render.apply(this, arguments);
    }
});
<|endoftext|>


<commit_before>/* Validation middleware for routes. */

var Error = require('./lib/errors');

var validate = function(obj, schema, callback, fieldName) {

	if (!schema)
		throw new Error('No schema supplied for validation.');
	fieldName = fieldName || 'obj';

	/* Type shortcuts. */
	if ('Object' == schema.constructor.name && schema.type === undefined)
		schema = { type: Object, schema: schema };
	else if ('Array' == schema.constructor.name)
		schema = { type: Array, schema: schema[0] };
	else if ('Function' == schema.constructor.name)
		schema = { type: schema };

	if (obj === undefined) {

		if (schema.default)
			callback(null, schema.default);
		else if (schema.required)
			callback(new Error('Validation: Required key ' + fieldName + ' not set.'));
		else
			callback(null, obj);

	} else {

		if ('Object' == schema.type.name) { /* Validate type Object. */

			if (obj === null)
				callback(null, null);
			else if ('Object' != obj.constructor.name) /* Enforce obj type. */
				callback(new Error('Validation: Key ' + fieldName + ' is not of type Object.'));
			else {

				/* Find unknown keys. */
				for (var key in obj)
					if (!schema.schema[key]) {
						callback(new Error('Validation: Unknown key ' + fieldName + '.' + key + '.'));
						return;
					}

				/* Copy schema. */
				var schemaCopy = {};
				for (var key in schema.schema)
					schemaCopy[key] = schema.schema[key];

				/* Validate obj and build validated object (validObj). */
				var validatedObj = {};

				(function validateKey() {
					for (var key in schemaCopy) break; /* Get first key. */
					if (key !== undefined) {
						validate(obj[key], schemaCopy[key], function(err, val) {
							if (!err) {
								delete schemaCopy[key];
								if (val !== undefined)
									validatedObj[key] = val;
								validateKey();
							} else
								callback(err);
						}, fieldName + '.' + key);
					} else
						callback(null, validatedObj);
				})();

			}

		} else if ('Array' == schema.type.name) { /* Validate type Array. */

			if (obj === null)
				callback(null, null);
			else if ('Array' != obj.constructor.name) /* Enforce obj type. */
				callback(new Error('Validation: Key ' + fieldName + ' is not of type Array.'));
			else {

				/* Enforce range option. */
				if (schema.len && 'String' == schema.len.constructor.name) {

					var lengthValid = false;
					var ranges = schema.len.replace(/[ ]+/, '').split(',');
					for (var index in ranges) {
						var range = ranges[index].split('-');
						if (range.length == 1 && obj.length == parseInt(range[0])) {
							lengthValid = true;
							continue;
						} else if (range.length == 2) {
							range[0] = (range[0].length == 0 ? 0 : parseInt(range[0]));
							range[1] = (range[1].length == 0 ? Infinity : parseInt(range[1]));
							if (obj.length >= range[0] && obj.length <= range[1]) {
								lengthValid = true;
								continue;
							}
						}
					}

					if (!lengthValid) {
						callback(new Error('Validation: Key ' + fieldName + ' length is not within the range(s) ' + schema.len + '.'));
						return;
					}

				} else if (schema.len && 'Number' == schema.len.constructor.name && obj.length != schema.len) {
					callback(new Error('Validation: Key ' + fieldName + ' array does not hold ' + schema.len + ' items.'));
					return;
				}

				var validatedArr = [];
				var index = 0;

				(function validateIndex() {

					if (index < obj.length) {

						validate(obj[index], schema.schema, function(err, val) {
							if (!err) {
								if (val !== undefined)
									validatedArr.push(val);
								index++;
								validateIndex();
							} else
								callback(err);
						}, fieldName + '.' + index);

					} else {

						if (schema.unique) {

							for (var a = 1 ; a < validatedArr.length ; a++)
								for (var b = 0 ; b < a ; b++)
									if (('Boolean' == schema.unique.constructor.type && validatedArr[a] == validatedArr[b])
										|| ('String' == schema.unique.constructor.name && validatedArr[a][schema.unique] == validatedArr[b][schema.unique])) {
										callback(new Error('Validation: Index ' + b + ' and ' + a + ' are non-unique in key ' + fieldName + '.'));
										return;
									}

						}

						callback(null, validatedArr);

					}

				})();

			}

		} else if ('String' == schema.type.name) { /* Validate type String */

			if (obj === null && !schema.match)
				callback(null, null)
			else if (obj === null)
				callback(new Error('Validation: Key ' + fieldName + ' does not match expression ' + schema.match.source + '.'));
			else if ('String' != obj.constructor.name) /* Enforce obj type */
				callback(new Error('Validation: Key ' + fieldName + ' is not of type String.'));
			else {

				var validStr = obj;

				if (schema.trim)
					validStr = validStr.replace(/^\s+|\s+$/g,'');

				if (schema.match && !schema.match.test(obj)) {
					callback(new Error('Validation: Key ' + fieldName + ' does not match expression ' + schema.match.source + '.' ));
					return;
				}

				callback(null, validStr);

			}

		} else if ('Number' == schema.type.name) {

			if (obj === null)
				callback(null, null);
			else {

				if ('Number' == obj.constructor.name)
					callback(null, obj);
				else if ('String' == obj.constructor.name) {

					var match = obj.match(/[0-9]+(?:\.[0.9])?/);
					if (match && match[0].length == obj.length) {
						callback(null, parseFloat(obj));
						return;
					}

				}

				callback(new Error('Validation: Key ' + fieldName + ' is not a number.'));

			}

		} else if ('Boolean' == schema.type.name) {

			if (obj === null || 'Boolean' == obj.constructor.name)
				callback(null, obj);
			else if ('String' == obj.constructor.name && (obj.toLowerCase() == 'true' || obj.toLowerCase() == 'false'))
				callback(null, (obj.toLowerCase() == 'true'));
			else
				callback(new Error('Validation: ' + fieldName + ' is not a boolean.'));

		} else {

			if (obj.constructor.name != schema.type.name)
				callback(new Error('Validation: Key ' + fieldName + ' is not a ' + schema.type.name + '.'));
			else
				callback(null, obj);

		}

	}

};

module.exports = {

	validate: validate,
	validateBody: function(schema) {

		return function(req, res, next) {
			validate(req.body, schema, function(err, validObj) {
				req.body = validObj;
				next(err);
			}, 'body');
		};

	},
	validateQuery: function(schema) {

		return function(req, res, next) {
			validate(req.query, schema, function(err, validObj) {
				req.query = validObj;
				next(err);
			}, 'query');
		};

	}

};
<commit_msg>Forgot to remove error require.<commit_after>/* Validation middleware for routes. */

var validate = function(obj, schema, callback, fieldName) {

	if (!schema)
		throw new Error('No schema supplied for validation.');
	fieldName = fieldName || 'obj';

	/* Type shortcuts. */
	if ('Object' == schema.constructor.name && schema.type === undefined)
		schema = { type: Object, schema: schema };
	else if ('Array' == schema.constructor.name)
		schema = { type: Array, schema: schema[0] };
	else if ('Function' == schema.constructor.name)
		schema = { type: schema };

	if (obj === undefined) {

		if (schema.default)
			callback(null, schema.default);
		else if (schema.required)
			callback(new Error('Validation: Required key ' + fieldName + ' not set.'));
		else
			callback(null, obj);

	} else {

		if ('Object' == schema.type.name) { /* Validate type Object. */

			if (obj === null)
				callback(null, null);
			else if ('Object' != obj.constructor.name) /* Enforce obj type. */
				callback(new Error('Validation: Key ' + fieldName + ' is not of type Object.'));
			else {

				/* Find unknown keys. */
				for (var key in obj)
					if (!schema.schema[key]) {
						callback(new Error('Validation: Unknown key ' + fieldName + '.' + key + '.'));
						return;
					}

				/* Copy schema. */
				var schemaCopy = {};
				for (var key in schema.schema)
					schemaCopy[key] = schema.schema[key];

				/* Validate obj and build validated object (validObj). */
				var validatedObj = {};

				(function validateKey() {
					for (var key in schemaCopy) break; /* Get first key. */
					if (key !== undefined) {
						validate(obj[key], schemaCopy[key], function(err, val) {
							if (!err) {
								delete schemaCopy[key];
								if (val !== undefined)
									validatedObj[key] = val;
								validateKey();
							} else
								callback(err);
						}, fieldName + '.' + key);
					} else
						callback(null, validatedObj);
				})();

			}

		} else if ('Array' == schema.type.name) { /* Validate type Array. */

			if (obj === null)
				callback(null, null);
			else if ('Array' != obj.constructor.name) /* Enforce obj type. */
				callback(new Error('Validation: Key ' + fieldName + ' is not of type Array.'));
			else {

				/* Enforce range option. */
				if (schema.len && 'String' == schema.len.constructor.name) {

					var lengthValid = false;
					var ranges = schema.len.replace(/[ ]+/, '').split(',');
					for (var index in ranges) {
						var range = ranges[index].split('-');
						if (range.length == 1 && obj.length == parseInt(range[0])) {
							lengthValid = true;
							continue;
						} else if (range.length == 2) {
							range[0] = (range[0].length == 0 ? 0 : parseInt(range[0]));
							range[1] = (range[1].length == 0 ? Infinity : parseInt(range[1]));
							if (obj.length >= range[0] && obj.length <= range[1]) {
								lengthValid = true;
								continue;
							}
						}
					}

					if (!lengthValid) {
						callback(new Error('Validation: Key ' + fieldName + ' length is not within the range(s) ' + schema.len + '.'));
						return;
					}

				} else if (schema.len && 'Number' == schema.len.constructor.name && obj.length != schema.len) {
					callback(new Error('Validation: Key ' + fieldName + ' array does not hold ' + schema.len + ' items.'));
					return;
				}

				var validatedArr = [];
				var index = 0;

				(function validateIndex() {

					if (index < obj.length) {

						validate(obj[index], schema.schema, function(err, val) {
							if (!err) {
								if (val !== undefined)
									validatedArr.push(val);
								index++;
								validateIndex();
							} else
								callback(err);
						}, fieldName + '.' + index);

					} else {

						if (schema.unique) {

							for (var a = 1 ; a < validatedArr.length ; a++)
								for (var b = 0 ; b < a ; b++)
									if (('Boolean' == schema.unique.constructor.type && validatedArr[a] == validatedArr[b])
										|| ('String' == schema.unique.constructor.name && validatedArr[a][schema.unique] == validatedArr[b][schema.unique])) {
										callback(new Error('Validation: Index ' + b + ' and ' + a + ' are non-unique in key ' + fieldName + '.'));
										return;
									}

						}

						callback(null, validatedArr);

					}

				})();

			}

		} else if ('String' == schema.type.name) { /* Validate type String */

			if (obj === null && !schema.match)
				callback(null, null)
			else if (obj === null)
				callback(new Error('Validation: Key ' + fieldName + ' does not match expression ' + schema.match.source + '.'));
			else if ('String' != obj.constructor.name) /* Enforce obj type */
				callback(new Error('Validation: Key ' + fieldName + ' is not of type String.'));
			else {

				var validStr = obj;

				if (schema.trim)
					validStr = validStr.replace(/^\s+|\s+$/g,'');

				if (schema.match && !schema.match.test(obj)) {
					callback(new Error('Validation: Key ' + fieldName + ' does not match expression ' + schema.match.source + '.' ));
					return;
				}

				callback(null, validStr);

			}

		} else if ('Number' == schema.type.name) {

			if (obj === null)
				callback(null, null);
			else {

				if ('Number' == obj.constructor.name)
					callback(null, obj);
				else if ('String' == obj.constructor.name) {

					var match = obj.match(/[0-9]+(?:\.[0.9])?/);
					if (match && match[0].length == obj.length) {
						callback(null, parseFloat(obj));
						return;
					}

				}

				callback(new Error('Validation: Key ' + fieldName + ' is not a number.'));

			}

		} else if ('Boolean' == schema.type.name) {

			if (obj === null || 'Boolean' == obj.constructor.name)
				callback(null, obj);
			else if ('String' == obj.constructor.name && (obj.toLowerCase() == 'true' || obj.toLowerCase() == 'false'))
				callback(null, (obj.toLowerCase() == 'true'));
			else
				callback(new Error('Validation: ' + fieldName + ' is not a boolean.'));

		} else {

			if (obj.constructor.name != schema.type.name)
				callback(new Error('Validation: Key ' + fieldName + ' is not a ' + schema.type.name + '.'));
			else
				callback(null, obj);

		}

	}

};

module.exports = {

	validate: validate,
	validateBody: function(schema) {

		return function(req, res, next) {
			validate(req.body, schema, function(err, validObj) {
				req.body = validObj;
				next(err);
			}, 'body');
		};

	},
	validateQuery: function(schema) {

		return function(req, res, next) {
			validate(req.query, schema, function(err, validObj) {
				req.query = validObj;
				next(err);
			}, 'query');
		};

	}

};
<|endoftext|>


<commit_before>/**
 * @license
 * Visual Blocks Editor
 *
 * Copyright 2012 Google Inc.
 * https://developers.google.com/blockly/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Variable input field.
 * @author fraser@google.com (Neil Fraser)
 */
'use strict';

goog.provide('Blockly.FieldVariable');

goog.require('Blockly.FieldDropdown');
goog.require('Blockly.Msg');
goog.require('Blockly.VariableModel');
goog.require('Blockly.Variables');
goog.require('goog.asserts');
goog.require('goog.string');

/**
 * Class for a variable's dropdown field.
 * @param {?string} varname The default name for the variable.  If null,
 *     a unique variable name will be generated.
 * @param {Function=} opt_validator A function that is executed when a new
 *     option is selected.  Its sole argument is the new option value.
 * @param {Array.<string>=} opt_variableTypes A list of the types of variables
 *     to include in the dropdown.
 * @param {string=} opt_defaultType The type of variable to create if this
 *     field's value is not explicitly set.  Defaults to ''.
 * @extends {Blockly.FieldDropdown}
 * @constructor
 */
Blockly.FieldVariable = function(varname, opt_validator, opt_variableTypes,
    opt_defaultType) {
  // The FieldDropdown constructor would call setValue, which might create a
  // spurious variable.  Just do the relevant parts of the constructor.
  this.menuGenerator_ = Blockly.FieldVariable.dropdownCreate;
  this.size_ = new goog.math.Size(0, Blockly.BlockSvg.MIN_BLOCK_Y);
  this.setValidator(opt_validator);
  this.defaultVariableName = (varname || '');

  this.setTypes_(opt_variableTypes, opt_defaultType);
  this.value_ = null;
};
goog.inherits(Blockly.FieldVariable, Blockly.FieldDropdown);

/**
 * Construct a FieldVariable from a JSON arg object,
 * dereferencing any string table references.
 * @param {!Object} options A JSON object with options (variable,
 *                          variableTypes, and defaultType).
 * @returns {!Blockly.FieldVariable} The new field instance.
 * @package
 */
Blockly.FieldVariable.fromJson = function(options) {
  var varname = Blockly.utils.replaceMessageReferences(options['variable']);
  var variableTypes = options['variableTypes'];
  var defaultType = options['defaultType'];
  return new Blockly.FieldVariable(varname, null, variableTypes, defaultType);
};

/**
 * Initialize everything needed to render this field.  This includes making sure
 * that the field's value is valid.
 * @public
 */
Blockly.FieldVariable.prototype.init = function() {
  if (this.fieldGroup_) {
    // Dropdown has already been initialized once.
    return;
  }
  Blockly.FieldVariable.superClass_.init.call(this);

  // TODO (1010): Change from init/initModel to initView/initModel
  this.initModel();
};

/**
 * Initialize the model for this field if it has not already been initialized.
 * If the value has not been set to a variable by the first render, we make up a
 * variable rather than let the value be invalid.
 * @package
 */
Blockly.FieldVariable.prototype.initModel = function() {
  if (this.variable_) {
    return; // Initialization already happened.
  }
  this.workspace_ = this.sourceBlock_.workspace;
  var variable = Blockly.Variables.getOrCreateVariablePackage(
      this.workspace_, null, this.defaultVariableName, this.defaultType_);

  // Don't fire a change event for this setValue.  It would have null as the
  // old value, which is not valid.
  Blockly.Events.disable();
  try {
    this.setValue(variable.getId());
  } finally {
    Blockly.Events.enable();
  }
};

/**
 * Dispose of this field.
 * @public
 */
Blockly.FieldVariable.dispose = function() {
  Blockly.FieldVariable.superClass_.dispose.call(this);
  this.workspace_ = null;
  this.variableMap_ = null;
};

/**
 * Attach this field to a block.
 * @param {!Blockly.Block} block The block containing this field.
 */
Blockly.FieldVariable.prototype.setSourceBlock = function(block) {
  goog.asserts.assert(!block.isShadow(),
      'Variable fields are not allowed to exist on shadow blocks.');
  Blockly.FieldVariable.superClass_.setSourceBlock.call(this, block);
};

/**
 * Get the variable's ID.
 * @return {string} Current variable's ID.
 */
Blockly.FieldVariable.prototype.getValue = function() {
  return this.variable_ ? this.variable_.getId() : null;
};

/**
 * Get the text from this field, which is the selected variable's name.
 * @return {string} The selected variable's name, or the empty string if no
 *     variable is selected.
 */
Blockly.FieldVariable.prototype.getText = function() {
  return this.variable_ ? this.variable_.name : '';
};

/**
 * Get the variable model for the selected variable.
 * Not guaranteed to be in the variable map on the workspace (e.g. if accessed
 * after the variable has been deleted).
 * @return {?Blockly.VariableModel} the selected variable, or null if none was
 *     selected.
 * @package
 */
Blockly.FieldVariable.prototype.getVariable = function() {
  return this.variable_;
};

/**
 * Set the variable ID.
 * @param {string} id New variable ID, which must reference an existing
 *     variable.
 */
Blockly.FieldVariable.prototype.setValue = function(id) {
  var workspace = this.sourceBlock_.workspace;
  var variable = Blockly.Variables.getVariable(workspace, id);

  if (!variable) {
    throw new Error('Variable id doesn\'t point to a real variable!  ID was ' +
        id);
  }
  // Type checks!
  var type = variable.type;
  if (!this.typeIsAllowed_(type)) {
    throw new Error('Variable type doesn\'t match this field!  Type was ' +
        type);
  }
  if (this.sourceBlock_ && Blockly.Events.isEnabled()) {
    var oldValue = this.variable_ ? this.variable_.getId() : null;
    Blockly.Events.fire(new Blockly.Events.BlockChange(
        this.sourceBlock_, 'field', this.name, oldValue, id));
  }
  this.variable_ = variable;
  this.value_ = id;
  this.setText(variable.name);
};

/**
 * Check whether the given variable type is allowed on this field.
 * @param {string} type The type to check.
 * @return {boolean} True if the type is in the list of allowed types.
 * @private
 */
Blockly.FieldVariable.prototype.typeIsAllowed_ = function(type) {
  var typeList = this.getVariableTypes_();
  if (!typeList) {
    return true; // If it's null, all types are valid.
  }
  for (var i = 0; i < typeList.length; i++) {
    if (type == typeList[i]) {
      return true;
    }
  }
  return false;
};

/**
 * Return a list of variable types to include in the dropdown.
 * @return {!Array.<string>} Array of variable types.
 * @throws {Error} if variableTypes is an empty array.
 * @private
 */
Blockly.FieldVariable.prototype.getVariableTypes_ = function() {
  // TODO (#1513): Try to avoid calling this every time the field is edited.
  var variableTypes = this.variableTypes;
  if (variableTypes === null) {
    // If variableTypes is null, return all variable types.
    if (this.sourceBlock_) {
      var workspace = this.sourceBlock_.workspace;
      return workspace.getVariableTypes();
    }
  }
  variableTypes = variableTypes || [''];
  if (variableTypes.length == 0) {
    // Throw an error if variableTypes is an empty list.
    var name = this.getText();
    throw new Error('\'variableTypes\' of field variable ' +
      name + ' was an empty list');
  }
  return variableTypes;
};

/**
 * Parse the optional arguments representing the allowed variable types and the
 * default variable type.
 * @param {Array.<string>=} opt_variableTypes A list of the types of variables
 *     to include in the dropdown.  If null or undefined, variables of all types
 *     will be displayed in the dropdown.
 * @param {string=} opt_defaultType The type of the variable to create if this
 *     field's value is not explicitly set.  Defaults to ''.
 * @private
 */
Blockly.FieldVariable.prototype.setTypes_ = function(opt_variableTypes,
    opt_defaultType) {
  // If you expected that the default type would be the same as the only entry
  // in the variable types array, tell the Blockly team by commenting on #1499.
  var defaultType = opt_defaultType || '';
  // Set the allowable variable types.  Null means all types on the workspace.
  if (opt_variableTypes == null || opt_variableTypes == undefined) {
    var variableTypes = null;
  } else if (Array.isArray(opt_variableTypes)) {
    var variableTypes = opt_variableTypes;
    // Make sure the default type is valid.
    var isInArray = false;
    for (var i = 0; i < variableTypes.length; i++) {
      if (variableTypes[i] == defaultType) {
        isInArray = true;
      }
    }
    if (!isInArray) {
      throw new Error('Invalid default type \'' + defaultType + '\' in ' +
          'the definition of a FieldVariable');
    }
  } else {
    throw new Error('\'variableTypes\' was not an array in the definition of ' +
        'a FieldVariable');
  }
  // Only update the field once all checks pass.
  this.defaultType_ =  defaultType;
  this.variableTypes = variableTypes;
};

/**
 * Return a sorted list of variable names for variable dropdown menus.
 * Include a special option at the end for creating a new variable name.
 * @return {!Array.<string>} Array of variable names.
 * @this {Blockly.FieldVariable}
 */
Blockly.FieldVariable.dropdownCreate = function() {
  if (!this.variable_) {
    throw new Error('Tried to call dropdownCreate on a variable field with no' +
        ' variable selected.');
  }
  var variableModelList = [];
  var name = this.getText();
  var workspace = null;
  if (this.sourceBlock_) {
    workspace = this.sourceBlock_.workspace;
  }
  if (workspace) {
    var variableTypes = this.getVariableTypes_();
    var variableModelList = [];
    // Get a copy of the list, so that adding rename and new variable options
    // doesn't modify the workspace's list.
    for (var i = 0; i < variableTypes.length; i++) {
      var variableType = variableTypes[i];
      var variables = workspace.getVariablesOfType(variableType);
      variableModelList = variableModelList.concat(variables);
    }
  }
  variableModelList.sort(Blockly.VariableModel.compareByName);

  var options = [];
  for (var i = 0; i < variableModelList.length; i++) {
    // Set the UUID as the internal representation of the variable.
    options[i] = [variableModelList[i].name, variableModelList[i].getId()];
  }
  options.push([Blockly.Msg.RENAME_VARIABLE, Blockly.RENAME_VARIABLE_ID]);
  if (Blockly.Msg.DELETE_VARIABLE) {
    options.push(
        [
          Blockly.Msg.DELETE_VARIABLE.replace('%1', name),
          Blockly.DELETE_VARIABLE_ID
        ]
    );
  }

  return options;
};

/**
 * Handle the selection of an item in the variable dropdown menu.
 * Special case the 'Rename variable...' and 'Delete variable...' options.
 * In the rename case, prompt the user for a new name.
 * @param {!goog.ui.Menu} menu The Menu component clicked.
 * @param {!goog.ui.MenuItem} menuItem The MenuItem selected within menu.
 */
Blockly.FieldVariable.prototype.onItemSelected = function(menu, menuItem) {
  var id = menuItem.getValue();
  if (this.sourceBlock_ && this.sourceBlock_.workspace) {
    var workspace = this.sourceBlock_.workspace;
    if (id == Blockly.RENAME_VARIABLE_ID) {
      // Rename variable.
      Blockly.Variables.renameVariable(workspace, this.variable_);
      return;
    } else if (id == Blockly.DELETE_VARIABLE_ID) {
      // Delete variable.
      workspace.deleteVariableById(this.variable_.getId());
      return;
    }

    // TODO (#1529): Call any validation function, and allow it to override.
  }
  this.setValue(id);
};

Blockly.Field.register('field_variable', Blockly.FieldVariable);
<commit_msg>Field.Variable's dispose should be on the prototype<commit_after>/**
 * @license
 * Visual Blocks Editor
 *
 * Copyright 2012 Google Inc.
 * https://developers.google.com/blockly/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Variable input field.
 * @author fraser@google.com (Neil Fraser)
 */
'use strict';

goog.provide('Blockly.FieldVariable');

goog.require('Blockly.FieldDropdown');
goog.require('Blockly.Msg');
goog.require('Blockly.VariableModel');
goog.require('Blockly.Variables');
goog.require('goog.asserts');
goog.require('goog.string');

/**
 * Class for a variable's dropdown field.
 * @param {?string} varname The default name for the variable.  If null,
 *     a unique variable name will be generated.
 * @param {Function=} opt_validator A function that is executed when a new
 *     option is selected.  Its sole argument is the new option value.
 * @param {Array.<string>=} opt_variableTypes A list of the types of variables
 *     to include in the dropdown.
 * @param {string=} opt_defaultType The type of variable to create if this
 *     field's value is not explicitly set.  Defaults to ''.
 * @extends {Blockly.FieldDropdown}
 * @constructor
 */
Blockly.FieldVariable = function(varname, opt_validator, opt_variableTypes,
    opt_defaultType) {
  // The FieldDropdown constructor would call setValue, which might create a
  // spurious variable.  Just do the relevant parts of the constructor.
  this.menuGenerator_ = Blockly.FieldVariable.dropdownCreate;
  this.size_ = new goog.math.Size(0, Blockly.BlockSvg.MIN_BLOCK_Y);
  this.setValidator(opt_validator);
  this.defaultVariableName = (varname || '');

  this.setTypes_(opt_variableTypes, opt_defaultType);
  this.value_ = null;
};
goog.inherits(Blockly.FieldVariable, Blockly.FieldDropdown);

/**
 * Construct a FieldVariable from a JSON arg object,
 * dereferencing any string table references.
 * @param {!Object} options A JSON object with options (variable,
 *                          variableTypes, and defaultType).
 * @returns {!Blockly.FieldVariable} The new field instance.
 * @package
 */
Blockly.FieldVariable.fromJson = function(options) {
  var varname = Blockly.utils.replaceMessageReferences(options['variable']);
  var variableTypes = options['variableTypes'];
  var defaultType = options['defaultType'];
  return new Blockly.FieldVariable(varname, null, variableTypes, defaultType);
};

/**
 * Initialize everything needed to render this field.  This includes making sure
 * that the field's value is valid.
 * @public
 */
Blockly.FieldVariable.prototype.init = function() {
  if (this.fieldGroup_) {
    // Dropdown has already been initialized once.
    return;
  }
  Blockly.FieldVariable.superClass_.init.call(this);

  // TODO (1010): Change from init/initModel to initView/initModel
  this.initModel();
};

/**
 * Initialize the model for this field if it has not already been initialized.
 * If the value has not been set to a variable by the first render, we make up a
 * variable rather than let the value be invalid.
 * @package
 */
Blockly.FieldVariable.prototype.initModel = function() {
  if (this.variable_) {
    return; // Initialization already happened.
  }
  this.workspace_ = this.sourceBlock_.workspace;
  var variable = Blockly.Variables.getOrCreateVariablePackage(
      this.workspace_, null, this.defaultVariableName, this.defaultType_);

  // Don't fire a change event for this setValue.  It would have null as the
  // old value, which is not valid.
  Blockly.Events.disable();
  try {
    this.setValue(variable.getId());
  } finally {
    Blockly.Events.enable();
  }
};

/**
 * Dispose of this field.
 * @public
 */
Blockly.FieldVariable.prototype.dispose = function() {
  Blockly.FieldVariable.superClass_.dispose.call(this);
  this.workspace_ = null;
  this.variableMap_ = null;
};

/**
 * Attach this field to a block.
 * @param {!Blockly.Block} block The block containing this field.
 */
Blockly.FieldVariable.prototype.setSourceBlock = function(block) {
  goog.asserts.assert(!block.isShadow(),
      'Variable fields are not allowed to exist on shadow blocks.');
  Blockly.FieldVariable.superClass_.setSourceBlock.call(this, block);
};

/**
 * Get the variable's ID.
 * @return {string} Current variable's ID.
 */
Blockly.FieldVariable.prototype.getValue = function() {
  return this.variable_ ? this.variable_.getId() : null;
};

/**
 * Get the text from this field, which is the selected variable's name.
 * @return {string} The selected variable's name, or the empty string if no
 *     variable is selected.
 */
Blockly.FieldVariable.prototype.getText = function() {
  return this.variable_ ? this.variable_.name : '';
};

/**
 * Get the variable model for the selected variable.
 * Not guaranteed to be in the variable map on the workspace (e.g. if accessed
 * after the variable has been deleted).
 * @return {?Blockly.VariableModel} the selected variable, or null if none was
 *     selected.
 * @package
 */
Blockly.FieldVariable.prototype.getVariable = function() {
  return this.variable_;
};

/**
 * Set the variable ID.
 * @param {string} id New variable ID, which must reference an existing
 *     variable.
 */
Blockly.FieldVariable.prototype.setValue = function(id) {
  var workspace = this.sourceBlock_.workspace;
  var variable = Blockly.Variables.getVariable(workspace, id);

  if (!variable) {
    throw new Error('Variable id doesn\'t point to a real variable!  ID was ' +
        id);
  }
  // Type checks!
  var type = variable.type;
  if (!this.typeIsAllowed_(type)) {
    throw new Error('Variable type doesn\'t match this field!  Type was ' +
        type);
  }
  if (this.sourceBlock_ && Blockly.Events.isEnabled()) {
    var oldValue = this.variable_ ? this.variable_.getId() : null;
    Blockly.Events.fire(new Blockly.Events.BlockChange(
        this.sourceBlock_, 'field', this.name, oldValue, id));
  }
  this.variable_ = variable;
  this.value_ = id;
  this.setText(variable.name);
};

/**
 * Check whether the given variable type is allowed on this field.
 * @param {string} type The type to check.
 * @return {boolean} True if the type is in the list of allowed types.
 * @private
 */
Blockly.FieldVariable.prototype.typeIsAllowed_ = function(type) {
  var typeList = this.getVariableTypes_();
  if (!typeList) {
    return true; // If it's null, all types are valid.
  }
  for (var i = 0; i < typeList.length; i++) {
    if (type == typeList[i]) {
      return true;
    }
  }
  return false;
};

/**
 * Return a list of variable types to include in the dropdown.
 * @return {!Array.<string>} Array of variable types.
 * @throws {Error} if variableTypes is an empty array.
 * @private
 */
Blockly.FieldVariable.prototype.getVariableTypes_ = function() {
  // TODO (#1513): Try to avoid calling this every time the field is edited.
  var variableTypes = this.variableTypes;
  if (variableTypes === null) {
    // If variableTypes is null, return all variable types.
    if (this.sourceBlock_) {
      var workspace = this.sourceBlock_.workspace;
      return workspace.getVariableTypes();
    }
  }
  variableTypes = variableTypes || [''];
  if (variableTypes.length == 0) {
    // Throw an error if variableTypes is an empty list.
    var name = this.getText();
    throw new Error('\'variableTypes\' of field variable ' +
      name + ' was an empty list');
  }
  return variableTypes;
};

/**
 * Parse the optional arguments representing the allowed variable types and the
 * default variable type.
 * @param {Array.<string>=} opt_variableTypes A list of the types of variables
 *     to include in the dropdown.  If null or undefined, variables of all types
 *     will be displayed in the dropdown.
 * @param {string=} opt_defaultType The type of the variable to create if this
 *     field's value is not explicitly set.  Defaults to ''.
 * @private
 */
Blockly.FieldVariable.prototype.setTypes_ = function(opt_variableTypes,
    opt_defaultType) {
  // If you expected that the default type would be the same as the only entry
  // in the variable types array, tell the Blockly team by commenting on #1499.
  var defaultType = opt_defaultType || '';
  // Set the allowable variable types.  Null means all types on the workspace.
  if (opt_variableTypes == null || opt_variableTypes == undefined) {
    var variableTypes = null;
  } else if (Array.isArray(opt_variableTypes)) {
    var variableTypes = opt_variableTypes;
    // Make sure the default type is valid.
    var isInArray = false;
    for (var i = 0; i < variableTypes.length; i++) {
      if (variableTypes[i] == defaultType) {
        isInArray = true;
      }
    }
    if (!isInArray) {
      throw new Error('Invalid default type \'' + defaultType + '\' in ' +
          'the definition of a FieldVariable');
    }
  } else {
    throw new Error('\'variableTypes\' was not an array in the definition of ' +
        'a FieldVariable');
  }
  // Only update the field once all checks pass.
  this.defaultType_ =  defaultType;
  this.variableTypes = variableTypes;
};

/**
 * Return a sorted list of variable names for variable dropdown menus.
 * Include a special option at the end for creating a new variable name.
 * @return {!Array.<string>} Array of variable names.
 * @this {Blockly.FieldVariable}
 */
Blockly.FieldVariable.dropdownCreate = function() {
  if (!this.variable_) {
    throw new Error('Tried to call dropdownCreate on a variable field with no' +
        ' variable selected.');
  }
  var variableModelList = [];
  var name = this.getText();
  var workspace = null;
  if (this.sourceBlock_) {
    workspace = this.sourceBlock_.workspace;
  }
  if (workspace) {
    var variableTypes = this.getVariableTypes_();
    var variableModelList = [];
    // Get a copy of the list, so that adding rename and new variable options
    // doesn't modify the workspace's list.
    for (var i = 0; i < variableTypes.length; i++) {
      var variableType = variableTypes[i];
      var variables = workspace.getVariablesOfType(variableType);
      variableModelList = variableModelList.concat(variables);
    }
  }
  variableModelList.sort(Blockly.VariableModel.compareByName);

  var options = [];
  for (var i = 0; i < variableModelList.length; i++) {
    // Set the UUID as the internal representation of the variable.
    options[i] = [variableModelList[i].name, variableModelList[i].getId()];
  }
  options.push([Blockly.Msg.RENAME_VARIABLE, Blockly.RENAME_VARIABLE_ID]);
  if (Blockly.Msg.DELETE_VARIABLE) {
    options.push(
        [
          Blockly.Msg.DELETE_VARIABLE.replace('%1', name),
          Blockly.DELETE_VARIABLE_ID
        ]
    );
  }

  return options;
};

/**
 * Handle the selection of an item in the variable dropdown menu.
 * Special case the 'Rename variable...' and 'Delete variable...' options.
 * In the rename case, prompt the user for a new name.
 * @param {!goog.ui.Menu} menu The Menu component clicked.
 * @param {!goog.ui.MenuItem} menuItem The MenuItem selected within menu.
 */
Blockly.FieldVariable.prototype.onItemSelected = function(menu, menuItem) {
  var id = menuItem.getValue();
  if (this.sourceBlock_ && this.sourceBlock_.workspace) {
    var workspace = this.sourceBlock_.workspace;
    if (id == Blockly.RENAME_VARIABLE_ID) {
      // Rename variable.
      Blockly.Variables.renameVariable(workspace, this.variable_);
      return;
    } else if (id == Blockly.DELETE_VARIABLE_ID) {
      // Delete variable.
      workspace.deleteVariableById(this.variable_.getId());
      return;
    }

    // TODO (#1529): Call any validation function, and allow it to override.
  }
  this.setValue(id);
};

Blockly.Field.register('field_variable', Blockly.FieldVariable);
<|endoftext|>


<commit_before>YAHOO.namespace("xbl.fr");
YAHOO.xbl.fr.Currency = {
    _instances: {},

    _getInstance: function(target) {
        var container = YAHOO.util.Dom.getAncestorByClassName(target, "xbl-fr-currency");
        return this._instances[container.id];
    },

    init: function(target) {
        var container = YAHOO.util.Dom.getAncestorByClassName(target, "xbl-fr-currency");
        if (! this._instances[container.id]) {

            // Get information from the DOM
            var xformsInputElement = YAHOO.util.Dom.getElementsByClassName("xbl-fr-currency-xforms-input", null, container)[0];
            var visibleInputElement = YAHOO.util.Dom.getElementsByClassName("xbl-fr-currency-visible-input", null, container)[0];
            var symbolElement = YAHOO.util.Dom.getElementsByClassName("xbl-fr-currency-prefix", null, container)[0];
            var prefix = ORBEON.xforms.Document.getValue(symbolElement.id);
            var digitsAfterDecimalElement = YAHOO.util.Dom.getElementsByClassName("xbl-fr-currency-digits-after-decimal", null, container)[0];
            var digitsAfterDecimal = ORBEON.xforms.Document.getValue(digitsAfterDecimalElement.id);
            var hasFocus = false;

            // Create instance
            var instance = {
                focus: function() {
                    hasFocus = true;
                    visibleInputElement.value = instance.currencyToNumber(visibleInputElement.value);
                },
                blur: function() {
                    hasFocus = false;
                    var cleanString = visibleInputElement.value.replace(new RegExp("[\\s,]", "g"), "");
                    // Remove prefix if at the beginning of the string
                    if (cleanString.indexOf(prefix) == 0)
                        cleanString = cleanString.substring(prefix.length);
                    var cleanNumber = new Number(cleanString).toString();
                    ORBEON.xforms.Document.setValue(xformsInputElement.id, visibleInputElement.value == "" || cleanNumber == "NaN" ? visibleInputElement.value : cleanNumber);
                    visibleInputElement.value = instance.numberToCurrency(visibleInputElement.value);
                },
                currencyToNumber: function(currency) {
                    if (currency.indexOf(prefix) == 0) {
                        var cleaned = currency.substring(prefix.length);
                        cleaned = cleaned.replace(new RegExp("[\\s,]", "g"), "");
                        return isNaN(new Number(cleaned)) ? currency : cleaned;
                    } else {
                        return currency;
                    }
                },
                numberToCurrency: function(number) {
                    var cleaned = number.replace(new RegExp("[\\s,]", "g"), "");
                    if (cleaned == "") {
                        return number;
                    } else {
                        var numberObject = new Number(cleaned);
                        if (isNaN(numberObject)) {
                            return number;
                        } else {
                            var fixed = numberObject.toFixed(digitsAfterDecimal);
                            var parts = fixed.split(".");
                            var regExp = /(\d+)(\d{3})/;
                            while (regExp.test(parts[0])) {
                                parts[0] = parts[0].replace(regExp, "$1" + "," + "$2");
                            }
                            var result = prefix == "" ? "" : prefix + " ";
                            result += parts[0];
                            if (parts.length > 1)
                                result += "." + parts[1];
                            return result;
                        }
                    }
                },
                xformsToVisible: function() {
                    var xformsValue = ORBEON.xforms.Document.getValue(xformsInputElement.id);
                    // If there is an update in the value, and the field already has the focus, just populate with the
                    // XForms value without currency formatting
                    var currencyFormattedValue = this.numberToCurrency(xformsValue);
                    visibleInputElement.value = hasFocus ? this.currencyToNumber(currencyFormattedValue) : currencyFormattedValue;
                },
                readonly: function() {
                    visibleInputElement.disabled = true;
                },
                readwrite: function() {
                    visibleInputElement.disabled = false;
                },
                propertyPrefixChanged: function() {
                    prefix = ORBEON.xforms.Document.getValue(symbolElement.id);
                    instance.xformsToVisible();
                },
                propertyDigitsAfterDecimalChanged: function() {
                    digitsAfterDecimal = ORBEON.xforms.Document.getValue(digitsAfterDecimalElement.id);
                    instance.xformsToVisible();
                }
            };

            // Initialize value of visible input
            instance.xformsToVisible();

            // Register listener
            YAHOO.util.Event.addFocusListener(visibleInputElement, instance.focus);
            YAHOO.util.Event.addBlurListener(visibleInputElement, instance.blur);
            this._instances[container.id] = instance;
        }
    },

    valueChanged:                           function(target) { this._getInstance(target).xformsToVisible(); },
    readonly:                               function(target) { this._getInstance(target).readonly(); },
    readwrite:                              function(target) { this._getInstance(target).readwrite(); },
    propertyPrefixChanged:                  function(target) { this._getInstance(target).propertyPrefixChanged(); },
    propertyDigitsAfterDecimalChanged:      function(target) { this._getInstance(target).propertyDigitsAfterDecimalChanged(); }
};
<commit_msg>Fix [ #314268 ] Currency component: does not initialize properly when re-added to an iteration<commit_after>YAHOO.namespace("xbl.fr");
YAHOO.xbl.fr.Currency = {
    _instances: {},

    _getInstance: function(target) {
        var container = YAHOO.util.Dom.getAncestorByClassName(target, "xbl-fr-currency");
        return this._instances[container.id];
    },

    init: function(target) {
        var container = YAHOO.util.Dom.getAncestorByClassName(target, "xbl-fr-currency");
        var instance = this._instances[container.id];
        if (instance == null || instance.container != container) {

            // Get information from the DOM
            var xformsInputElement = YAHOO.util.Dom.getElementsByClassName("xbl-fr-currency-xforms-input", null, container)[0];
            var visibleInputElement = YAHOO.util.Dom.getElementsByClassName("xbl-fr-currency-visible-input", null, container)[0];
            var symbolElement = YAHOO.util.Dom.getElementsByClassName("xbl-fr-currency-prefix", null, container)[0];
            var prefix = ORBEON.xforms.Document.getValue(symbolElement.id);
            var digitsAfterDecimalElement = YAHOO.util.Dom.getElementsByClassName("xbl-fr-currency-digits-after-decimal", null, container)[0];
            var digitsAfterDecimal = ORBEON.xforms.Document.getValue(digitsAfterDecimalElement.id);
            var hasFocus = false;

            // Create instance
            var instance = {
                container: container,
                focus: function() {
                    hasFocus = true;
                    visibleInputElement.value = instance.currencyToNumber(visibleInputElement.value);
                },
                blur: function() {
                    hasFocus = false;
                    var cleanString = visibleInputElement.value.replace(new RegExp("[\\s,]", "g"), "");
                    // Remove prefix if at the beginning of the string
                    if (cleanString.indexOf(prefix) == 0)
                        cleanString = cleanString.substring(prefix.length);
                    var cleanNumber = new Number(cleanString).toString();
                    ORBEON.xforms.Document.setValue(xformsInputElement.id, visibleInputElement.value == "" || cleanNumber == "NaN" ? visibleInputElement.value : cleanNumber);
                    visibleInputElement.value = instance.numberToCurrency(visibleInputElement.value);
                },
                currencyToNumber: function(currency) {
                    if (currency.indexOf(prefix) == 0) {
                        var cleaned = currency.substring(prefix.length);
                        cleaned = cleaned.replace(new RegExp("[\\s,]", "g"), "");
                        return isNaN(new Number(cleaned)) ? currency : cleaned;
                    } else {
                        return currency;
                    }
                },
                numberToCurrency: function(number) {
                    var cleaned = number.replace(new RegExp("[\\s,]", "g"), "");
                    if (cleaned == "") {
                        return number;
                    } else {
                        var numberObject = new Number(cleaned);
                        if (isNaN(numberObject)) {
                            return number;
                        } else {
                            var fixed = numberObject.toFixed(digitsAfterDecimal);
                            var parts = fixed.split(".");
                            var regExp = /(\d+)(\d{3})/;
                            while (regExp.test(parts[0])) {
                                parts[0] = parts[0].replace(regExp, "$1" + "," + "$2");
                            }
                            var result = prefix == "" ? "" : prefix + " ";
                            result += parts[0];
                            if (parts.length > 1)
                                result += "." + parts[1];
                            return result;
                        }
                    }
                },
                xformsToVisible: function() {
                    var xformsValue = ORBEON.xforms.Document.getValue(xformsInputElement.id);
                    // If there is an update in the value, and the field already has the focus, just populate with the
                    // XForms value without currency formatting
                    var currencyFormattedValue = this.numberToCurrency(xformsValue);
                    visibleInputElement.value = hasFocus ? this.currencyToNumber(currencyFormattedValue) : currencyFormattedValue;
                },
                readonly: function() {
                    visibleInputElement.disabled = true;
                },
                readwrite: function() {
                    visibleInputElement.disabled = false;
                },
                propertyPrefixChanged: function() {
                    prefix = ORBEON.xforms.Document.getValue(symbolElement.id);
                    instance.xformsToVisible();
                },
                propertyDigitsAfterDecimalChanged: function() {
                    digitsAfterDecimal = ORBEON.xforms.Document.getValue(digitsAfterDecimalElement.id);
                    instance.xformsToVisible();
                }
            };

            // Initialize value of visible input
            instance.xformsToVisible();

            // Register listener
            YAHOO.util.Event.addFocusListener(visibleInputElement, instance.focus);
            YAHOO.util.Event.addBlurListener(visibleInputElement, instance.blur);
            this._instances[container.id] = instance;
        }
    },

    valueChanged:                           function(target) { this._getInstance(target).xformsToVisible(); },
    readonly:                               function(target) { this._getInstance(target).readonly(); },
    readwrite:                              function(target) { this._getInstance(target).readwrite(); },
    propertyPrefixChanged:                  function(target) { this._getInstance(target).propertyPrefixChanged(); },
    propertyDigitsAfterDecimalChanged:      function(target) { this._getInstance(target).propertyDigitsAfterDecimalChanged(); }
};
<|endoftext|>


<commit_before>/**
 * @license
 * Visual Blocks Editor
 *
 * Copyright 2012 Google Inc.
 * https://blockly.googlecode.com/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Dropdown input field.  Used for editable titles and variables.
 * In the interests of a consistent UI, the toolbox shares some functions and
 * properties with the context menu.
 * @author fraser@google.com (Neil Fraser)
 */
'use strict';

/**
 * Class for an editable dropdown field.
 * @param {(!Array.<string>|!Function)} menuGenerator An array of options
 *     for a dropdown list, or a function which generates these options.
 * @param {Function} opt_changeHandler A function that is executed when a new
 *     option is selected, with the newly selected value as its sole argument.
 *     If it returns a value, that value (which must be one of the options) will
 *     become selected in place of the newly selected option, unless the return
 *     value is null, in which case the change is aborted.
 * @extends {Blockly.Field}
 * @constructor
 */
Blockly.FieldDropdown = function(menuGenerator, opt_changeHandler) {
  this.menuGenerator_ = menuGenerator;
  this.changeHandler_ = opt_changeHandler;
  this.trimOptions_();
  var firstTuple = this.getOptions_()[0];
  this.value_ = firstTuple[1];

  // Add dropdown arrow: "option ▾" (LTR) or "▾ אופציה" (RTL)
  // Android can't (in 2014) display "▾", so use "▼" instead.
  var arrowChar = Ext.is.Android ? '\u25BC' : '\u25BE';
  this.arrow_ = Blockly.createSvgElement('tspan', {}, null);
  this.arrow_.appendChild(document.createTextNode(
      Blockly.RTL ? arrowChar + ' ' : ' ' + arrowChar));

  // Call parent's constructor.
  Blockly.FieldDropdown.superClass_.constructor.call(this, firstTuple[0]);
};
Blockly.inherits(Blockly.FieldDropdown, Blockly.Field);

/**
 * Horizontal distance that a checkmark ovehangs the dropdown.
 */
Blockly.FieldDropdown.CHECKMARK_OVERHANG = 25;

/**
 * Clone this FieldDropdown.
 * @return {!Blockly.FieldDropdown} The result of calling the constructor again
 *   with the current values of the arguments used during construction.
 */
Blockly.FieldDropdown.prototype.clone = function() {
  return new Blockly.FieldDropdown(this.menuGenerator_, this.changeHandler_);
};

/**
 * Mouse cursor style when over the hotspot that initiates the editor.
 */
Blockly.FieldDropdown.prototype.CURSOR = 'default';

/**
 * Create a dropdown menu under the text.
 * @private
 */
Blockly.FieldDropdown.prototype.showEditor_ = function() {
  Blockly.WidgetDiv.show(this, null);
  var thisField = this;

    var menuCfg = {
        renderTo: Blockly.WidgetDiv.DIV,
        floating: true,
        items:[],
        shrinkWrap: 3,
        minWidth: 30,
        listeners: {
            click: function (menu, item) {
                if (item != null) {
                    var value = item.getId();
                    if (thisField.changeHandler_) {
                        // Call any change handler, and allow it to override.
                        var override = thisField.changeHandler_(value);
                        if (override !== undefined) {
                            value = override;
                        }
                    }
                    if (value !== null) {
                        thisField.setValue(value);
                    }
                }
                Ext.destroy(menu);
            }
            //TODO: Error here. We need to destroy the menu, but the click handler fires after all others!!!
        }
    };

    menuCfg.renderTo = 'blocklyDiv';

  var options = this.getOptions_();
  for (var x = 0; x < options.length; x++) {
    var menuItem = {};
      menuItem.id = options[x][1];          // Language-neutral value.
      menuItem.text = options[x][0];        // Human-readable text.
      if(menuItem.id == this.value_)
          menuItem.iconCls = "x-menu-item-arrow";
//      menuItem.checked = true;
//    console.log("menuItem.setCheckable(true);");
      console.log("menuItem.setChecked(value == this.value_);");
      menuCfg.items.push(menuItem);
  }

  // Record windowSize and scrollOffset before adding menu.
    console.log("var windowSize = goog.dom.getViewportSize();");
    var windowSize = 500;
    console.log("var scrollOffset = goog.style.getViewportPageOffset(document);");
    var scrollOffset = {x:200,y:200};
  var xy = Blockly.getAbsoluteXY_(/** @type {!Element} */ (this.borderRect_));
  var borderBBox = this.borderRect_.getBBox();
//  var div = Blockly.WidgetDiv.DIV;

    var menu = new Ext.menu.Menu(menuCfg);
    menu.showAt(xy.x,xy.y + borderBBox.height);
//  menu.render(div);
//  var menuDom = menu.getElement();
//  Blockly.addClass_(menuDom, 'blocklyDropdownMenu');
  // Record menuSize after adding menu.
/*  console.log("var menuSize = goog.style.getSize(menuDom);");
    var menuSize = {};
    menuSize.width = menu.getWidth();
    menuSize.height = 300;//menu.getHeight();

  // Position the menu.
  // Flip menu vertically if off the bottom.
  if (xy.y + menuSize.height + borderBBox.height >=
      windowSize.height + scrollOffset.y) {
    xy.y -= menuSize.height;
  } else {
    xy.y += borderBBox.height;
  }
  if (Blockly.RTL) {
    xy.x += borderBBox.width;
    xy.x += Blockly.FieldDropdown.CHECKMARK_OVERHANG;
    // Don't go offscreen left.
    if (xy.x < scrollOffset.x + menuSize.width) {
      xy.x = scrollOffset.x + menuSize.width;
    }
  } else {
    xy.x -= Blockly.FieldDropdown.CHECKMARK_OVERHANG;
    // Don't go offscreen right.
    if (xy.x > windowSize.width + scrollOffset.x - menuSize.width) {
      xy.x = windowSize.width + scrollOffset.x - menuSize.width;
    }
  }
  Blockly.WidgetDiv.position(xy.x, xy.y, windowSize, scrollOffset);
  console.log("menu.setAllowAutoFocus(true);");*/
//  menuDom.focus();
};

/**
 * Factor out common words in statically defined options.
 * Create prefix and/or suffix labels.
 * @private
 */
Blockly.FieldDropdown.prototype.trimOptions_ = function() {
  this.prefixField = null;
  this.suffixField = null;
  var options = this.menuGenerator_;
  if (!Ext.isArray(options) || options.length < 2) {
    return;
  }
  var strings = options.map(function(t) {return t[0];});
  var shortest = Blockly.shortestStringLength(strings);
  var prefixLength = Blockly.commonWordPrefix(strings, shortest);
  var suffixLength = Blockly.commonWordSuffix(strings, shortest);
  if (!prefixLength && !suffixLength) {
    return;
  }
  if (shortest <= prefixLength + suffixLength) {
    // One or more strings will entirely vanish if we proceed.  Abort.
    return;
  }
  if (prefixLength) {
    this.prefixField = strings[0].substring(0, prefixLength - 1);
  }
  if (suffixLength) {
    this.suffixField = strings[0].substr(1 - suffixLength);
  }
  // Remove the prefix and suffix from the options.
  var newOptions = [];
  for (var x = 0; x < options.length; x++) {
    var text = options[x][0];
    var value = options[x][1];
    text = text.substring(prefixLength, text.length - suffixLength);
    newOptions[x] = [text, value];
  }
  this.menuGenerator_ = newOptions;
};

/**
 * Return a list of the options for this dropdown.
 * @return {!Array.<!Array.<string>>} Array of option tuples:
 *     (human-readable text, language-neutral name).
 * @private
 */
Blockly.FieldDropdown.prototype.getOptions_ = function() {
  if (Ext.isFunction(this.menuGenerator_)) {
    return this.menuGenerator_.call(this);
  }
  return /** @type {!Array.<!Array.<string>>} */ (this.menuGenerator_);
};

/**
 * Get the language-neutral value from this dropdown menu.
 * @return {string} Current text.
 */
Blockly.FieldDropdown.prototype.getValue = function() {
  return this.value_;
};

/**
 * Set the language-neutral value for this dropdown menu.
 * @param {string} newValue New value to set.
 */
Blockly.FieldDropdown.prototype.setValue = function(newValue) {
  this.value_ = newValue;
  // Look up and display the human-readable text.
  var options = this.getOptions_();
  for (var x = 0; x < options.length; x++) {
    // Options are tuples of human-readable text and language-neutral values.
    if (options[x][1] == newValue) {
      this.setText(options[x][0]);
      return;
    }
  }
  // Value not found.  Add it, maybe it will become valid once set
  // (like variable names).
  this.setText(newValue);
};

/**
 * Set the text in this field.  Trigger a rerender of the source block.
 * @param {?string} text New text.
 */
Blockly.FieldDropdown.prototype.setText = function(text) {
  if (this.sourceBlock_) {
    // Update arrow's colour.
    this.arrow_.style.fill = Blockly.makeColour(this.sourceBlock_.getColour());
  }
  if (text === null) {
    // No change if null.
    return;
  }
  this.text_ = text;
  // Empty the text element.
  Blockly.removeChildren(this.textElement_);
  // Replace whitespace with non-breaking spaces so the text doesn't collapse.
  text = text.replace(/\s/g, Blockly.Field.NBSP);
  if (!text) {
    // Prevent the field from disappearing if empty.
    text = Blockly.Field.NBSP;
  }
  var textNode = document.createTextNode(text);
  this.textElement_.appendChild(textNode);

  // Insert dropdown arrow.
  if (Blockly.RTL) {
    this.textElement_.insertBefore(this.arrow_, this.textElement_.firstChild);
  } else {
    this.textElement_.appendChild(this.arrow_);
  }

  // Cached width is obsolete.  Clear it.
  this.size_.width = 0;

  if (this.sourceBlock_ && this.sourceBlock_.rendered) {
    this.sourceBlock_.render();
    this.sourceBlock_.bumpNeighbours_();
    this.sourceBlock_.workspace.fireChangeEvent();
  }
};

/**
 * Close the dropdown menu if this input is being deleted.
 */
Blockly.FieldDropdown.prototype.dispose = function() {
  Blockly.WidgetDiv.hideIfOwner(this);
  Blockly.FieldDropdown.superClass_.dispose.call(this);
};
<commit_msg>Fix menu delete problem<commit_after>/**
 * @license
 * Visual Blocks Editor
 *
 * Copyright 2012 Google Inc.
 * https://blockly.googlecode.com/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Dropdown input field.  Used for editable titles and variables.
 * In the interests of a consistent UI, the toolbox shares some functions and
 * properties with the context menu.
 * @author fraser@google.com (Neil Fraser)
 */
'use strict';

/**
 * Class for an editable dropdown field.
 * @param {(!Array.<string>|!Function)} menuGenerator An array of options
 *     for a dropdown list, or a function which generates these options.
 * @param {Function} opt_changeHandler A function that is executed when a new
 *     option is selected, with the newly selected value as its sole argument.
 *     If it returns a value, that value (which must be one of the options) will
 *     become selected in place of the newly selected option, unless the return
 *     value is null, in which case the change is aborted.
 * @extends {Blockly.Field}
 * @constructor
 */
Blockly.FieldDropdown = function(menuGenerator, opt_changeHandler) {
  this.menuGenerator_ = menuGenerator;
  this.changeHandler_ = opt_changeHandler;
  this.trimOptions_();
  var firstTuple = this.getOptions_()[0];
  this.value_ = firstTuple[1];

  // Add dropdown arrow: "option ▾" (LTR) or "▾ אופציה" (RTL)
  // Android can't (in 2014) display "▾", so use "▼" instead.
  var arrowChar = Ext.is.Android ? '\u25BC' : '\u25BE';
  this.arrow_ = Blockly.createSvgElement('tspan', {}, null);
  this.arrow_.appendChild(document.createTextNode(
      Blockly.RTL ? arrowChar + ' ' : ' ' + arrowChar));

  // Call parent's constructor.
  Blockly.FieldDropdown.superClass_.constructor.call(this, firstTuple[0]);
};
Blockly.inherits(Blockly.FieldDropdown, Blockly.Field);

/**
 * Horizontal distance that a checkmark ovehangs the dropdown.
 */
Blockly.FieldDropdown.CHECKMARK_OVERHANG = 25;

/**
 * Clone this FieldDropdown.
 * @return {!Blockly.FieldDropdown} The result of calling the constructor again
 *   with the current values of the arguments used during construction.
 */
Blockly.FieldDropdown.prototype.clone = function() {
  return new Blockly.FieldDropdown(this.menuGenerator_, this.changeHandler_);
};

/**
 * Mouse cursor style when over the hotspot that initiates the editor.
 */
Blockly.FieldDropdown.prototype.CURSOR = 'default';

/**
 * Create a dropdown menu under the text.
 * @private
 */
Blockly.FieldDropdown.prototype.showEditor_ = function() {
  Blockly.WidgetDiv.show(this, null);
  var thisField = this;

    var callback = function(item) {
        if (item != null) {
            var value = item.getId();
            if (thisField.changeHandler_) {
                // Call any change handler, and allow it to override.
                var override = thisField.changeHandler_(value);
                if (override !== undefined) {
                    value = override;
                }
            }
            if (value !== null) {
                thisField.setValue(value);
            }
        }
    };

    var menuCfg = {
        renderTo: Blockly.WidgetDiv.DIV,
        floating: true,
        items:[],
        shrinkWrap: 3,
        minWidth: 30,
        listeners: {
            hide: function (menu, item) {
                Ext.destroy(menu);
            }
        }
    };

    menuCfg.renderTo = 'blocklyDiv';

  var options = this.getOptions_();
  for (var x = 0; x < options.length; x++) {
    var menuItem = {};
      menuItem.id = options[x][1];          // Language-neutral value.
      menuItem.text = options[x][0];        // Human-readable text.
      menuItem.handler = callback;
      if(menuItem.id == this.value_)
          menuItem.iconCls = "x-menu-item-arrow";
//      menuItem.checked = true;
//    console.log("menuItem.setCheckable(true);");
      console.log("menuItem.setChecked(value == this.value_);");
      menuCfg.items.push(menuItem);
  }

  // Record windowSize and scrollOffset before adding menu.
    console.log("var windowSize = goog.dom.getViewportSize();");
    var windowSize = 500;
    console.log("var scrollOffset = goog.style.getViewportPageOffset(document);");
    var scrollOffset = {x:200,y:200};
  var xy = Blockly.getAbsoluteXY_(/** @type {!Element} */ (this.borderRect_));
  var borderBBox = this.borderRect_.getBBox();
//  var div = Blockly.WidgetDiv.DIV;

    var menu = new Ext.menu.Menu(menuCfg);
    menu.showAt(xy.x,xy.y + borderBBox.height);
//  menu.render(div);
//  var menuDom = menu.getElement();
//  Blockly.addClass_(menuDom, 'blocklyDropdownMenu');
  // Record menuSize after adding menu.
/*  console.log("var menuSize = goog.style.getSize(menuDom);");
    var menuSize = {};
    menuSize.width = menu.getWidth();
    menuSize.height = 300;//menu.getHeight();

  // Position the menu.
  // Flip menu vertically if off the bottom.
  if (xy.y + menuSize.height + borderBBox.height >=
      windowSize.height + scrollOffset.y) {
    xy.y -= menuSize.height;
  } else {
    xy.y += borderBBox.height;
  }
  if (Blockly.RTL) {
    xy.x += borderBBox.width;
    xy.x += Blockly.FieldDropdown.CHECKMARK_OVERHANG;
    // Don't go offscreen left.
    if (xy.x < scrollOffset.x + menuSize.width) {
      xy.x = scrollOffset.x + menuSize.width;
    }
  } else {
    xy.x -= Blockly.FieldDropdown.CHECKMARK_OVERHANG;
    // Don't go offscreen right.
    if (xy.x > windowSize.width + scrollOffset.x - menuSize.width) {
      xy.x = windowSize.width + scrollOffset.x - menuSize.width;
    }
  }
  Blockly.WidgetDiv.position(xy.x, xy.y, windowSize, scrollOffset);
  console.log("menu.setAllowAutoFocus(true);");*/
//  menuDom.focus();
};

/**
 * Factor out common words in statically defined options.
 * Create prefix and/or suffix labels.
 * @private
 */
Blockly.FieldDropdown.prototype.trimOptions_ = function() {
  this.prefixField = null;
  this.suffixField = null;
  var options = this.menuGenerator_;
  if (!Ext.isArray(options) || options.length < 2) {
    return;
  }
  var strings = options.map(function(t) {return t[0];});
  var shortest = Blockly.shortestStringLength(strings);
  var prefixLength = Blockly.commonWordPrefix(strings, shortest);
  var suffixLength = Blockly.commonWordSuffix(strings, shortest);
  if (!prefixLength && !suffixLength) {
    return;
  }
  if (shortest <= prefixLength + suffixLength) {
    // One or more strings will entirely vanish if we proceed.  Abort.
    return;
  }
  if (prefixLength) {
    this.prefixField = strings[0].substring(0, prefixLength - 1);
  }
  if (suffixLength) {
    this.suffixField = strings[0].substr(1 - suffixLength);
  }
  // Remove the prefix and suffix from the options.
  var newOptions = [];
  for (var x = 0; x < options.length; x++) {
    var text = options[x][0];
    var value = options[x][1];
    text = text.substring(prefixLength, text.length - suffixLength);
    newOptions[x] = [text, value];
  }
  this.menuGenerator_ = newOptions;
};

/**
 * Return a list of the options for this dropdown.
 * @return {!Array.<!Array.<string>>} Array of option tuples:
 *     (human-readable text, language-neutral name).
 * @private
 */
Blockly.FieldDropdown.prototype.getOptions_ = function() {
  if (Ext.isFunction(this.menuGenerator_)) {
    return this.menuGenerator_.call(this);
  }
  return /** @type {!Array.<!Array.<string>>} */ (this.menuGenerator_);
};

/**
 * Get the language-neutral value from this dropdown menu.
 * @return {string} Current text.
 */
Blockly.FieldDropdown.prototype.getValue = function() {
  return this.value_;
};

/**
 * Set the language-neutral value for this dropdown menu.
 * @param {string} newValue New value to set.
 */
Blockly.FieldDropdown.prototype.setValue = function(newValue) {
  this.value_ = newValue;
  // Look up and display the human-readable text.
  var options = this.getOptions_();
  for (var x = 0; x < options.length; x++) {
    // Options are tuples of human-readable text and language-neutral values.
    if (options[x][1] == newValue) {
      this.setText(options[x][0]);
      return;
    }
  }
  // Value not found.  Add it, maybe it will become valid once set
  // (like variable names).
  this.setText(newValue);
};

/**
 * Set the text in this field.  Trigger a rerender of the source block.
 * @param {?string} text New text.
 */
Blockly.FieldDropdown.prototype.setText = function(text) {
  if (this.sourceBlock_) {
    // Update arrow's colour.
    this.arrow_.style.fill = Blockly.makeColour(this.sourceBlock_.getColour());
  }
  if (text === null) {
    // No change if null.
    return;
  }
  this.text_ = text;
  // Empty the text element.
  Blockly.removeChildren(this.textElement_);
  // Replace whitespace with non-breaking spaces so the text doesn't collapse.
  text = text.replace(/\s/g, Blockly.Field.NBSP);
  if (!text) {
    // Prevent the field from disappearing if empty.
    text = Blockly.Field.NBSP;
  }
  var textNode = document.createTextNode(text);
  this.textElement_.appendChild(textNode);

  // Insert dropdown arrow.
  if (Blockly.RTL) {
    this.textElement_.insertBefore(this.arrow_, this.textElement_.firstChild);
  } else {
    this.textElement_.appendChild(this.arrow_);
  }

  // Cached width is obsolete.  Clear it.
  this.size_.width = 0;

  if (this.sourceBlock_ && this.sourceBlock_.rendered) {
    this.sourceBlock_.render();
    this.sourceBlock_.bumpNeighbours_();
    this.sourceBlock_.workspace.fireChangeEvent();
  }
};

/**
 * Close the dropdown menu if this input is being deleted.
 */
Blockly.FieldDropdown.prototype.dispose = function() {
  Blockly.WidgetDiv.hideIfOwner(this);
  Blockly.FieldDropdown.superClass_.dispose.call(this);
};
<|endoftext|>