<commit_before>/* examples.js
* Created by:
* Date: 03/06/14
* Examples, to be fleshed out later
*
* TODO:
* - Add examples
*
* DONE:
*
*/

// Ok, this will be all the menu items right now

// Just so I don't have to keep copying and pasting examples goddamnit
$(document).ready(function() {
	// *** SETUP ***\\
	// --- Lessons --- \\
	var count = 0;
	less.resize();

	// *** EVENT LISTENERS ***\\
	// -- All -- \\
	// Show menu items on hover
	// Get rid of border?
	$(".top-menu").hover(
		function () {
			$(this).children(".menu-items").show();
			$(this).css("background", props.activeNum);
		}
		, function () {
			$(this).children(".menu-items").hide();
			$(this).css("background", "none");
		}
	);
	$("li").hover(
		function () {
			$(this).css({"background": props.activeText});
		},
		function () {
			$(this).css("background", "inherit");
		}
	);

	// --- Examples --- \\
	$("#examples").click(function() {$(this).children("ul").toggle();});
	$("#examples li").click(function() {ex.pasteEx($(this));});

	// --- Lessons --- \\
	// Make it look like the editor is expanding to fill the page
	$("#lessons").click(function () {
		// A way to toggle, since I couldn't figure out .toggle
		// Change it, restore it, and allow the cycle to progress
		if (count%2 == 0){less.openLess();}
		else {less.closeLess();}
		count++
	});
	// If the sim isn't showing, clicking reference (now an "X")
	// should animate it to be shown
	$("#reference").click(function () {
		if (!less.isSim) {less.closeLess();}
	})
	// Make it bold when hovered over
	.hover(function () {
		if (!less.isSim) {$(this).toggleClass("bold");}
	});
});

var ex = {
	pasteEx: function ($thisElem) {
		/* ($ collection) -> None

		Copy the text in the $ collection, paste
		it into the editor, and evaluate
		*/

		// If the simulator isn't there, get it back
		if (!less.isSim) {
			less.closeLess();
			// Wait till animation is over, I think it's
			// not pasting text while sim is hidden
			setTimeout(function () {
				// Replace any text with new text, evaluate
				editor.getSession().setValue($thisElem.text());
				$("#evaluate").trigger("click");
				}
				, less.slideTime + less.fadeTime + 5 // Need +5?
			);  // end of setTimeout
		}  // end if
		// Otherwise do it without delay
		else {
			// Replace any text with new text, evaluate
			editor.getSession().setValue($thisElem.text());
			$("#evaluate").trigger("click");
		}  // end else
	}
};

var less = {
	// Is the simulator showing?
	isSim: true,
	// Has one toggle animation completed?
// [Something to do with "width",["toggle","swing"]] Why isn't this working?
	canToggle: true,
	// Animation times
	fadeTime: 200,
	slideTime: 900,
	// Remember the width of the visualizer
	visWidth: null,
	// Remember #reference's padding
	refPad: null,

	resize: function () {
		/* (None) -> None

		Stores some dimension variables (perhaps move that)
		and sizes the fake ace editor to match the real one
		*/

		// Set the various properties that need remembering
		less.visWidth = $("#visualizer").width();
		less.refPad = $("#reference").css("padding");

		// Elements to same dimensions as ace
		// Whole area and #top-ribbon height same
		$("#scrollable-area").css("height", $(".ace_scroller").outerHeight());
		$("#top-ribbon").css("height", $(".ace_active-line").outerHeight());
		// Corner and textrow height and width same
		$(".num-row").css("height", $(".ace_active-line").outerHeight());
		$(".num-row").css("width", "100%");
		$(".text-row").css("height", $(".ace_active-line").outerHeight());
		$(".text-row").css("width", "100%");
		// Gutter width same
		$("#num-gutter").css("width", $(".ace_gutter").outerWidth());

	},

// Maybe put queue to false
//

	openLess: function () {
		/* (None) -> None

		If less.closeLess is done animating, hide the
		ace editor and resize the visualizer to let
		the fake ace editor (just looks like it, with
		possibly a search bar) fill the width of the page.
		*/
		if (less.canToggle) {
			// Disallow toggling
			less.canToggle = false;
			// Get rid of any text in there
			// Hiding it gets rid of the text anyway
			// editor.getSession().setValue("");

			// Don't know what could have happened between then and now, resize
			// Wish I could put some kind of delay here...
			// less.resize();

			// Show that "lessons" is active
			$("#lessons").css("border", props.activeBorder);

			// // Changing ace editor (as soon as you type text
			// // it gets small again)
			// editor.setShowPrintMargin(false);
			// $($(".ace_content")[0]).css("width","100%");
			// $("#visualizer").animate(
			// 	{"width":["toggle","swing"]},1000, "linear",
			// 	{progress: function () {$("#ace").css("width", $("#editor").innerWidth());}}
			// );

			// Fade out ace fast
			$("#ace").fadeOut(less.fadeTime, function () {
				// Make fake ace relative and flexible width
				$("#scrollable-area").css({position: "relative"});
				// $("#REPL").animate({"height": "930px"});
				// $("#editor").animate({"height": "100%"});
				// $("#scrollable-area").animate({"height": "100%"});
				// Narrow the width of visualizer till it's gone
				$("#visualizer").animate({"width":"0"}
					, less.slideTime, "swing", function () {
						// Re-allow toggling now
						less.canToggle = true;
						// For things that may want to get the simulator back
						less.isSim = false;
						// Reference will be an X to indicate closing
						$("#reference").text("X")
							.css({"padding-right": "5px"});
				});  // end of visualizer animate contraction
			});  // end of ace fadeOut
		}  // end of canToggle
	},

	closeLess: function () {
		/* (None) -> None

		If less.openLess() is done animating, undo what
		that function did.
		*/

		if (less.canToggle) {
			// Disallow toggling
			less.canToggle = false;
			// For things that may want to get the simulator back
			// and to prevent #reference from becoming bold
			// though that doesn't work if it's right at the beginning
			less.isSim = true;

			// Restore reference to whatever it was
			$("#reference").text("tbd")
				.css("padding", less.refPad)
				// Make sure this is removed, otherwise if it was
				// hovered over, it waits till part way through
				// the animation
				.removeClass("bold");
			// Bring the visualizer back
			$("#visualizer").animate({"width":less.visWidth + "px"}
				, less.slideTime, "swing"
				, function () {
					// Then restore our fake to it's factory settings
					// so that ace won't be pushed of the page
					$("#scrollable-area").css({position: "absolute"});
					// Fade ace in, then
	        		$("#ace").fadeIn(less.fadeTime, function () {
						// Set the future "search bar" area to empty again
						$(".text-row").val("");
						// Show that "lessons" is inactive
						$("#lessons").css("border", props.inactiveBorder);
						// Re-allow toggling now
						less.canToggle = true;
	        		});  // end of ace fadeIn
	        });  // end of visualizer animate expansion
		}  // end of canToggle
	},
};
<commit_msg>Examples list doesn't disappear when examples clicked on<commit_after>/* examples.js
* Created by:
* Date: 03/06/14
* Examples, to be fleshed out later
*
* TODO:
* - Add examples
*
* DONE:
*
*/

// Ok, this will be all the menu items right now

// Just so I don't have to keep copying and pasting examples goddamnit
$(document).ready(function() {
	// *** SETUP ***\\
	// --- Lessons --- \\
	var count = 0;
	less.resize();

	// *** EVENT LISTENERS ***\\
	// -- All -- \\
	// Show menu items on hover
	// Get rid of border?
	$(".top-menu").hover(
		function () {
			$(this).children(".menu-items").show();
			$(this).css("background", props.activeNum);
		}
		, function () {
			$(this).children(".menu-items").hide();
			$(this).css("background", "none");
		}
	);
	$("li").hover(
		function () {
			$(this).css({"background": props.activeText});
		},
		function () {
			$(this).css("background", "inherit");
		}
	);

	// --- Examples --- \\
	$("#examples>a").on("click", function() {$(this).children("ul").toggle();});
	$("#examples li").click(function() {ex.pasteEx($(this));});

	// --- Lessons --- \\
	// Make it look like the editor is expanding to fill the page
	$("#lessons").click(function () {
		// A way to toggle, since I couldn't figure out .toggle
		// Change it, restore it, and allow the cycle to progress
		if (count%2 == 0){less.openLess();}
		else {less.closeLess();}
		count++
	});
	// If the sim isn't showing, clicking reference (now an "X")
	// should animate it to be shown
	$("#reference").click(function () {
		if (!less.isSim) {less.closeLess();}
	})
	// Make it bold when hovered over
	.hover(function () {
		if (!less.isSim) {$(this).toggleClass("bold");}
	});
});

var ex = {
	pasteEx: function ($thisElem) {
		/* ($ collection) -> None

		Copy the text in the $ collection, paste
		it into the editor, and evaluate
		*/

		// If the simulator isn't there, get it back
		if (!less.isSim) {
			less.closeLess();
			// Wait till animation is over, I think it's
			// not pasting text while sim is hidden
			setTimeout(function () {
				// Replace any text with new text, evaluate
				editor.getSession().setValue($thisElem.text());
				$("#evaluate").trigger("click");
				}
				, less.slideTime + less.fadeTime + 5 // Need +5?
			);  // end of setTimeout
		}  // end if
		// Otherwise do it without delay
		else {
			// Replace any text with new text, evaluate
			editor.getSession().setValue($thisElem.text());
			$("#evaluate").trigger("click");
		}  // end else
	}
};

var less = {
	// Is the simulator showing?
	isSim: true,
	// Has one toggle animation completed?
// [Something to do with "width",["toggle","swing"]] Why isn't this working?
	canToggle: true,
	// Animation times
	fadeTime: 200,
	slideTime: 900,
	// Remember the width of the visualizer
	visWidth: null,
	// Remember #reference's padding
	refPad: null,

	resize: function () {
		/* (None) -> None

		Stores some dimension variables (perhaps move that)
		and sizes the fake ace editor to match the real one
		*/

		// Set the various properties that need remembering
		less.visWidth = $("#visualizer").width();
		less.refPad = $("#reference").css("padding");

		// Elements to same dimensions as ace
		// Whole area and #top-ribbon height same
		$("#scrollable-area").css("height", $(".ace_scroller").outerHeight());
		$("#top-ribbon").css("height", $(".ace_active-line").outerHeight());
		// Corner and textrow height and width same
		$(".num-row").css("height", $(".ace_active-line").outerHeight());
		$(".num-row").css("width", "100%");
		$(".text-row").css("height", $(".ace_active-line").outerHeight());
		$(".text-row").css("width", "100%");
		// Gutter width same
		$("#num-gutter").css("width", $(".ace_gutter").outerWidth());

	},

// Maybe put queue to false
//

	openLess: function () {
		/* (None) -> None

		If less.closeLess is done animating, hide the
		ace editor and resize the visualizer to let
		the fake ace editor (just looks like it, with
		possibly a search bar) fill the width of the page.
		*/
		if (less.canToggle) {
			// Disallow toggling
			less.canToggle = false;
			// Get rid of any text in there
			// Hiding it gets rid of the text anyway
			// editor.getSession().setValue("");

			// Don't know what could have happened between then and now, resize
			// Wish I could put some kind of delay here...
			// less.resize();

			// Show that "lessons" is active
			$("#lessons").css("border", props.activeBorder);

			// // Changing ace editor (as soon as you type text
			// // it gets small again)
			// editor.setShowPrintMargin(false);
			// $($(".ace_content")[0]).css("width","100%");
			// $("#visualizer").animate(
			// 	{"width":["toggle","swing"]},1000, "linear",
			// 	{progress: function () {$("#ace").css("width", $("#editor").innerWidth());}}
			// );

			// Fade out ace fast
			$("#ace").fadeOut(less.fadeTime, function () {
				// Make fake ace relative and flexible width
				$("#scrollable-area").css({position: "relative"});
				// $("#REPL").animate({"height": "930px"});
				// $("#editor").animate({"height": "100%"});
				// $("#scrollable-area").animate({"height": "100%"});
				// Narrow the width of visualizer till it's gone
				$("#visualizer").animate({"width":"0"}
					, less.slideTime, "swing", function () {
						// Re-allow toggling now
						less.canToggle = true;
						// For things that may want to get the simulator back
						less.isSim = false;
						// Reference will be an X to indicate closing
						$("#reference").text("X")
							.css({"padding-right": "5px"});
				});  // end of visualizer animate contraction
			});  // end of ace fadeOut
		}  // end of canToggle
	},

	closeLess: function () {
		/* (None) -> None

		If less.openLess() is done animating, undo what
		that function did.
		*/

		if (less.canToggle) {
			// Disallow toggling
			less.canToggle = false;
			// For things that may want to get the simulator back
			// and to prevent #reference from becoming bold
			// though that doesn't work if it's right at the beginning
			less.isSim = true;

			// Restore reference to whatever it was
			$("#reference").text("tbd")
				.css("padding", less.refPad)
				// Make sure this is removed, otherwise if it was
				// hovered over, it waits till part way through
				// the animation
				.removeClass("bold");
			// Bring the visualizer back
			$("#visualizer").animate({"width":less.visWidth + "px"}
				, less.slideTime, "swing"
				, function () {
					// Then restore our fake to it's factory settings
					// so that ace won't be pushed of the page
					$("#scrollable-area").css({position: "absolute"});
					// Fade ace in, then
	        		$("#ace").fadeIn(less.fadeTime, function () {
						// Set the future "search bar" area to empty again
						$(".text-row").val("");
						// Show that "lessons" is inactive
						$("#lessons").css("border", props.inactiveBorder);
						// Re-allow toggling now
						less.canToggle = true;
	        		});  // end of ace fadeIn
	        });  // end of visualizer animate expansion
		}  // end of canToggle
	},
};
<|endoftext|>


<commit_before>// @flow
import type { GraphQLFieldConfig } from 'graphql';
import { pageable } from 'relay-cursor-paging';
import { connectionFromArraySlice } from 'graphql-relay';
import _ from 'lodash';
import gravity from '../lib/loaders/gravity';
import cached from './fields/cached';
import { artworkConnection } from './artwork';
import Artist from './artist';
import Image from './image';
import filterArtworks, { filterArtworksArgs } from './filter_artworks';
import { queriedForFieldsOtherThanBlacklisted, parseRelayOptions } from '../lib/helpers';
import { GravityIDFields, NodeInterface } from './object_identification';
import {
  GraphQLObjectType,
  GraphQLString,
  GraphQLNonNull,
  GraphQLList,
  GraphQLInt,
} from 'graphql';

const GeneType = new GraphQLObjectType({
  name: 'Gene',
  interfaces: [NodeInterface],
  isTypeOf: (obj) => _.has(obj, 'family') && _.has(obj, 'browseable'),
  fields: {
    ...GravityIDFields,
    cached,
    filtered_artworks: filterArtworks('gene_id'),
    artworks_connection: {
      type: artworkConnection,
      args: pageable(filterArtworksArgs),
      resolve: ({ id }, options, request, { rootValue: { accessToken } }) => {
        const gravityOptions = parseRelayOptions(options);
        // Do some massaging of the options for ElasticSearch
        gravityOptions.aggregations = options.aggregations || [];
        gravityOptions.aggregations.push('total');
        // Remove medium if we are trying to get all mediums
        if (options.medium === '*') {
          delete gravityOptions.medium;
        }
        // Manually set the gene_id to the current id
        gravityOptions.gene_id = id;
        console.log('gravityOptions.offset', gravityOptions.offset);
        return gravity.with(accessToken)('filter/artworks', gravityOptions)
          .then((response) => {
            console.log('response.aggregations.total.value', response.aggregations.total.value);
            return connectionFromArraySlice(response.hits, options, {
              arrayLength: response.aggregations.total.value,
              sliceStart: gravityOptions.offset,
            });
          });
      },
    },
    href: {
      type: GraphQLString,
      resolve: ({ id }) => `gene/${id}`,
    },
    name: {
      type: GraphQLString,
    },
    description: {
      type: GraphQLString,
    },
    image: Image,
    artists: {
      type: new GraphQLList(Artist.type),
      resolve: ({ id }) => {
        return gravity(`gene/${id}/artists`, {
          exclude_artists_without_artworks: true,
        });
      },
    },
    trending_artists: {
      type: new GraphQLList(Artist.type),
      args: {
        sample: {
          type: GraphQLInt,
        },
      },
      resolve: ({ id }, options) => {
        return gravity(`artists/trending`, {
          gene: id,
        }).then(artists => {
          if (_.has(options, 'sample')) return _.take(_.shuffle(artists), options.sample);
          return artists;
        });
      },
    },
  },
});

const Gene: GraphQLFieldConfig<GeneType, *> = {
  type: GeneType,
  args: {
    id: {
      description: 'The slug or ID of the Gene',
      type: new GraphQLNonNull(GraphQLString),
    },
  },
  resolve: (root, { id }, request, { fieldNodes }) => {
    // If you are just making an artworks call ( e.g. if paginating )
    // do not make a Gravity call for the gene data.
    const blacklistedFields = ['filtered_artworks', 'id'];
    if (!fieldNodes || queriedForFieldsOtherThanBlacklisted(fieldNodes, blacklistedFields)) {
      return gravity(`gene/${id}`);
    }

    // The family and browsable are here so that the type system's `isTypeOf`
    // resolves correctly when we're skipping gravity data
    return { id, family: null, browseable: null };
  },
};

export default Gene;
<commit_msg>Remove logs<commit_after>// @flow
import type { GraphQLFieldConfig } from 'graphql';
import { pageable } from 'relay-cursor-paging';
import { connectionFromArraySlice } from 'graphql-relay';
import _ from 'lodash';
import gravity from '../lib/loaders/gravity';
import cached from './fields/cached';
import { artworkConnection } from './artwork';
import Artist from './artist';
import Image from './image';
import filterArtworks, { filterArtworksArgs } from './filter_artworks';
import { queriedForFieldsOtherThanBlacklisted, parseRelayOptions } from '../lib/helpers';
import { GravityIDFields, NodeInterface } from './object_identification';
import {
  GraphQLObjectType,
  GraphQLString,
  GraphQLNonNull,
  GraphQLList,
  GraphQLInt,
} from 'graphql';

const GeneType = new GraphQLObjectType({
  name: 'Gene',
  interfaces: [NodeInterface],
  isTypeOf: (obj) => _.has(obj, 'family') && _.has(obj, 'browseable'),
  fields: {
    ...GravityIDFields,
    cached,
    filtered_artworks: filterArtworks('gene_id'),
    artworks_connection: {
      type: artworkConnection,
      args: pageable(filterArtworksArgs),
      resolve: ({ id }, options, request, { rootValue: { accessToken } }) => {
        const gravityOptions = parseRelayOptions(options);
        // Do some massaging of the options for ElasticSearch
        gravityOptions.aggregations = options.aggregations || [];
        gravityOptions.aggregations.push('total');
        // Remove medium if we are trying to get all mediums
        if (options.medium === '*') {
          delete gravityOptions.medium;
        }
        // Manually set the gene_id to the current id
        gravityOptions.gene_id = id;
        return gravity.with(accessToken)('filter/artworks', gravityOptions)
          .then((response) => {
            return connectionFromArraySlice(response.hits, options, {
              arrayLength: response.aggregations.total.value,
              sliceStart: gravityOptions.offset,
            });
          });
      },
    },
    href: {
      type: GraphQLString,
      resolve: ({ id }) => `gene/${id}`,
    },
    name: {
      type: GraphQLString,
    },
    description: {
      type: GraphQLString,
    },
    image: Image,
    artists: {
      type: new GraphQLList(Artist.type),
      resolve: ({ id }) => {
        return gravity(`gene/${id}/artists`, {
          exclude_artists_without_artworks: true,
        });
      },
    },
    trending_artists: {
      type: new GraphQLList(Artist.type),
      args: {
        sample: {
          type: GraphQLInt,
        },
      },
      resolve: ({ id }, options) => {
        return gravity(`artists/trending`, {
          gene: id,
        }).then(artists => {
          if (_.has(options, 'sample')) return _.take(_.shuffle(artists), options.sample);
          return artists;
        });
      },
    },
  },
});

const Gene: GraphQLFieldConfig<GeneType, *> = {
  type: GeneType,
  args: {
    id: {
      description: 'The slug or ID of the Gene',
      type: new GraphQLNonNull(GraphQLString),
    },
  },
  resolve: (root, { id }, request, { fieldNodes }) => {
    // If you are just making an artworks call ( e.g. if paginating )
    // do not make a Gravity call for the gene data.
    const blacklistedFields = ['filtered_artworks', 'id'];
    if (!fieldNodes || queriedForFieldsOtherThanBlacklisted(fieldNodes, blacklistedFields)) {
      return gravity(`gene/${id}`);
    }

    // The family and browsable are here so that the type system's `isTypeOf`
    // resolves correctly when we're skipping gravity data
    return { id, family: null, browseable: null };
  },
};

export default Gene;
<|endoftext|>


<commit_before>/**!
 * Star Rating
 *
 * Version: 1.2.0
 * Author: Paul Ryley (http://geminilabs.io)
 * URL: https://github.com/geminilabs/star-rating.js
 * License: MIT
 */

;(function( window, document, undefined )
{
	"use strict";

	var Plugin = function( el, options )
	{
		this.el = el;
		this.options = options;
		this.metadata = this.el.getAttribute( 'data-options' );
		this.stars = 0;

		this.init();
	};

	Plugin.prototype = {

		defaults: {
			clearable  : true,
			clickFn    : null,
			initialText: "Click to Rate",
			showText   : true,
		},

		init: function()
		{
			if( this.el.tagName !== 'SELECT' )return;

			for( var i = 0; i < this.el.length; i++ ) {
				if( this.el[i].value === '' )continue;
				// abort if any value is not numerical
				if( isNaN( parseFloat( this.el[i].value )) || !isFinite( this.el[i].value ))return;
				this.stars++;
			}

			// abort if number of stars is outside the 1-10 range
			if( this.stars < 1 || this.stars > 10 )return;

			this.config = this._extend( {}, this.defaults, this.options, this.metadata );

			this.build();

			this._on( "change", this.el, this.change.bind( this ));
			this._on( "mouseenter", this.wrap, this.enter.bind( this ));
			this._on( "mouseleave", this.wrap, this.leave.bind( this ));
			this._on( "click", this.wrap, this.select.bind( this ));

			this.current = this.el.options[ this.el.selectedIndex ].value;
			this.selected = this.current;
			this.width = this.wrap.offsetWidth;
			this.star = Math.round( this.width / this.stars );

			this.select();

			return this;
		},

		build: function()
		{
			var ordered = {};
			var unordered = {};

			var wrapper = this._createEl( "span", {
				"class": "gl-star-rating",
				"data-star-rating": "",
			});

			this.el.parentNode.insertBefore( wrapper, this.el );
			wrapper.appendChild( this.el );

			this.wrap = this._insertAfterEl( this.el, "span", {
				"class": "gl-star-rating-stars",
			});

			if( this.config.showText ) {
				this.text = this._insertAfterEl( this.wrap, "span", {
					"class": "gl-star-rating-text",
				});
			}

			for( var i = 0; i < this.el.length; i++ ) {
				if( this.el[i].value !== '' ) {
					unordered[ this.el[i].value ] = this.el[i].text;
				}
			}

			Object.keys( unordered ).sort().forEach( function( key ) {
				ordered[key] = unordered[key];
			});

			for( var key in ordered ) {
				this._appendTo( this.wrap, 'span', {
					"data-value": key,
					"data-text": ordered[ key ],
				});
			}
		},

		change: function()
		{
			this.show( this.el.options[ this.el.selectedIndex ].value );
		},

		clear: function( reset )
		{
			if( this.config.clearable ) {
				this.el.value = "";
				this.selected = "";
				this.show(0);
			}
		},

		enter: function()
		{
			var rect = this.wrap.getBoundingClientRect();

			this._on( "mousemove", this.wrap, this.move.bind( this ));
			this.offsetLeft = rect.left + document.body.scrollLeft;
		},

		getIndexFromPosition: function( pageX )
		{
			return Math.min(
				Math.ceil( Math.max( pageX - this.offsetLeft, 1 ) / this.star ),
				this.stars
			);
		},

		leave: function()
		{
			this._off( "mousemove", this.wrap, this.move );
			this.show( this.selected );
		},

		move: function( ev )
		{
			this.show( this.getIndexFromPosition( ev.pageX ));
		},

		select: function( ev )
		{
			var index = this.current;

			if( ev !== undefined ) {
				index = this.getIndexFromPosition( ev.pageX );
				if( this.current !== '' && parseFloat( this.selected ) === index ) {
					this.clear();
					return;
				}
			}

			this.el.value = index;
			this.selected = index;

			this.show( index );

			if( ev !== undefined && typeof this.config.clickFn === 'function' ) {
				this.config.clickFn( this.selected );
			}
		},

		show: function( index )
		{
			if( index < 0 || index === "" ) {
				index = 0;
			}
			if( index > this.stars ) {
				index = this.stars;
			}

			this._removeClass( this.wrap, 's' + ( 10 * this.current ));
			this._addClass( this.wrap, 's' + ( 10 * index ));

			if( this.config.showText ) {
				this.text.textContent = index < 1 ? this.config.initialText : this.wrap.childNodes[ index - 1 ].dataset.text;
			}

			this.current = index;
		},

		_addClass: function( el, className )
		{
			if( el.classList ) el.classList.add( className );
			else if( !this._hasClass( el, className )) el.className += ' ' + className;
		},

		_appendTo: function( el, tag, attributes )
		{
			var newEl = this._createEl( tag, attributes );
			el.innerHTML += newEl.outerHTML;
		},

		_createEl: function( tag, attributes )
		{
			var el = ( typeof tag === 'string' ) ? document.createElement( tag ) : tag;

			attributes = attributes || {};

			for( var key in attributes ) {
				el.setAttribute( key, attributes[ key ] );
			}

			return el;
		},

		_extend: function()
		{
			var args = [].slice.call( arguments );
			var deep = false;
			if( typeof args[0] === 'boolean' ) {
				deep = args.shift();
			}
			var result = args[0];
			var extenders = args.slice(1);
			var len = extenders.length;
			for( var i = 0; i < len; i++ ) {
				var extender = extenders[i];
				for( var key in extender ) {
					var value = extender[ key ];
					if( deep && value && ( typeof value == 'object' )) {
						var base = Array.isArray( value ) ? [] : {};
						result[ key ] = this._extend( true, base, value );
					}
					else {
						result[ key ] = value;
					}
				}
			}
			return result;
		},

		_hasClass: function( el, className )
		{
			if( el.classList ) return el.classList.contains( className );
			else return new RegExp( '\\b' + className + '\\b' ).test( el.className );
		},

		_insertAfterEl: function( el, tag, attributes )
		{
			var newEl = this._createEl( tag, attributes );
			el.parentNode.insertBefore( newEl, el.nextSibling );

			return newEl;
		},

		_off: function( type, el, handler )
		{
			if( el.detachEvent ) el.detachEvent( 'on' + type, handler );
			else el.removeEventListener( type, handler );
		},

		_on: function( type, el, handler )
		{
			if( el.attachEvent ) el.attachEvent( 'on' + type, handler );
			else el.addEventListener( type, handler );
		},

		_removeClass: function( el, className )
		{
			if( el.classList ) el.classList.remove( className );
			else el.className = el.className.replace( new RegExp( '\\b' + className + '\\b', 'g' ), '' );
		},
	};

	Plugin.defaults = Plugin.prototype.defaults;

	if( window.jQuery ) {
		jQuery.fn.starrating = function( options ) {
			return this.each( function() {
				if( !jQuery.data( this, "plugin_starrating" )) {
					jQuery.data( this, "plugin_starrating", new Plugin( this, options ));
				}
			});
		};
	}

	window.StarRating = Plugin;

})( window, document );
<commit_msg>add extend src<commit_after>/**!
 * Star Rating
 *
 * Version: 1.2.0
 * Author: Paul Ryley (http://geminilabs.io)
 * URL: https://github.com/geminilabs/star-rating.js
 * License: MIT
 */

;(function( window, document, undefined )
{
	"use strict";

	var Plugin = function( el, options )
	{
		this.el = el;
		this.options = options;
		this.metadata = this.el.getAttribute( 'data-options' );
		this.stars = 0;

		this.init();
	};

	Plugin.prototype = {

		defaults: {
			clearable  : true,
			clickFn    : null,
			initialText: "Click to Rate",
			showText   : true,
		},

		init: function()
		{
			if( this.el.tagName !== 'SELECT' )return;

			for( var i = 0; i < this.el.length; i++ ) {
				if( this.el[i].value === '' )continue;
				// abort if any value is not numerical
				if( isNaN( parseFloat( this.el[i].value )) || !isFinite( this.el[i].value ))return;
				this.stars++;
			}

			// abort if number of stars is outside the 1-10 range
			if( this.stars < 1 || this.stars > 10 )return;

			this.config = this._extend( {}, this.defaults, this.options, this.metadata );

			this.build();

			this._on( "change", this.el, this.change.bind( this ));
			this._on( "mouseenter", this.wrap, this.enter.bind( this ));
			this._on( "mouseleave", this.wrap, this.leave.bind( this ));
			this._on( "click", this.wrap, this.select.bind( this ));

			this.current = this.el.options[ this.el.selectedIndex ].value;
			this.selected = this.current;
			this.width = this.wrap.offsetWidth;
			this.star = Math.round( this.width / this.stars );

			this.select();

			return this;
		},

		build: function()
		{
			var ordered = {};
			var unordered = {};

			var wrapper = this._createEl( "span", {
				"class": "gl-star-rating",
				"data-star-rating": "",
			});

			this.el.parentNode.insertBefore( wrapper, this.el );
			wrapper.appendChild( this.el );

			this.wrap = this._insertAfterEl( this.el, "span", {
				"class": "gl-star-rating-stars",
			});

			if( this.config.showText ) {
				this.text = this._insertAfterEl( this.wrap, "span", {
					"class": "gl-star-rating-text",
				});
			}

			for( var i = 0; i < this.el.length; i++ ) {
				if( this.el[i].value !== '' ) {
					unordered[ this.el[i].value ] = this.el[i].text;
				}
			}

			Object.keys( unordered ).sort().forEach( function( key ) {
				ordered[key] = unordered[key];
			});

			for( var key in ordered ) {
				this._appendTo( this.wrap, 'span', {
					"data-value": key,
					"data-text": ordered[ key ],
				});
			}
		},

		change: function()
		{
			this.show( this.el.options[ this.el.selectedIndex ].value );
		},

		clear: function( reset )
		{
			if( this.config.clearable ) {
				this.el.value = "";
				this.selected = "";
				this.show(0);
			}
		},

		enter: function()
		{
			var rect = this.wrap.getBoundingClientRect();

			this._on( "mousemove", this.wrap, this.move.bind( this ));
			this.offsetLeft = rect.left + document.body.scrollLeft;
		},

		getIndexFromPosition: function( pageX )
		{
			return Math.min(
				Math.ceil( Math.max( pageX - this.offsetLeft, 1 ) / this.star ),
				this.stars
			);
		},

		leave: function()
		{
			this._off( "mousemove", this.wrap, this.move );
			this.show( this.selected );
		},

		move: function( ev )
		{
			this.show( this.getIndexFromPosition( ev.pageX ));
		},

		select: function( ev )
		{
			var index = this.current;

			if( ev !== undefined ) {
				index = this.getIndexFromPosition( ev.pageX );
				if( this.current !== '' && parseFloat( this.selected ) === index ) {
					this.clear();
					return;
				}
			}

			this.el.value = index;
			this.selected = index;

			this.show( index );

			if( ev !== undefined && typeof this.config.clickFn === 'function' ) {
				this.config.clickFn( this.selected );
			}
		},

		show: function( index )
		{
			if( index < 0 || index === "" ) {
				index = 0;
			}
			if( index > this.stars ) {
				index = this.stars;
			}

			this._removeClass( this.wrap, 's' + ( 10 * this.current ));
			this._addClass( this.wrap, 's' + ( 10 * index ));

			if( this.config.showText ) {
				this.text.textContent = index < 1 ? this.config.initialText : this.wrap.childNodes[ index - 1 ].dataset.text;
			}

			this.current = index;
		},

		_addClass: function( el, className )
		{
			if( el.classList ) el.classList.add( className );
			else if( !this._hasClass( el, className )) el.className += ' ' + className;
		},

		_appendTo: function( el, tag, attributes )
		{
			var newEl = this._createEl( tag, attributes );
			el.innerHTML += newEl.outerHTML;
		},

		_createEl: function( tag, attributes )
		{
			var el = ( typeof tag === 'string' ) ? document.createElement( tag ) : tag;

			attributes = attributes || {};

			for( var key in attributes ) {
				el.setAttribute( key, attributes[ key ] );
			}

			return el;
		},

		/**
		 * https://github.com/angus-c/just#just-extend
		 */
		_extend: function()
		{
			var args = [].slice.call( arguments );
			var deep = false;
			if( typeof args[0] === 'boolean' ) {
				deep = args.shift();
			}
			var result = args[0];
			var extenders = args.slice(1);
			var len = extenders.length;
			for( var i = 0; i < len; i++ ) {
				var extender = extenders[i];
				for( var key in extender ) {
					var value = extender[ key ];
					if( deep && value && ( typeof value == 'object' )) {
						var base = Array.isArray( value ) ? [] : {};
						result[ key ] = this._extend( true, base, value );
					}
					else {
						result[ key ] = value;
					}
				}
			}
			return result;
		},

		_hasClass: function( el, className )
		{
			if( el.classList ) return el.classList.contains( className );
			else return new RegExp( '\\b' + className + '\\b' ).test( el.className );
		},

		_insertAfterEl: function( el, tag, attributes )
		{
			var newEl = this._createEl( tag, attributes );
			el.parentNode.insertBefore( newEl, el.nextSibling );

			return newEl;
		},

		_off: function( type, el, handler )
		{
			if( el.detachEvent ) el.detachEvent( 'on' + type, handler );
			else el.removeEventListener( type, handler );
		},

		_on: function( type, el, handler )
		{
			if( el.attachEvent ) el.attachEvent( 'on' + type, handler );
			else el.addEventListener( type, handler );
		},

		_removeClass: function( el, className )
		{
			if( el.classList ) el.classList.remove( className );
			else el.className = el.className.replace( new RegExp( '\\b' + className + '\\b', 'g' ), '' );
		},
	};

	Plugin.defaults = Plugin.prototype.defaults;

	if( window.jQuery ) {
		jQuery.fn.starrating = function( options ) {
			return this.each( function() {
				if( !jQuery.data( this, "plugin_starrating" )) {
					jQuery.data( this, "plugin_starrating", new Plugin( this, options ));
				}
			});
		};
	}

	window.StarRating = Plugin;

})( window, document );
<|endoftext|>


<commit_before>var embed = {};

embed.init = function() {
  var messageElements = document.getElementsByClassName("divMessage");

  for (var i = 0; i < messageElements.length; ++i) {
    var linkElements = messageElements[i].getElementsByTagName("a");

    for (var j = 0; j < linkElements.length; ++j)
      embed.processLinkForEmbed(linkElements[j]);
  }
};

embed.processLinkForEmbed = function(linkElement) {
  var url = linkElement.href;
  var domain = embed.getDomain(url);

  if (!domain)
    return;

  var domainSrcFunction = embed.domainFunctionMap[domain];

  if (!domainSrcFunction)
    return;

  var embedSrc = domainSrcFunction(url);

  if (!embedSrc)
    return;

  var embedElement = embed.buildEmbed(650, 350, embedSrc);
  linkElement.parentNode.insertBefore(embedElement, linkElement.nextSibling);
};

embed.buildEmbed = function(width, height, src) {
  var html = '';

  var wrapperElement = document.createElement('div');
  wrapperElement.style.display = 'inline';

  var divElement = document.createElement('div');
  divElement.style.display = 'none';

  var buttonElement = document.createElement('span');
  buttonElement.innerHTML = '[Embed]';
  buttonElement.className = 'embedButton glowOnHover';

  buttonElement.onclick = function() {
    if (divElement.style.display === "none") {
      divElement.style.display = "block";
      divElement.innerHTML = html;
      buttonElement.innerHTML = "[Remove]";
    }

    else {
      divElement.style.display = "none";
      divElement.innerHTML = null;
      buttonElement.innerHTML = "[Embed]";
     }
  };

  // note: append order
  wrapperElement.appendChild(buttonElement);
  wrapperElement.appendChild(divElement);

  return wrapperElement;
};

// substrings to the first occurrence of an input string if present
embed.getUntil = function(string, input) {
  var inputIndex = string.indexOf(input);

  return inputIndex !== -1 ?
	string.substring(0, inputIndex) :
	string;
};

embed.getDomain = function(url) {
  return url.match(/\b(?!www.)\b([a-z0-9]+\.)*[a-z0-9]+\.[a-z]+/i)[0];
};

embed.getSrcYouTubeCommon = function(url, secure, domain) {
  var videoId = url.split('v=')[1];

  if (!videoId)
    return;

  videoId = embed.getUntil(videoId, '&');

  var embedSrc = secure ? 'https' : 'http';
  embedSrc += '://' + domain + '/embed/' + videoId;

  var startTime = embed.getYouTubeStartTime(url);

  if (startTime)
    embedSrc += '?start=' + startTime;

  return embedSrc;
};

embed.getSrcYouTube = function(url) {
  return embed.getSrcYouTubeCommon(url, true, 'www.youtube.com');
};

embed.getSrcInvidious = function(url) {
  return embed.getSrcYouTubeCommon(url, true, 'www.invidio.us');
};

embed.getSrcYouTubeShortened = function(url) {
  var videoId = url.split('/')[3];

  if (!videoId)
    return;

  // trim any params in the url
  videoId = embed.getUntil(videoId, '?');

  var embedSrc = 'https://www.youtube.com/embed/' + videoId;

  var startTime = embed.getYouTubeStartTime(url);

  if (startTime)
    embedSrc += '?start=' + startTime;

  return embedSrc;
};

embed.getSrcBitChute = function(url) {
  if (!url.includes('/video/')) // bitchute has parts of it's main site besides videos
    return;

  var videoId = url.split('/')[4];

  if (!videoId)
    return;

  return 'https://www.bitchute.com/embed/' + videoId;
};

embed.getSrcLiveLeak = function(url) {
  var videoId = url.split('t=')[1];

  if (!videoId)
    return;

  videoId = embed.getUntil(videoId, '&');

  return 'https://www.liveleak.com/e/' + videoId;
};

// translate ?t=XhXmXs to raw seconds (the only input supported by youtube's embed start time)
// todo: recode with loop
embed.getYouTubeStartTime = function(url) {
  var startTime = url.split('t=')[1];

  if (!startTime)
    return;

  startTime = embed.getUntil(startTime, '&');

  var totalSeconds = 0;

  var hours = startTime.match(/(\d+)(?:h)/);

  if (hours)
    totalSeconds += parseInt(hours[1]) * 3600;

  var minutes = startTime.match(/(\d+)(?:m)/);

  if (minutes)
    totalSeconds += parseInt(minutes[1]) * 60;

  var seconds = startTime.match(/(\d+)(?:s)/);

  if (seconds)
    totalSeconds += parseInt(seconds[1]);

  return totalSeconds;
};

embed.domainFunctionMap = {};

embed.domainFunctionMap['youtube.com'] = embed.getSrcYouTube;
embed.domainFunctionMap['youtu.be'] = embed.getSrcYouTubeShortened;
embed.domainFunctionMap['invidio.us'] = embed.getSrcInvidious;
embed.domainFunctionMap['bitchute.com'] = embed.getSrcBitChute;
embed.domainFunctionMap['liveleak.com'] = embed.getSrcLiveLeak;

embed.init();<commit_msg>string declariation consistency<commit_after>var embed = {};

embed.init = function() {
  var messageElements = document.getElementsByClassName("divMessage");

  for (var i = 0; i < messageElements.length; ++i) {
    var linkElements = messageElements[i].getElementsByTagName("a");

    for (var j = 0; j < linkElements.length; ++j)
      embed.processLinkForEmbed(linkElements[j]);
  }
};

embed.processLinkForEmbed = function(linkElement) {
  var url = linkElement.href;
  var domain = embed.getDomain(url);

  if (!domain)
    return;

  var domainSrcFunction = embed.domainFunctionMap[domain];

  if (!domainSrcFunction)
    return;

  var embedSrc = domainSrcFunction(url);

  if (!embedSrc)
    return;

  var embedElement = embed.buildEmbed(650, 350, embedSrc);
  linkElement.parentNode.insertBefore(embedElement, linkElement.nextSibling);
};

embed.buildEmbed = function(width, height, src) {
  var html = '';

  var wrapperElement = document.createElement('div');
  wrapperElement.style.display = 'inline';

  var divElement = document.createElement('div');
  divElement.style.display = 'none';

  var buttonElement = document.createElement('span');
  buttonElement.innerHTML = '[Embed]';
  buttonElement.className = 'embedButton glowOnHover';

  buttonElement.onclick = function() {
    if (divElement.style.display === 'none') {
      divElement.style.display = 'block';
      divElement.innerHTML = html;
      buttonElement.innerHTML = '[Remove]';
    }

    else {
      divElement.style.display = 'none';
      divElement.innerHTML = null;
      buttonElement.innerHTML = '[Embed]';
     }
  };

  // note: append order
  wrapperElement.appendChild(buttonElement);
  wrapperElement.appendChild(divElement);

  return wrapperElement;
};

// substrings to the first occurrence of an input string if present
embed.getUntil = function(string, input) {
  var inputIndex = string.indexOf(input);

  return inputIndex !== -1 ?
	string.substring(0, inputIndex) :
	string;
};

embed.getDomain = function(url) {
  return url.match(/\b(?!www.)\b([a-z0-9]+\.)*[a-z0-9]+\.[a-z]+/i)[0];
};

embed.getSrcYouTubeCommon = function(url, secure, domain) {
  var videoId = url.split('v=')[1];

  if (!videoId)
    return;

  videoId = embed.getUntil(videoId, '&');

  var embedSrc = secure ? 'https' : 'http';
  embedSrc += '://' + domain + '/embed/' + videoId;

  var startTime = embed.getYouTubeStartTime(url);

  if (startTime)
    embedSrc += '?start=' + startTime;

  return embedSrc;
};

embed.getSrcYouTube = function(url) {
  return embed.getSrcYouTubeCommon(url, true, 'www.youtube.com');
};

embed.getSrcInvidious = function(url) {
  return embed.getSrcYouTubeCommon(url, true, 'www.invidio.us');
};

embed.getSrcYouTubeShortened = function(url) {
  var videoId = url.split('/')[3];

  if (!videoId)
    return;

  // trim any params in the url
  videoId = embed.getUntil(videoId, '?');

  var embedSrc = 'https://www.youtube.com/embed/' + videoId;

  var startTime = embed.getYouTubeStartTime(url);

  if (startTime)
    embedSrc += '?start=' + startTime;

  return embedSrc;
};

embed.getSrcBitChute = function(url) {
  if (!url.includes('/video/')) // bitchute has parts of it's main site besides videos
    return;

  var videoId = url.split('/')[4];

  if (!videoId)
    return;

  return 'https://www.bitchute.com/embed/' + videoId;
};

embed.getSrcLiveLeak = function(url) {
  var videoId = url.split('t=')[1];

  if (!videoId)
    return;

  videoId = embed.getUntil(videoId, '&');

  return 'https://www.liveleak.com/e/' + videoId;
};

// translate ?t=XhXmXs to raw seconds (the only input supported by youtube's embed start time)
// todo: recode with loop
embed.getYouTubeStartTime = function(url) {
  var startTime = url.split('t=')[1];

  if (!startTime)
    return;

  startTime = embed.getUntil(startTime, '&');

  var totalSeconds = 0;

  var hours = startTime.match(/(\d+)(?:h)/);

  if (hours)
    totalSeconds += parseInt(hours[1]) * 3600;

  var minutes = startTime.match(/(\d+)(?:m)/);

  if (minutes)
    totalSeconds += parseInt(minutes[1]) * 60;

  var seconds = startTime.match(/(\d+)(?:s)/);

  if (seconds)
    totalSeconds += parseInt(seconds[1]);

  return totalSeconds;
};

embed.domainFunctionMap = {};

embed.domainFunctionMap['youtube.com'] = embed.getSrcYouTube;
embed.domainFunctionMap['youtu.be'] = embed.getSrcYouTubeShortened;
embed.domainFunctionMap['invidio.us'] = embed.getSrcInvidious;
embed.domainFunctionMap['bitchute.com'] = embed.getSrcBitChute;
embed.domainFunctionMap['liveleak.com'] = embed.getSrcLiveLeak;

embed.init();<|endoftext|>


<commit_before>
/*!
 * Express - proto
 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var connect = require('connect')
  , Router = require('./router')
  , toArray = require('./utils').toArray
  , methods = Router.methods.concat('del', 'all')
  , res = require('./response')
  , View = require('./view')
  , url = require('url')
  , utils = connect.utils
  , path = require('path')
  , join = path.join
  , fs = require('fs')
  , qs = require('qs');

/**
 * Expose `HTTPServer`.
 */

var app = exports = module.exports = {};

/**
 * Initialize the server.
 *
 *   - setup default configuration
 *   - setup default middleware
 *   - setup route reflection methods
 *
 * @api private
 */

app.init = function(){
  var self = this;
  this.cache = {};
  this.settings = {};
  this.engines = {};
  this.redirects = {};
  this.isCallbacks = {};
  this.viewCallbacks = [];
  this.defaultConfiguration();

  // route reflection methods
  methods.forEach(function(method){
    self.lookup[method] = function(path){
      return self.routes.lookup(method, path);
    };

    self.match[method] = function(path){
      return self.routes.match(method, path);
    };

    self.remove[method] = function(path){
      return self.routes.lookup(method, path).remove();
    };
  });

  // del -> delete
  self.lookup.del = self.lookup.delete;
  self.match.del = self.match.delete;
  self.remove.del = self.remove.delete;
};

/**
 * Initialize application configuration.
 *
 * @api private
 */

app.defaultConfiguration = function(){
  var self = this;

  // default settings
  this.set('home', '/');
  this.set('env', process.env.NODE_ENV || 'development');

  // always parse req.query
  this.use(connect.query());

  this.locals = function(obj){
    for (var key in obj) self.locals[key] = obj[key];
    return self;
  };

  this.locals.use = function(fn){
    if (3 == fn.length) {
      self.viewCallbacks.push(fn);
    } else {
      self.viewCallbacks.push(function(req, res, done){
        fn(req, res);
        done();
      });
    }
    return this;
  };

  // expose objects to each other
  this.use(function(req, res, next){
    var charset;
    res.setHeader('X-Powered-By', 'Express');
    req.app = res.app = self;
    req.res = res;
    res.req = req;
    req.next = next;

    // charset
    if (charset = self.set('charset')) res.charset = charset;

    res.locals = function(obj){
      for (var key in obj) res.locals[key] = obj[key];
      return res;
    };

    next();
  });

  // router
  this.routes = new Router(this);
  this.routeList = this.routes.routes;
  this.__defineGetter__('router', function(){
    this.__usedRouter = true;
    return self.routes.middleware;
  });

  // default locals
  this.locals.settings = this.settings;

  // default production configuration
  this.configure('production', function(){
    this.enable('view cache');
  });
};

/**
 * Remove routes matching the given `path`.
 *
 * @param {Route} path
 * @return {Boolean}
 * @api public
 */

app.remove = function(path){
  return this.routes.lookup('all', path).remove();
};

/**
 * Lookup routes defined with a path
 * equivalent to `path`.
 *
 * @param {Stirng} path
 * @return {Array}
 * @api public
 */

app.lookup = function(path){
  return this.routes.lookup('all', path);
};

/**
 * Lookup routes matching the given `url`.
 *
 * @param {Stirng} url
 * @return {Array}
 * @api public
 */

app.match = function(url){
  return this.routes.match('all', url);
};

/**
 * Proxy `connect.HTTPServer#use()` to apply settings to
 * mounted applications.
 *
 * @param {String|Function|Server} route
 * @param {Function|Server} middleware
 * @return {Server} for chaining
 * @api public
 */

app.use = function(route, middleware){
  var app, home, handle;

  if ('string' != typeof route) {
    middleware = route, route = '/';
  }

  // express app
  if (middleware.handle && middleware.set) app = middleware;

  // restore .app property on req and res
  if (app) {
    app.route = route;
    middleware = function(req, res, next) {
      var orig = req.app;
      app.handle(req, res, function(err){
        req.app = res.app = orig;
        next(err);
      });
    };
  }

  connect.proto.use.call(this, route, middleware);

  // mounted an app, invoke the hook
  // and adjust some settings
  if (app) {
    base = this.set('basepath') || this.route;
    if ('/' == base) base = '';
    base = base + (app.set('basepath') || app.route);
    app.set('basepath', base);
    app.parent = this;
    if (app.__mounted) app.__mounted.call(app, this);
  }

  return this;
};

/**
 * Assign a callback `fn` which is called
 * when this `Server` is passed to `Server#use()`.
 *
 * Examples:
 *
 *    var app = express.createServer()
 *      , blog = express.createServer();
 *
 *    blog.mounted(function(parent){
 *      // parent is app
 *      // "this" is blog
 *    });
 *
 *    app.use(blog);
 *
 * @param {Function} fn
 * @return {Server} for chaining
 * @api public
 */

app.mounted = function(fn){
  this.__mounted = fn;
  return this;
};

/**
 * Register the given template engine callback `fn`
 * as `ext`. For example we may wish to map ".html"
 * files to ejs rather than using the ".ejs" extension.
 *
 *    app.register('.html', require('ejs').render);
 *
 * or
 *
 *    app.register('html', require('ejs').render);
 *
 * @param {String} ext
 * @param {Function} fn
 * @return {Server} for chaining
 * @api public
 */

app.register = function(ext, fn){
  if ('.' != ext[0]) ext = '.' + ext;
  this.engines[ext] = fn;
  return this;
};

/**
 * Map the given param placeholder `name`(s) to the given callback(s).
 *
 * Param mapping is used to provide pre-conditions to routes
 * which us normalized placeholders. This callback has the same
 * signature as regular middleware, for example below when ":userId"
 * is used this function will be invoked in an attempt to load the user.
 *
 *      app.param('userId', function(req, res, next, id){
 *        User.find(id, function(err, user){
 *          if (err) {
 *            next(err);
 *          } else if (user) {
 *            req.user = user;
 *            next();
 *          } else {
 *            next(new Error('failed to load user'));
 *          }
 *        });
 *      });
 *
 * Passing a single function allows you to map logic
 * to the values passed to `app.param()`, for example
 * this is useful to provide coercion support in a concise manner.
 *
 * The following example maps regular expressions to param values
 * ensuring that they match, otherwise passing control to the next
 * route:
 *
 *      app.param(function(name, regexp){
 *        if (regexp instanceof RegExp) {
 *          return function(req, res, next, val){
 *            var captures;
 *            if (captures = regexp.exec(String(val))) {
 *              req.params[name] = captures;
 *              next();
 *            } else {
 *              next('route');
 *            }
 *          }
 *        }
 *      });
 *
 * We can now use it as shown below, where "/commit/:commit" expects
 * that the value for ":commit" is at 5 or more digits. The capture
 * groups are then available as `req.params.commit` as we defined
 * in the function above.
 *
 *    app.param('commit', /^\d{5,}$/);
 *
 * For more of this useful functionality take a look
 * at [express-params](http://github.com/visionmedia/express-params).
 *
 * @param {String|Array|Function} name
 * @param {Function} fn
 * @return {Server} for chaining
 * @api public
 */

app.param = function(name, fn){
  var self = this
    , fns = [].slice.call(arguments, 1);

  // array
  if (Array.isArray(name)) {
    name.forEach(function(name){
      fns.forEach(function(fn){
        self.param(name, fn);
      });
    });
  // param logic
  } else if ('function' == typeof name) {
    this.routes.param(name);
  // single
  } else {
    if (':' == name[0]) name = name.substr(1);
    fns.forEach(function(fn){
      self.routes.param(name, fn);
    });
  }

  return this;
};

/**
 * Register the given callback `fn` for the given `type`.
 *
 * @param {String} type
 * @param {Function} fn
 * @return {Server} for chaining
 * @api public
 */

app.is = function(type, fn){
  if (!fn) return this.isCallbacks[type];
  this.isCallbacks[type] = fn;
  return this;
};

/**
 * Assign `setting` to `val`, or return `setting`'s value.
 * Mounted servers inherit their parent server's settings.
 *
 * @param {String} setting
 * @param {String} val
 * @return {Server|Mixed} for chaining, or the setting value
 * @api public
 */

app.set = function(setting, val){
  if (val === undefined) {
    if (this.settings.hasOwnProperty(setting)) {
      return this.settings[setting];
    } else if (this.parent) {
      return this.parent.set(setting);
    }
  } else {
    this.settings[setting] = val;
    return this;
  }
};

/**
 * Check if `setting` is enabled.
 *
 * @param {String} setting
 * @return {Boolean}
 * @api public
 */

app.enabled = function(setting){
  return !!this.set(setting);
};

/**
 * Check if `setting` is disabled.
 *
 * @param {String} setting
 * @return {Boolean}
 * @api public
 */

app.disabled = function(setting){
  return !this.set(setting);
};

/**
 * Enable `setting`.
 *
 * @param {String} setting
 * @return {Server} for chaining
 * @api public
 */

app.enable = function(setting){
  return this.set(setting, true);
};

/**
 * Disable `setting`.
 *
 * @param {String} setting
 * @return {Server} for chaining
 * @api public
 */

app.disable = function(setting){
  return this.set(setting, false);
};

/**
 * Redirect `key` to `url`.
 *
 * @param {String} key
 * @param {String} url
 * @return {Server} for chaining
 * @api public
 */

app.redirect = function(key, url){
  this.redirects[key] = url;
  return this;
};

/**
 * Configure callback for zero or more envs,
 * when no env is specified that callback will
 * be invoked for all environments. Any combination
 * can be used multiple times, in any order desired.
 *
 * Examples:
 *
 *    app.configure(function(){
 *      // executed for all envs
 *    });
 *
 *    app.configure('stage', function(){
 *      // executed staging env
 *    });
 *
 *    app.configure('stage', 'production', function(){
 *      // executed for stage and production
 *    });
 *
 * @param {String} env...
 * @param {Function} fn
 * @return {Server} for chaining
 * @api public
 */

app.configure = function(env, fn){
  var envs = 'all'
    , args = toArray(arguments);
  fn = args.pop();
  if (args.length) envs = args;
  if ('all' == envs || ~envs.indexOf(this.settings.env)) fn.call(this);
  return this;
};

/**
 * Delegate `.VERB(...)` calls to `.route(VERB, ...)`.
 */

methods.forEach(function(method){
  app[method] = function(path){
    if ('get' == method && 1 == arguments.length) return this.set(path);
    var args = [method].concat(toArray(arguments));
    if (!this.__usedRouter) this.use(this.router);
    return this.routes._route.apply(this.routes, args);
  }
});

/**
 * Special-cased "all" method, applying the given route `path`,
 * middleware, and callback to _every_ HTTP method.
 *
 * @param {String} path
 * @param {Function} ...
 * @return {Server} for chaining
 * @api public
 */

app.all = function(path){
  var args = arguments;
  methods.forEach(function(method){
    if ('all' == method) return;
    app[method].apply(this, args);
  }, this);
  return this;
};

// del -> delete alias

app.del = app.delete;

/**
 * Render the given view `name` name with `options`
 * and a callback accepting an error and the
 * rendered template string.
 *
 * @param {String} name
 * @param {String|Function} options or fn
 * @param {Function} fn
 * @api public
 */

app.render = function(name, options, fn){
  var self = this
    , opts = {}
    , cache = this.cache
    , engines = this.engines
    , view;

  // support callback function as second arg
  if ('function' == typeof options) {
    fn = options, options = {};
  }

  // merge app.locals
  utils.merge(opts, this.locals);

  // merge options.locals
  if (options.locals) {
    utils.merge(opts, options.locals);
    delete opts.locals;
  }

  // merge options
  utils.merge(opts, options);

  // set .cache unless explicitly provided
  opts.cache = null == opts.cache
    ? this.enabled('view cache')
    : opts.cache;

  // primed cache
  if (opts.cache) view = cache[name];

  // view
  if (!view) {
    view = new View(name, {
        defaultEngine: this.set('view engine')
      , root: this.set('views') || process.cwd() + '/views'
      , engines: engines
    });

    // prime the cache
    if (opts.cache) cache[name] = view;
  }

  // render
  try {
    view.render(opts, fn);
  } catch (err) {
    fn(err);
  }
};
<commit_msg>Added `app.routes`, replacing `app.routes.all()`. Closes #811<commit_after>
/*!
 * Express - proto
 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var connect = require('connect')
  , Router = require('./router')
  , toArray = require('./utils').toArray
  , methods = Router.methods.concat('del', 'all')
  , res = require('./response')
  , View = require('./view')
  , url = require('url')
  , utils = connect.utils
  , path = require('path')
  , join = path.join
  , fs = require('fs')
  , qs = require('qs');

/**
 * Expose `HTTPServer`.
 */

var app = exports = module.exports = {};

/**
 * Initialize the server.
 *
 *   - setup default configuration
 *   - setup default middleware
 *   - setup route reflection methods
 *
 * @api private
 */

app.init = function(){
  var self = this;
  this.cache = {};
  this.settings = {};
  this.engines = {};
  this.redirects = {};
  this.isCallbacks = {};
  this.viewCallbacks = [];
  this.defaultConfiguration();

  // route reflection methods
  methods.forEach(function(method){
    self.lookup[method] = function(path){
      return self._router.lookup(method, path);
    };

    self.match[method] = function(path){
      return self._router.match(method, path);
    };

    self.remove[method] = function(path){
      return self._router.lookup(method, path).remove();
    };
  });

  // del -> delete
  self.lookup.del = self.lookup.delete;
  self.match.del = self.match.delete;
  self.remove.del = self.remove.delete;
};

/**
 * Initialize application configuration.
 *
 * @api private
 */

app.defaultConfiguration = function(){
  var self = this;

  // default settings
  this.set('home', '/');
  this.set('env', process.env.NODE_ENV || 'development');

  // always parse req.query
  this.use(connect.query());

  this.locals = function(obj){
    for (var key in obj) self.locals[key] = obj[key];
    return self;
  };

  this.locals.use = function(fn){
    if (3 == fn.length) {
      self.viewCallbacks.push(fn);
    } else {
      self.viewCallbacks.push(function(req, res, done){
        fn(req, res);
        done();
      });
    }
    return this;
  };

  // expose objects to each other
  this.use(function(req, res, next){
    var charset;
    res.setHeader('X-Powered-By', 'Express');
    req.app = res.app = self;
    req.res = res;
    res.req = req;
    req.next = next;

    // charset
    if (charset = self.set('charset')) res.charset = charset;

    res.locals = function(obj){
      for (var key in obj) res.locals[key] = obj[key];
      return res;
    };

    next();
  });

  // router
  this._router = new Router(this);
  this.routes = this._router.routes;
  this.__defineGetter__('router', function(){
    this.__usedRouter = true;
    return self._router.middleware;
  });

  // default locals
  this.locals.settings = this.settings;

  // default production configuration
  this.configure('production', function(){
    this.enable('view cache');
  });
};

/**
 * Remove routes matching the given `path`.
 *
 * @param {Route} path
 * @return {Boolean}
 * @api public
 */

app.remove = function(path){
  return this._router.lookup('all', path).remove();
};

/**
 * Lookup routes defined with a path
 * equivalent to `path`.
 *
 * @param {Stirng} path
 * @return {Array}
 * @api public
 */

app.lookup = function(path){
  return this._router.lookup('all', path);
};

/**
 * Lookup routes matching the given `url`.
 *
 * @param {Stirng} url
 * @return {Array}
 * @api public
 */

app.match = function(url){
  return this._router.match('all', url);
};

/**
 * Proxy `connect.HTTPServer#use()` to apply settings to
 * mounted applications.
 *
 * @param {String|Function|Server} route
 * @param {Function|Server} middleware
 * @return {Server} for chaining
 * @api public
 */

app.use = function(route, middleware){
  var app, home, handle;

  if ('string' != typeof route) {
    middleware = route, route = '/';
  }

  // express app
  if (middleware.handle && middleware.set) app = middleware;

  // restore .app property on req and res
  if (app) {
    app.route = route;
    middleware = function(req, res, next) {
      var orig = req.app;
      app.handle(req, res, function(err){
        req.app = res.app = orig;
        next(err);
      });
    };
  }

  connect.proto.use.call(this, route, middleware);

  // mounted an app, invoke the hook
  // and adjust some settings
  if (app) {
    base = this.set('basepath') || this.route;
    if ('/' == base) base = '';
    base = base + (app.set('basepath') || app.route);
    app.set('basepath', base);
    app.parent = this;
    if (app.__mounted) app.__mounted.call(app, this);
  }

  return this;
};

/**
 * Assign a callback `fn` which is called
 * when this `Server` is passed to `Server#use()`.
 *
 * Examples:
 *
 *    var app = express.createServer()
 *      , blog = express.createServer();
 *
 *    blog.mounted(function(parent){
 *      // parent is app
 *      // "this" is blog
 *    });
 *
 *    app.use(blog);
 *
 * @param {Function} fn
 * @return {Server} for chaining
 * @api public
 */

app.mounted = function(fn){
  this.__mounted = fn;
  return this;
};

/**
 * Register the given template engine callback `fn`
 * as `ext`. For example we may wish to map ".html"
 * files to ejs rather than using the ".ejs" extension.
 *
 *    app.register('.html', require('ejs').render);
 *
 * or
 *
 *    app.register('html', require('ejs').render);
 *
 * @param {String} ext
 * @param {Function} fn
 * @return {Server} for chaining
 * @api public
 */

app.register = function(ext, fn){
  if ('.' != ext[0]) ext = '.' + ext;
  this.engines[ext] = fn;
  return this;
};

/**
 * Map the given param placeholder `name`(s) to the given callback(s).
 *
 * Param mapping is used to provide pre-conditions to routes
 * which us normalized placeholders. This callback has the same
 * signature as regular middleware, for example below when ":userId"
 * is used this function will be invoked in an attempt to load the user.
 *
 *      app.param('userId', function(req, res, next, id){
 *        User.find(id, function(err, user){
 *          if (err) {
 *            next(err);
 *          } else if (user) {
 *            req.user = user;
 *            next();
 *          } else {
 *            next(new Error('failed to load user'));
 *          }
 *        });
 *      });
 *
 * Passing a single function allows you to map logic
 * to the values passed to `app.param()`, for example
 * this is useful to provide coercion support in a concise manner.
 *
 * The following example maps regular expressions to param values
 * ensuring that they match, otherwise passing control to the next
 * route:
 *
 *      app.param(function(name, regexp){
 *        if (regexp instanceof RegExp) {
 *          return function(req, res, next, val){
 *            var captures;
 *            if (captures = regexp.exec(String(val))) {
 *              req.params[name] = captures;
 *              next();
 *            } else {
 *              next('route');
 *            }
 *          }
 *        }
 *      });
 *
 * We can now use it as shown below, where "/commit/:commit" expects
 * that the value for ":commit" is at 5 or more digits. The capture
 * groups are then available as `req.params.commit` as we defined
 * in the function above.
 *
 *    app.param('commit', /^\d{5,}$/);
 *
 * For more of this useful functionality take a look
 * at [express-params](http://github.com/visionmedia/express-params).
 *
 * @param {String|Array|Function} name
 * @param {Function} fn
 * @return {Server} for chaining
 * @api public
 */

app.param = function(name, fn){
  var self = this
    , fns = [].slice.call(arguments, 1);

  // array
  if (Array.isArray(name)) {
    name.forEach(function(name){
      fns.forEach(function(fn){
        self.param(name, fn);
      });
    });
  // param logic
  } else if ('function' == typeof name) {
    this._router.param(name);
  // single
  } else {
    if (':' == name[0]) name = name.substr(1);
    fns.forEach(function(fn){
      self._router.param(name, fn);
    });
  }

  return this;
};

/**
 * Register the given callback `fn` for the given `type`.
 *
 * @param {String} type
 * @param {Function} fn
 * @return {Server} for chaining
 * @api public
 */

app.is = function(type, fn){
  if (!fn) return this.isCallbacks[type];
  this.isCallbacks[type] = fn;
  return this;
};

/**
 * Assign `setting` to `val`, or return `setting`'s value.
 * Mounted servers inherit their parent server's settings.
 *
 * @param {String} setting
 * @param {String} val
 * @return {Server|Mixed} for chaining, or the setting value
 * @api public
 */

app.set = function(setting, val){
  if (val === undefined) {
    if (this.settings.hasOwnProperty(setting)) {
      return this.settings[setting];
    } else if (this.parent) {
      return this.parent.set(setting);
    }
  } else {
    this.settings[setting] = val;
    return this;
  }
};

/**
 * Check if `setting` is enabled.
 *
 * @param {String} setting
 * @return {Boolean}
 * @api public
 */

app.enabled = function(setting){
  return !!this.set(setting);
};

/**
 * Check if `setting` is disabled.
 *
 * @param {String} setting
 * @return {Boolean}
 * @api public
 */

app.disabled = function(setting){
  return !this.set(setting);
};

/**
 * Enable `setting`.
 *
 * @param {String} setting
 * @return {Server} for chaining
 * @api public
 */

app.enable = function(setting){
  return this.set(setting, true);
};

/**
 * Disable `setting`.
 *
 * @param {String} setting
 * @return {Server} for chaining
 * @api public
 */

app.disable = function(setting){
  return this.set(setting, false);
};

/**
 * Redirect `key` to `url`.
 *
 * @param {String} key
 * @param {String} url
 * @return {Server} for chaining
 * @api public
 */

app.redirect = function(key, url){
  this.redirects[key] = url;
  return this;
};

/**
 * Configure callback for zero or more envs,
 * when no env is specified that callback will
 * be invoked for all environments. Any combination
 * can be used multiple times, in any order desired.
 *
 * Examples:
 *
 *    app.configure(function(){
 *      // executed for all envs
 *    });
 *
 *    app.configure('stage', function(){
 *      // executed staging env
 *    });
 *
 *    app.configure('stage', 'production', function(){
 *      // executed for stage and production
 *    });
 *
 * @param {String} env...
 * @param {Function} fn
 * @return {Server} for chaining
 * @api public
 */

app.configure = function(env, fn){
  var envs = 'all'
    , args = toArray(arguments);
  fn = args.pop();
  if (args.length) envs = args;
  if ('all' == envs || ~envs.indexOf(this.settings.env)) fn.call(this);
  return this;
};

/**
 * Delegate `.VERB(...)` calls to `.route(VERB, ...)`.
 */

methods.forEach(function(method){
  app[method] = function(path){
    if ('get' == method && 1 == arguments.length) return this.set(path);
    var args = [method].concat(toArray(arguments));
    if (!this.__usedRouter) this.use(this._router);
    return this._router._route.apply(this._router, args);
  }
});

/**
 * Special-cased "all" method, applying the given route `path`,
 * middleware, and callback to _every_ HTTP method.
 *
 * @param {String} path
 * @param {Function} ...
 * @return {Server} for chaining
 * @api public
 */

app.all = function(path){
  var args = arguments;
  methods.forEach(function(method){
    if ('all' == method) return;
    app[method].apply(this, args);
  }, this);
  return this;
};

// del -> delete alias

app.del = app.delete;

/**
 * Render the given view `name` name with `options`
 * and a callback accepting an error and the
 * rendered template string.
 *
 * @param {String} name
 * @param {String|Function} options or fn
 * @param {Function} fn
 * @api public
 */

app.render = function(name, options, fn){
  var self = this
    , opts = {}
    , cache = this.cache
    , engines = this.engines
    , view;

  // support callback function as second arg
  if ('function' == typeof options) {
    fn = options, options = {};
  }

  // merge app.locals
  utils.merge(opts, this.locals);

  // merge options.locals
  if (options.locals) {
    utils.merge(opts, options.locals);
    delete opts.locals;
  }

  // merge options
  utils.merge(opts, options);

  // set .cache unless explicitly provided
  opts.cache = null == opts.cache
    ? this.enabled('view cache')
    : opts.cache;

  // primed cache
  if (opts.cache) view = cache[name];

  // view
  if (!view) {
    view = new View(name, {
        defaultEngine: this.set('view engine')
      , root: this.set('views') || process.cwd() + '/views'
      , engines: engines
    });

    // prime the cache
    if (opts.cache) cache[name] = view;
  }

  // render
  try {
    view.render(opts, fn);
  } catch (err) {
    fn(err);
  }
};
<|endoftext|>


<commit_before>// Remove label before colon.
function stripColon(text) {
    var i = text.indexOf(":");
    if (i >= 0) {
        text = text.substr(i).trim();
        if (text[1] == " ") {
            text = text.substr(1).trim();
        }
    }
    return text;
}

// Split right label
function splitLabel(text) {
    var label = "", right = "";
    var i = text.lastIndexOf(":");
    if (i >= 0) {
        right = text.substr(i + 1).trim();
        if (right) {
            text = text.substr(0, i);

            i = text.lastIndexOf(" ");
            if (i >= 0) {
                label = text.substr(i + 1);
                if (label) {
                    text = text.substr(0, i);
                }
            }
        }
    }
    return [text, label, right];
}

var escapeTable = {
    "<": "<",
    "&": "&"
};

function escapeRepl(match) {
    return escapeTable[match];
}

// escape character to html entity
function escape(s) {
    // this should be safe since there is no CJK contains "&", "<"
    return s.replace(/<|&/g, escapeRepl);
}

function Span(f, b, l) {
    this.f = f;
    this.b = b;
    this.l = l;
    this.flash = false;
    this.halfStart = false;
    this.halfEnd = false;
    this.text = "";
    this.i = 0;
}

Span.prototype.toString = function() {
    if (!this.text) {
        return "";
    }

    var cls = "f" + this.f + " b" + this.b,
        w = this.text.length;

    if (this.l) {
        cls += " l";
    }
    if (this.flash) {
        cls += " flash";
    }
    if (this.halfStart) {
        cls += " half-start";
        w--;
    }
    if (this.halfEnd) {
        cls += " half-end";
        w--;
    }
    return "" + escape(this.text) + "";
};

Span.prototype.copy = function() {
    var span = new Span(this.f, this.b, this.l);
    span.flash = this.flash;
    return span;
};

Span.prototype.reset = function() {
    this.f = 7;
    this.b = 0;
    this.l = false;
    this.flash = false;
}

// create head line
function makeHead(lLabel, lText, rLabel, rText) {
    if (!lText) {
        return "";
    }

    var fillSpace = 78, result = "
";

    fillSpace -= 2 + lLabel.length + 1 + lText.length;

    if (rText) {
        fillSpace -= 2 + rLabel.length + 2 + rText.length;
    }

    if (fillSpace < 0) {
        fillSpace = 0;
    }

    var span = new Span(4, 7, false);

    span.text = " " + lLabel + " ";

    result += span.toString();

    span.f = 7;
    span.b = 4;
    span.text = " " + lText + " ".repeat(fillSpace);

    result += span.toString();

    // result += makeSpan({f:4,b:7,text:" "+lLabel+" "}) + makeSpan({f:7,b:4,text:" "+lText+" ".repeat(fillSpace)});

    if (rText) {
        span.f = 4;
        span.b = 7;
        span.text = " " + rLabel + " ";

        result += span.toString();

        span.f = 7;
        span.b = 4;
        span.text = " " + rText + " ";

        result += span.toString();
        // result += makeSpan({f:4,b:7,text:" "+rLabel+" "}) + makeSpan({f:7,b:4,text:" "+rText+" "});
    }

    result += "
";

    return result;
}

// extract text to color
function extractColor(text, i, color) {
    var re = /\033\[([\d;]*)m/g;
    re.lastIndex = i;
    var matches = [],
        match;
    do {
        match = re.exec(text);
        if (!match || match.index != i) {
            break;
        }
        matches.push(match);
        i = re.lastIndex;
    } while (text[re.lastIndex] == "\x1b");

    if (!matches.length) {
        return null;
    }

    var tokens = matches.map(function(match){
        return match[1].split(";");
    });

    tokens = Array.prototype.concat.apply([], tokens);

    var span = color.copy();
    span.i = re.lastIndex;

    var code;

    for (i = 0; i < tokens.length; i++) {
        code = +tokens[i];
        if (code == 0) {
            span.reset();
        } else if (code == 1) {
            span.l = true;
        } else if (code == 5) {
            span.flash = true;
        } else if (code == 7) {
            var t = span.f;
            span.f = span.b;
            span.b = t;
        } else if (code < 40) {
            span.f = code - 30;
        } else if (code < 50) {
            span.b = code - 40;
        }
    }
    return span;
}

// convert ansi string into html
function bbsReader(data) {
    var i = 0, match, result = "";

    var author, title, time, label, board;

    if ((match = /^(\xa7@\xaa\xcc:.*)\n(.*)\n(.*)\n/.exec(data))) {
        // draw header
        author = stripColon(match[1]);
        title = stripColon(match[2]);
        time = stripColon(match[3]);

        // find board
        var t = splitLabel(author);
        author = t[0];
        label = t[1];
        board = t[2];

        // 作者
        result += makeHead("\xa7@\xaa\xcc", author, label, board);
        // 標題
        result += makeHead("\xbc\xd0\xc3D", title);
        // 時間
        result += makeHead("\xae\xc9\xb6\xa1", time);

        // ─
        result += "
" + "\xa2w".repeat(39) + "
";

        i += match[0].length;
    }

    var span = new Span(7, 0, false),
        pos = 0, cleanLine = false, cjk = false;

    result += "
";

    for (; i < data.length; i++) {
        // Special color
        if (pos == 0) {
            var ch = data.substr(i, 2);
            if (ch == "\xa1\xb0") {
                // ※
                cleanLine = true;
                span.reset();
                span.f = 2;
            } else if (ch == ": ") {
                // :
                cleanLine = true;
                span.reset();
                span.f = 6;
            }
        }
        if (data[i] == "\x1b") {
            // ESC
            var span2 = extractColor(data, i, span);
            if (!span2) {
                span.text += data[i];
                pos++;
            } else if (cjk && data[span2.i] != "\n") {
                span.text += data[span2.i];
                span.halfEnd = true;

                result += span.toString();

                span2.text += span.text.substring(span.text.length - 2);
                span2.halfStart = true;

                pos++;
                i = span2.i;
                span = span2;
                cjk = false;
            } else {
                cjk = false;
                result += span.toString();
                i = span2.i - 1;
                span = span2;
            }
        } else if (data[i] == "\r") {
            continue;
        } else if (data[i] == "\n") {
            result += span.toString() + "

";
            span.text = "";
            span.halfStart = false;
            span.halfEnd = false;
            cjk = false;

            if (cleanLine) {
                span.reset();
                cleanLine = false;
            }

            pos = 0;
        } else {
            if (cjk) {
                cjk = false;
            } else if (data.charCodeAt(i) & 0x80) {
                cjk = true;
            }
            span.text += data[i];
            pos++;
        }
    }

    result += span.toString() + "
";

    return {
        html: result,
        title: title,
        author: author,
        time: time
    };
}

module.exports = bbsReader;
<commit_msg>Consider CR character<commit_after>// Remove label before colon.
function stripColon(text) {
    var i = text.indexOf(":");
    if (i >= 0) {
        text = text.substr(i).trim();
        if (text[1] == " ") {
            text = text.substr(1).trim();
        }
    }
    return text;
}

// Split right label
function splitLabel(text) {
    var label = "", right = "";
    var i = text.lastIndexOf(":");
    if (i >= 0) {
        right = text.substr(i + 1).trim();
        if (right) {
            text = text.substr(0, i);

            i = text.lastIndexOf(" ");
            if (i >= 0) {
                label = text.substr(i + 1);
                if (label) {
                    text = text.substr(0, i);
                }
            }
        }
    }
    return [text, label, right];
}

var escapeTable = {
    "<": "<",
    "&": "&"
};

function escapeRepl(match) {
    return escapeTable[match];
}

// escape character to html entity
function escape(s) {
    // this should be safe since there is no CJK contains "&", "<"
    return s.replace(/<|&/g, escapeRepl);
}

function Span(f, b, l) {
    this.f = f;
    this.b = b;
    this.l = l;
    this.flash = false;
    this.halfStart = false;
    this.halfEnd = false;
    this.text = "";
    this.i = 0;
}

Span.prototype.toString = function() {
    if (!this.text) {
        return "";
    }

    var cls = "f" + this.f + " b" + this.b,
        w = this.text.length;

    if (this.l) {
        cls += " l";
    }
    if (this.flash) {
        cls += " flash";
    }
    if (this.halfStart) {
        cls += " half-start";
        w--;
    }
    if (this.halfEnd) {
        cls += " half-end";
        w--;
    }
    return "" + escape(this.text) + "";
};

Span.prototype.copy = function() {
    var span = new Span(this.f, this.b, this.l);
    span.flash = this.flash;
    return span;
};

Span.prototype.reset = function() {
    this.f = 7;
    this.b = 0;
    this.l = false;
    this.flash = false;
}

// create head line
function makeHead(lLabel, lText, rLabel, rText) {
    if (!lText) {
        return "";
    }

    var fillSpace = 78, result = "
";

    fillSpace -= 2 + lLabel.length + 1 + lText.length;

    if (rText) {
        fillSpace -= 2 + rLabel.length + 2 + rText.length;
    }

    if (fillSpace < 0) {
        fillSpace = 0;
    }

    var span = new Span(4, 7, false);

    span.text = " " + lLabel + " ";

    result += span.toString();

    span.f = 7;
    span.b = 4;
    span.text = " " + lText + " ".repeat(fillSpace);

    result += span.toString();

    // result += makeSpan({f:4,b:7,text:" "+lLabel+" "}) + makeSpan({f:7,b:4,text:" "+lText+" ".repeat(fillSpace)});

    if (rText) {
        span.f = 4;
        span.b = 7;
        span.text = " " + rLabel + " ";

        result += span.toString();

        span.f = 7;
        span.b = 4;
        span.text = " " + rText + " ";

        result += span.toString();
        // result += makeSpan({f:4,b:7,text:" "+rLabel+" "}) + makeSpan({f:7,b:4,text:" "+rText+" "});
    }

    result += "
";

    return result;
}

// extract text to color
function extractColor(text, i, color) {
    var re = /\033\[([\d;]*)m/g;
    re.lastIndex = i;
    var matches = [],
        match;
    do {
        match = re.exec(text);
        if (!match || match.index != i) {
            break;
        }
        matches.push(match);
        i = re.lastIndex;
    } while (text[re.lastIndex] == "\x1b");

    if (!matches.length) {
        return null;
    }

    var tokens = matches.map(function(match){
        return match[1].split(";");
    });

    tokens = Array.prototype.concat.apply([], tokens);

    var span = color.copy();
    span.i = re.lastIndex;

    var code;

    for (i = 0; i < tokens.length; i++) {
        code = +tokens[i];
        if (code == 0) {
            span.reset();
        } else if (code == 1) {
            span.l = true;
        } else if (code == 5) {
            span.flash = true;
        } else if (code == 7) {
            var t = span.f;
            span.f = span.b;
            span.b = t;
        } else if (code < 40) {
            span.f = code - 30;
        } else if (code < 50) {
            span.b = code - 40;
        }
    }
    return span;
}

// convert ansi string into html
function bbsReader(data) {
    var i = 0, match, result = "";

    var author, title, time, label, board;

    if ((match = /^(\xa7@\xaa\xcc:.*)\n(.*)\n(.*)\n/.exec(data))) {
        // draw header
        author = stripColon(match[1]);
        title = stripColon(match[2]);
        time = stripColon(match[3]);

        // find board
        var t = splitLabel(author);
        author = t[0];
        label = t[1];
        board = t[2];

        // 作者
        result += makeHead("\xa7@\xaa\xcc", author, label, board);
        // 標題
        result += makeHead("\xbc\xd0\xc3D", title);
        // 時間
        result += makeHead("\xae\xc9\xb6\xa1", time);

        // ─
        result += "
" + "\xa2w".repeat(39) + "
";

        i += match[0].length;
    }

    var span = new Span(7, 0, false),
        pos = 0, cleanLine = false, cjk = false;

    result += "
";

    for (; i < data.length; i++) {
        // Special color
        if (pos == 0) {
            var ch = data.substr(i, 2);
            if (ch == "\xa1\xb0") {
                // ※
                cleanLine = true;
                span.reset();
                span.f = 2;
            } else if (ch == ": ") {
                // :
                cleanLine = true;
                span.reset();
                span.f = 6;
            }
        }
        if (data[i] == "\x1b") {
            // ESC
            var span2 = extractColor(data, i, span);
            if (!span2) {
                span.text += data[i];
                pos++;
            } else if (cjk && data[span2.i] != "\n") {
                span.text += data[span2.i];
                span.halfEnd = true;

                result += span.toString();

                span2.text += span.text.substring(span.text.length - 2);
                span2.halfStart = true;

                pos++;
                i = span2.i;
                span = span2;
                cjk = false;
            } else {
                cjk = false;
                result += span.toString();
                i = span2.i - 1;
                span = span2;
            }
        } else if (data[i] == "\r" && data[i + 1] == "\n") {
            continue;
        } else if (data[i] == "\r" || data[i] == "\n") {
            result += span.toString() + "

";
            span.text = "";
            span.halfStart = false;
            span.halfEnd = false;
            cjk = false;

            if (cleanLine) {
                span.reset();
                cleanLine = false;
            }

            pos = 0;
        } else {
            if (cjk) {
                cjk = false;
            } else if (data.charCodeAt(i) & 0x80) {
                cjk = true;
            }
            span.text += data[i];
            pos++;
        }
    }

    result += span.toString() + "
";

    return {
        html: result,
        title: title,
        author: author,
        time: time
    };
}

module.exports = bbsReader;
<|endoftext|>


<commit_before>var storageElement = null;

function withElement(id, f, timeout) {
  if (typeof timeout === "undefined") {
    timeout = 100;
  }

  let element = document.getElementById(id);
  if (element == null) {
    var expiration = performance.now() + timeout;

    requestAnimationFrame(
      function(now) {
        withElementHelp(id, f, expiration, now);
      }
    );
  } else {
    f(element);
  }
}

function withElementHelp(id, f, expiration, now) {
  var element = document.getElementById(id);

  if (element == null) {
    if (now < expiration) {
      requestAnimationFrame(
        function(now) {
          withElementHelp(id, f, expiration, now);
        }
      );
    } else {
      console.log("Gave up waiting for \"" + id + "\" to appear")
    }
  } else {
    f(element);
  }
}

var harpExists = false;
var dragPos = null;

function setHarpExistence(newHarpExistence) {
  if (newHarpExistence != harpExists) {
    harpExists = newHarpExistence;

    if (harpExists) {
      withElement("harp", initHarp);
    }
  }
}

function initHarp(harp) {
  harp.addEventListener("mousedown", harpClicked);
  dragPos = null;
}

function harpClicked(event) {
  event.preventDefault();

  var rect = event.target.getBoundingClientRect();
  dragPos = {
    x: (event.clientX - rect.left) / rect.width,
    y: (event.clientY - rect.top) / rect.height
  }
}

var translationRegex = /translate\s*\(\s*([^\s,)]+)[ ,]/;

function harpDragged(event) {
  if (dragPos != null) {
    var harp = document.getElementById("harp");
    if (harp != null) {
      var rect = harp.getBoundingClientRect();

      var oldPos = dragPos;
      dragPos = {
        x: (event.clientX - rect.left) / rect.width,
        y: (event.clientY - rect.top) / rect.height
      }

      var upper = oldPos;
      var lower = dragPos;
      if (dragPos.y < oldPos.y) {
        upper = dragPos;
        lower = oldPos;
      }

      if (upper.y < 1 && lower.y > 0) {
        var highX = upper.x;
        if (upper.y < 0) {
          highX =
            (upper.x * lower.y - lower.x * upper.y) /
              (lower.y - upper.y);
        }

        var lowX = lower.x;
        if (lower.y > 1) {
          lowX =
            (upper.x * (lower.y - 1) - lower.x * (upper.y - 1)) /
              (lower.y - upper.y);
        }

        var viewBox = harp.viewBox.baseVal;

        var xStart = viewBox.x + Math.min(lowX, highX) * viewBox.width;
        var xStop = viewBox.x + Math.max(lowX, highX) * viewBox.width;

        var pitches = [];

        var ropes = harp.getElementsByClassName("rope");
        for (var i = 0; i < ropes.length; i++) {
          var transform = ropes[i].getAttribute("transform");
          var coordinates = translationRegex.exec(transform);
          var x = parseFloat(coordinates[1]);
          if (x >= xStart && x < xStop) {
            var pitch = parseInt(ropes[i].getAttribute("name"));
            pitches.push(pitch);
          }
        }

        if (pitches.length > 0) {
          app.ports.harpPlucked.send(
            { now: ac.currentTime,
              pitches: pitches
            }
          );
        }
      }
    }
  }
}

function harpReleased(event) {
  dragPos = null;
}

window.addEventListener("mousemove", harpDragged);

window.addEventListener("mouseup", harpReleased);
<commit_msg>make edge happy<commit_after>var storageElement = null;

function withElement(id, f, timeout) {
  if (typeof timeout === "undefined") {
    timeout = 100;
  }

  let element = document.getElementById(id);
  if (element == null) {
    var expiration = performance.now() + timeout;

    requestAnimationFrame(
      function(now) {
        withElementHelp(id, f, expiration, now);
      }
    );
  } else {
    f(element);
  }
}

function withElementHelp(id, f, expiration, now) {
  var element = document.getElementById(id);

  if (element == null) {
    if (now < expiration) {
      requestAnimationFrame(
        function(now) {
          withElementHelp(id, f, expiration, now);
        }
      );
    } else {
      console.log("Gave up waiting for \"" + id + "\" to appear")
    }
  } else {
    f(element);
  }
}

var harpExists = false;
var dragPos = null;

function setHarpExistence(newHarpExistence) {
  if (newHarpExistence != harpExists) {
    harpExists = newHarpExistence;

    if (harpExists) {
      withElement("harp", initHarp);
    }
  }
}

function initHarp(harp) {
  harp.addEventListener("mousedown", harpClicked);
  dragPos = null;
}

function harpClicked(event) {
  event.preventDefault();

  var rect = event.target.getBoundingClientRect();
  dragPos = {
    x: (event.clientX - rect.left) / rect.width,
    y: (event.clientY - rect.top) / rect.height
  }
}

var translationRegex = /translate\s*\(\s*([^\s,)]+)[ ,)]/;

function harpDragged(event) {
  if (dragPos != null) {
    var harp = document.getElementById("harp");
    if (harp != null) {
      var rect = harp.getBoundingClientRect();

      var oldPos = dragPos;
      dragPos = {
        x: (event.clientX - rect.left) / rect.width,
        y: (event.clientY - rect.top) / rect.height
      }

      var upper = oldPos;
      var lower = dragPos;
      if (dragPos.y < oldPos.y) {
        upper = dragPos;
        lower = oldPos;
      }

      if (upper.y < 1 && lower.y > 0) {
        var highX = upper.x;
        if (upper.y < 0) {
          highX =
            (upper.x * lower.y - lower.x * upper.y) /
              (lower.y - upper.y);
        }

        var lowX = lower.x;
        if (lower.y > 1) {
          lowX =
            (upper.x * (lower.y - 1) - lower.x * (upper.y - 1)) /
              (lower.y - upper.y);
        }

        var viewBox = harp.viewBox.baseVal;

        var xStart = viewBox.x + Math.min(lowX, highX) * viewBox.width;
        var xStop = viewBox.x + Math.max(lowX, highX) * viewBox.width;

        var pitches = [];

        var ropes = harp.getElementsByClassName("rope");
        for (var i = 0; i < ropes.length; i++) {
          var transform = ropes[i].getAttribute("transform");
          var coordinates = translationRegex.exec(transform);
          var x = parseFloat(coordinates[1]);
          if (x >= xStart && x < xStop) {
            var pitch = parseInt(ropes[i].getAttribute("name"));
            pitches.push(pitch);
          }
        }

        if (pitches.length > 0) {
          app.ports.harpPlucked.send(
            { now: ac.currentTime,
              pitches: pitches
            }
          );
        }
      }
    }
  }
}

function harpReleased(event) {
  dragPos = null;
}

window.addEventListener("mousemove", harpDragged);

window.addEventListener("mouseup", harpReleased);
<|endoftext|>


<commit_before>describe('idai field app', function() {

    function createObject(fun) {

        clickCreateObject()
                .then(selectTypeObject)
                .then(typeInIdentifier)
                .then(saveObject)
                .then(function(){fun();});
    }

    function clickCreateObject() {
        return element(by.id('object-overview-button-create-object')).click()
    }

    function selectTypeObject() {
        return element(by.id('create-object-option-0')).click();
    }

    function typeInIdentifier() {
        return element(by.id('object-edit-input-identifier')).clear().sendKeys('1').sendKeys('2');
    }

    function saveObject() {
        return element(by.id('object-edit-button-save-object')).click();
    }

    beforeEach(function(){
        browser.get('/');
    });

    it('should create a new object of first listed type ', function() {
        createObject(function(){
            expect(element(by.id('object-overview-identifier-0')).getText()).toEqual("12");
        });
    });

    it('should warn if an existing id is used ', function() {
        createObject(function(){

            createObject(function() {
                expect(element(by.id('message-0')).getText()).
                    toEqual("Objekt Identifier existiert bereits. Bei Klick auf ein anderes Objekt wird der ursprüngliche Zustand wiederhergestellt.");
            });
        });
    });
});
<commit_msg>Improve readability further.<commit_after>describe('idai field app', function() {

    function createObject() {

        return clickCreateObject()
                .then(selectTypeObject)
                .then(typeInIdentifier)
                .then(saveObject);
    }

    function clickCreateObject() {
        return element(by.id('object-overview-button-create-object')).click()
    }

    function selectTypeObject() {
        return element(by.id('create-object-option-0')).click();
    }

    function typeInIdentifier() {
        return element(by.id('object-edit-input-identifier')).clear().sendKeys('1').sendKeys('2');
    }

    function saveObject() {
        return element(by.id('object-edit-button-save-object')).click();
    }

    beforeEach(function(){
        browser.get('/');
    });

    it('should create a new object of first listed type ', function() {
        createObject()
            .then(function(){
                expect(element(by.id('object-overview-identifier-0')).getText()).toEqual("12");
            });
    });

    fit('should warn if an existing id is used ', function() {
        createObject()
            .then(createObject)
            .then(function(){
                expect(element(by.id('message-0')).getText()).
                    toEqual("Objekt Identifier existiert bereits. Bei Klick auf ein anderes Objekt wird der ursprüngliche Zustand wiederhergestellt.");
            });
    });
});
<|endoftext|>


<commit_before><commit_msg>use window.* for explicitly referring to a global<commit_after><|endoftext|>