<commit_before>var Q = require('q');
var Twit = require('twit');

var cinema = require('../models/theater');
var schedule = require('../models/schedule');
var models = require('../models');
var utils = require('../helpers/utils');
var config = require('../config');
var match = require('../helpers/match')
var tweetBuilder = require('./tweetBuilder');

var T = new Twit(config.twitter);

var removeHashTag = function(text) {
  return text.replace(/#\w+/i, '');
};

var removeUser = function(text) {
  return text.replace(/@\w/i, '')
};

var parseFieldsFromTweet = function(tweet, fields) {
  var parsed = {};
  var mapFields = {
    user_id: 'user.id',
    id: 'id',
    name: 'user.name',
    screen_name: 'user.screen_name',
    text: 'text',
    hashtags: 'entities.hashtags'
  };

  fields.forEach(function(k) {
    if (k in mapFields) {
      parsed[k] = utils.getField(tweet, mapFields[k], 'none '+k);
    }
  })

  return parsed;
};

var getLocation = function(id) {
  console.log(getLocation);
  return Q.promise(function (resolve, reject) {
    T.get('statuses/show/:id', {id: id}, function (err, data, response) {
      console.log(err, id);
      console.log(data);
    });
  });
};

var findScheduleAndPost = function(msg, screen_name) {

  match.parseQueryFromTweet(msg)
    .then(models.schedule.getOne)
    .then(function(data) {
      if(!data.schedule) {
        data.schedule = 'No schedule found';
      };
      var posts = tweetBuilder.createPosts(screen_name, data);
      posts.forEach(function(post) {
        postTweet(post);
      })
    });
};

var follow = function(id, cb) {
  T.post('friendships/create', {user_id: id, follow: true},
    function(err, data, response) {
      if (err) return cb('follow ' + err);
      cb(null);
    }
  )
};

var handleNewTweet = function(tweet) {

  var tweetFields = parseFieldsFromTweet(tweet,
                     ['id','user_id', 'name', 'screen_name', 'text', 'hashtags']);
  var msg = tweetFields.text;
  if (msg.indexOf('boterino123') < 0 && msg.indexOf(config.hashtag) < 0) {
    return;
  }
  console.log('------------------on tweet --------------------------');
  followTweetUser(tweetFields.user_id, function(err) {
    if (err) return console.log(err);
    handleMessage(tweetFields);
  });
};

var extractValFromHashtag = function(text, regex) {
  var matches = text.match(regex);
  if (!matches) return '';
  return text.match(regex)[0].replace(/#[a-z] /, '').trim();
};

var extractMovieAndCinema = function(text) {
  return {
    movie: extractValFromHashtag(text, /#m [^#]*/),
    cinema: extractValFromHashtag(text, /#c [^#]*/)
  }
};

var tweetMovieTimes = function(to, timeInfo) {
  tweetBuilder
    .createPosts(to, timeInfo)
    .forEach(postTweet);
};

var tweetMoviesFromCinema = function(to, cinema) {
  var fields = {
    cinema: cinema.realName,
    movies: cinema.movies
      .map(function(movie) {
        return movie.realName;
      })
      .join(' ,')
  };
  tweetBuilder
    .moviesPost(to, fields)
    .forEach(postTweet);
};

var messages = {
  NOT_FOUND: 'No data found'
};

var chooseResponse = function(to, matches) {
  if (!matches.movie && !matches.theater) {
    postTweet(tweetBuilder.createSimpleMsg(to, message.NOT_FOUND));
  } else if (!matches.movie && matches.theater) {
    cinema
      .getMovies(matches.theater)
      .then(utils.partial(tweetMoviesFromCinema, to));
  } if (matches.movie && matches.theater) {
    schedule.getOne(matches)
      .then(utils.partial(tweetMovieTimes, to));
  }
};

var handleMessage = function(tweetFields) {
  var queryFields = extractMovieAndCinema(tweetFields.text);
  match.findMovieTimes(queryFields)
    .then(utils.partial(chooseResponse, tweetFields.screen_name));
};

var listenStream = function(hashtag) {
  var stream = T.stream('statuses/filter', { track: hashtag});
  stream.on('tweet', handleNewTweet);
  T.stream('user').on('tweet', handleNewTweet);
};

var postTweet = function(message) {
  T.post('statuses/update', {status: message},
    function(err, data, response) {
      if (err) return console.log(err);
      console.log('posted', message);
    }
  )
};
/*
var mongoose = require('mongoose');
var config = require('../config');

mongoose.connect(config.mongoConnection);

handleMessage({screen_name: 'ggas', text: '#c basadre #m magallanes'});

module.exports = listenStream;
listenStream.listenStream = listenStream;
*/
<commit_msg>refactor: create function on each response condition<commit_after>var Q = require('q');
var Twit = require('twit');

var cinema = require('../models/theater');
var schedule = require('../models/schedule');
var models = require('../models');
var utils = require('../helpers/utils');
var config = require('../config');
var match = require('../helpers/match')
var tweetBuilder = require('./tweetBuilder');

var T = new Twit(config.twitter);

var removeHashTag = function(text) {
  return text.replace(/#\w+/i, '');
};

var removeUser = function(text) {
  return text.replace(/@\w/i, '')
};

var parseFieldsFromTweet = function(tweet, fields) {
  var parsed = {};
  var mapFields = {
    user_id: 'user.id',
    id: 'id',
    name: 'user.name',
    screen_name: 'user.screen_name',
    text: 'text',
    hashtags: 'entities.hashtags'
  };

  fields.forEach(function(k) {
    if (k in mapFields) {
      parsed[k] = utils.getField(tweet, mapFields[k], 'none '+k);
    }
  })

  return parsed;
};

var getLocation = function(id) {
  console.log(getLocation);
  return Q.promise(function (resolve, reject) {
    T.get('statuses/show/:id', {id: id}, function (err, data, response) {
      console.log(err, id);
      console.log(data);
    });
  });
};

var findScheduleAndPost = function(msg, screen_name) {

  match.parseQueryFromTweet(msg)
    .then(models.schedule.getOne)
    .then(function(data) {
      if(!data.schedule) {
        data.schedule = 'No schedule found';
      };
      var posts = tweetBuilder.createPosts(screen_name, data);
      posts.forEach(function(post) {
        postTweet(post);
      })
    });
};

var follow = function(id, cb) {
  T.post('friendships/create', {user_id: id, follow: true},
    function(err, data, response) {
      if (err) return cb('follow ' + err);
      cb(null);
    }
  )
};

var handleNewTweet = function(tweet) {

  var tweetFields = parseFieldsFromTweet(tweet,
                     ['id','user_id', 'name', 'screen_name', 'text', 'hashtags']);
  var msg = tweetFields.text;
  if (msg.indexOf('boterino123') < 0 && msg.indexOf(config.hashtag) < 0) {
    return;
  }
  console.log('------------------on tweet --------------------------');
  followTweetUser(tweetFields.user_id, function(err) {
    if (err) return console.log(err);
    handleMessage(tweetFields);
  });
};

var extractValFromHashtag = function(text, regex) {
  var matches = text.match(regex);
  if (!matches) return '';
  return text.match(regex)[0].replace(/#[a-z] /, '').trim();
};

var extractMovieAndCinema = function(text) {
  return {
    movie: extractValFromHashtag(text, /#m [^#]*/),
    cinema: extractValFromHashtag(text, /#c [^#]*/)
  }
};

var tweetMovieTimes = function(to, timeInfo) {
  tweetBuilder
    .createPosts(to, timeInfo)
    .forEach(postTweet);
};

var tweetMoviesFromCinema = function(to, cinema) {
  var fields = {
    cinema: cinema.realName,
    movies: cinema.movies
      .map(function(movie) {
        return movie.realName;
      })
      .join(' ,')
  };
  tweetBuilder
    .moviesPost(to, fields)
    .forEach(postTweet);
};

var messages = {
  NOT_FOUND: 'Not found, this is how u do it';
};

var noMatches = function(matches) {
  return !matches.movie && !matches.theater;
};

var sendTweetNotMatches = function(to) {
  postTweet(tweetBuilder.createSimpleMsg(to, message.NOT_FOUND));
};

var sendTweetMovieList = function(to, matches) {
  cinema
    .getMovies(matches.theater)
    .then(utils.partial(tweetMoviesFromCinema, to));
};

var sendTweetMovieTimes = function(to, matches) {
  schedule.getOne(matches)
    .then(utils.partial(tweetMovieTimes, to));
};

var chooseResponse = function(to, matches) {
  if (!matches.movie && !matches.theater) {
    sendTweetNotMatches(to);
  } else if (!matches.movie && matches.theater) {
    sendTweetMovieList(to, matches);
  } if (matches.movie && matches.theater) {
    sendTweetMovieTimes(to, matches);
  }
};

var handleMessage = function(tweetFields) {
  var queryFields = extractMovieAndCinema(tweetFields.text);
  match.findMovieTimes(queryFields)
    .then(utils.partial(chooseResponse, tweetFields.screen_name));
};

var listenStream = function(hashtag) {
  var stream = T.stream('statuses/filter', { track: hashtag});
  stream.on('tweet', handleNewTweet);
  T.stream('user').on('tweet', handleNewTweet);
};

var postTweet = function(message) {
  T.post('statuses/update', {status: message},
    function(err, data, response) {
      if (err) return console.log(err);
      console.log('posted', message);
    }
  )
};
/*
var mongoose = require('mongoose');
var config = require('../config');

mongoose.connect(config.mongoConnection);

handleMessage({screen_name: 'ggas', text: '#c basadre #m magallanes'});

module.exports = listenStream;
listenStream.listenStream = listenStream;
*/
<|endoftext|>


<commit_before>var db = require('../helpers/db');
var _ = require('underscore');

var express = require('express');
var router = express.Router();

router.get('/', function(req, res) {
  var query = "SELECT * FROM continent";

  db.query(query)
  .then(function(result) {
    res.status(200).send(result.rows);
  })
  .catch(function(error) {
    res.status(500).send(error);
  });
});

router.post('/', function(req, res) {
  var name = req.body.name;

  if(!(_.isString(name))) {
    res.status(400).send("Bad Request: no name parameter");
    return;
  }

  var query = 'INSERT INTO continents ("name") VALUES ($1) RETURNING continent_id';
  var values = [name];

  db.query(query, values)
  .then(function(result) {
    res.status(201).send(result.rows[0]);
  })
  .catch(function(error) {
    res.status(500).send(error);
  });
});

router.use('/:id', function(req, res, next) {
  var id = Number(req.params.id);

  if(_.isNumber(id)) {
    next();
  } else {
    res.status(400).send("Value should be a number");
  }
});

router.get('/:id', function(req, res) {
  var query = "SELECT * FROM continents WHERE continent_id = $1";
  var values = [req.params.id];

  db.query(query, values)
  .then(function(result) {
    res.status(200).send(result.rows[0]);
  })
  .catch(function(error) {
    res.status(500).send(error);
  });
});

router.put('/:id', function(req, res) {
  var name = req.body.name;

  if(!(_.isString(name))) {
    res.status(400).send("Bad Request: no name parameter");
    return;
  }

  var query = "UPDATE continents SET name = $1 WHERE continent_id = $2";
  var values = [name, req.params.id];

  db.query(query, values)
  .then(function(result) {
    res.status(200).end();
  })
  .catch(function(error) {
    res.status(500).send(error);
  })
});

module.exports = router;
<commit_msg>Create delete method<commit_after>var db = require('../helpers/db');
var _ = require('underscore');

var express = require('express');
var router = express.Router();

router.get('/', function(req, res) {
  var query = "SELECT * FROM continent";

  db.query(query)
  .then(function(result) {
    res.status(200).send(result.rows);
  })
  .catch(function(error) {
    res.status(500).send(error);
  });
});

router.post('/', function(req, res) {
  var name = req.body.name;

  if(!(_.isString(name))) {
    res.status(400).send("Bad Request: no name parameter");
    return;
  }

  var query = 'INSERT INTO continents ("name") VALUES ($1) RETURNING continent_id';
  var values = [name];

  db.query(query, values)
  .then(function(result) {
    res.status(201).send(result.rows[0]);
  })
  .catch(function(error) {
    res.status(500).send(error);
  });
});

router.use('/:id', function(req, res, next) {
  var id = Number(req.params.id);

  if(_.isNumber(id)) {
    next();
  } else {
    res.status(400).send("Value should be a number");
  }
});

router.get('/:id', function(req, res) {
  var query = "SELECT * FROM continents WHERE continent_id = $1";
  var values = [req.params.id];

  db.query(query, values)
  .then(function(result) {
    res.status(200).send(result.rows[0]);
  })
  .catch(function(error) {
    res.status(500).send(error);
  });
});

router.put('/:id', function(req, res) {
  var name = req.body.name;

  if(!(_.isString(name))) {
    res.status(400).send("Bad Request: no name parameter");
    return;
  }

  var query = "UPDATE continents SET name = $1 WHERE continent_id = $2";
  var values = [name, req.params.id];

  db.query(query, values)
  .then(function(result) {
    res.status(200).end();
  })
  .catch(function(error) {
    res.status(500).send(error);
  })
});

router.delete('/:id', function(req, res) {
  var query = "DELETE FROM continents WHERE continent_id=$1";
  var values = [req.params.id];

  db.query(query, values)
  .then(function(result) {
    res.status(204).end();
  })
  .catch(function(error) {
    res.status(500).send(error);
  })
});

module.exports = router;
<|endoftext|>


<commit_before>import { batchedSubscribe } from '../';
import { spy } from 'sinon';

function createStoreShape() {
  return {
    dispatch: spy(),
    subscribe: spy()
  };
}

describe('batchedSubscribe()', () => {
  it('it calls batch function on dispatch', () => {
    const batchSpy = spy();
    const baseStore = createStoreShape();
    const createStore = () => baseStore;
    const store = batchedSubscribe(batchSpy)(createStore)();

    store.dispatch({ type: 'foo' });

    expect(batchSpy.callCount).to.equal(1);
  });

  it('batch callback executes listeners', () => {
    const subscribeCallbackSpy = spy();
    const baseStore = createStoreShape();
    const createStore = () => baseStore;
    const store = batchedSubscribe((cb) => cb())(createStore)();

    store.subscribe(subscribeCallbackSpy);
    store.dispatch({ type: 'foo' });

    expect(baseStore.subscribe.callCount).to.equal(0);
    expect(subscribeCallbackSpy.callCount).to.equal(1);
  });

  it('it exposes base subscribe as subscribeImmediate', () => {
    const baseStore = createStoreShape();
    const createStore = () => baseStore;
    const store = batchedSubscribe((cb) => cb())(createStore)();

    store.subscribeImmediate();

    expect(baseStore.subscribe.callCount).to.equal(1);
  });
});
<commit_msg>add test for unsubscribe.<commit_after>import { batchedSubscribe } from '../';
import { spy } from 'sinon';

function createStoreShape() {
  return {
    dispatch: spy(),
    subscribe: spy()
  };
}

describe('batchedSubscribe()', () => {
  it('it calls batch function on dispatch', () => {
    const batchSpy = spy();
    const baseStore = createStoreShape();
    const createStore = () => baseStore;
    const store = batchedSubscribe(batchSpy)(createStore)();

    store.dispatch({ type: 'foo' });

    expect(batchSpy.callCount).to.equal(1);
  });

  it('batch callback executes listeners', () => {
    const subscribeCallbackSpy = spy();
    const baseStore = createStoreShape();
    const createStore = () => baseStore;
    const store = batchedSubscribe((cb) => cb())(createStore)();

    store.subscribe(subscribeCallbackSpy);
    store.dispatch({ type: 'foo' });

    expect(baseStore.subscribe.callCount).to.equal(0);
    expect(subscribeCallbackSpy.callCount).to.equal(1);
  });

  it('it exposes base subscribe as subscribeImmediate', () => {
    const baseStore = createStoreShape();
    const createStore = () => baseStore;
    const store = batchedSubscribe((cb) => cb())(createStore)();

    store.subscribeImmediate();

    expect(baseStore.subscribe.callCount).to.equal(1);
  });

  it('unsubscribes batch callbacks', () => {
    const subscribeCallbackSpy = spy();
    const baseStore = createStoreShape();
    const createStore = () => baseStore;
    const store = batchedSubscribe((cb) => cb())(createStore)();
    const unsubscribe = store.subscribe(subscribeCallbackSpy);

    unsubscribe();

    store.dispatch({ type: 'foo' });

    expect(subscribeCallbackSpy.callCount).to.equal(0);
  });
});
<|endoftext|>


<commit_before>"use strict";

function CollisionManager() {

    var objects = [];

    this.intersect = function(object1, object2) {
        var pos1 = object1.getLocation();
        var pos2 = object2.getLocation();
        return (((pos1.x >= pos2.x && pos1.x <= pos2.x + pos2.width) &&
                  (pos1.y >= pos2.y && pos1.y <= pos2.y + pos2.height)) ||
                ((pos2.x >= pos1.x && pos2.x <= pos1.x + pos1.width) &&
                  (pos2.y >= pos1.y && pos2.y <= pos1.y + pos1.height)));
    }

    this.addObject = function(object) {
        objects.push(object);
    }

    this.findCollisions = function(object) {
        var collidedWith = [];
        for (var i = 0; i < objects.length; i++) {
            if (objects.id != objects[i].id &&
                    this.intersect(object, objects[i]))
                collidedWith.push(objects[i].id);
        }

        if (collidedWith.length > 0) {
            object.onCollision({
                'collided_with': collidedWith
            });
        }
    }
}
<commit_msg>Fix minor issues in CollisionManager<commit_after>"use strict";

function CollisionManager() {

    var objects = [];

    this.intersect = function(object1, object2) {
        var pos1 = object1.getLocation();
        var pos2 = object2.getLocation();
        return (((pos1.x >= pos2.x && pos1.x <= pos2.x + pos2.width) &&
                  (pos1.y >= pos2.y && pos1.y <= pos2.y + pos2.height)) ||
                ((pos2.x >= pos1.x && pos2.x <= pos1.x + pos1.width) &&
                  (pos2.y >= pos1.y && pos2.y <= pos1.y + pos1.height)));
    }

    this.addObject = function(object) {
        objects.push(object);
    }

    this.findCollisions = function(object) {
        var collidedWith = [];
        for (var i = 0; i < objects.length; i++) {
            if (object.settings.id.value != objects[i].settings.id.value &&
                    this.intersect(object, objects[i]))
                collidedWith.push(objects[i].settings.id.value);
        }

        if (collidedWith.length > 0) {
            console.log('Found collision');
            object.onCollision({
                'collidedWith': collidedWith
            });
        }
    }
}
<|endoftext|>


<commit_before>
var EventEmitter = require('events').EventEmitter,
	logger = require('../logging').getLogger(__LOGGER__),
	Router = require('routr'),
	Q = require('q'),
	History = require("../components/History"),
	PageUtil = require("../util/PageUtil");

class Navigator extends EventEmitter {

	constructor (context, routes, applicationStore) {
		this.router = new Router(routes.routes);
		this.context = context;

		this._globalMiddleware = routes.middleware;
		this._loading = false;
		this._currentRoute = null;
	}

	/**
	 * type is one of
	 *    History.events.PUSHSTATE: user clicked something to go forward but browser didn't do a
	 * full page load
	 *    History.events.POPSTATE: user clicked back button but browser didn't do a full page load
	 *    History.events.PAGELOAD: full browser page load, not using History API.
	 *
	 * Default is History.events.PAGELOAD.
	 */
	navigate (request, type) {
		logger.debug(`Navigating to ${request.getUrl()}`);
		type = type || History.events.PAGELOAD;

		var route = this.router.getRoute(request.getUrl(), {navigate: {path:request.getUrl(), type:type}});
		if (!route) {
			setTimeout( () => {
				this.emit('navigateDone', { status: 404, message: "No Route!" }, null, request.getUrl(), type);
			}, 0);
			return;
		}
		logger.debug(`Mapped ${request.getUrl()} to route ${route.name}`);

		this.startRoute(route);
		this.emit('navigateStart', route);

		/* Breathe... */

		route.config.page().done( pageConstructor => {
			if (request.setRoute) {
				request.setRoute(route);
			}
			this.handlePage(pageConstructor, request, type);

		}, err => {
			console.error("Error resolving page", err);
		});

	}

	handlePage(pageConstructor, request, type) {
		// instantiate the pages we need to fulfill this request.
		var pageClasses = [];

		this._addPageMiddlewareToArray(this._globalMiddleware, pageClasses);
		this._addPageMiddlewareToArray([pageConstructor], pageClasses);

		var pages = pageClasses.map((pageClass) => {
			if (Object.getOwnPropertyNames(pageClass).length === 0) {
				throw new Error("Tried to instantiate a page or middleware class that was an empty object. Did you forget to assign a class to module.exports?");
			}
			return new pageClass();
		});
		var page = PageUtil.createPageChain(pages);

		// call page.handleRoute(), and use the resulting code to decide how to
		// respond.
		page.handleRoute(request).then(handleRouteResult => {
			// TODO: I think that 3xx/4xx/5xx shouldn't be considered "errors" in navigateDone, but that's
			// how the code is structured right now, and I'm changing too many things at once at the moment. -sra.
			if (handleRouteResult.code && handleRouteResult.code / 100 !== 2) {
				this.emit("navigateDone", {status: handleRouteResult.code, redirectUrl: handleRouteResult.location}, null, request.getUrl(), type);
				return;
			}
			if (handleRouteResult.page) {
				// in this case, we should forward to a new page *without* changing the URL. Since we are already
				// in an async callback, we should schedule a new handlePage with the new page constructor and return
				// from this call.
				setTimeout(() => this.handlePage(handleRouteResult.page, request, type), 0);
				return;
			}

			this.finishRoute();
			this.emit('navigateDone', null, page, request.getUrl(), type);
		}).catch(err => {
			logger.error("Error while handling route.", err.stack);
		});

	}

	/**
	 * recursively adds the middleware in the pages array to array.
	 */
	_addPageMiddlewareToArray(pages, array) {
		if (!pages) return;
		pages.forEach((page) => {
			if (page.middleware) {
				this._addPageMiddlewareToArray(page.middleware(), array);
			}
			array.push(page);
		});
	}

	getState () {
		return {
			loading: this._loading,
			route: this._currentRoute
		}
	}

	getCurrentRoute () {
		return this._currentRoute;
	}

	getLoading () {
		return this._loading;
	}

	startRoute (route) {
		this._loading = true;
		this._currentRoute = route;
	}

	finishRoute () {
		this._loading = false;
	}

}

module.exports = Navigator;<commit_msg>Short-circuit return 500 on navigate error<commit_after>
var EventEmitter = require('events').EventEmitter,
	logger = require('../logging').getLogger(__LOGGER__),
	Router = require('routr'),
	Q = require('q'),
	History = require("../components/History"),
	PageUtil = require("../util/PageUtil");

class Navigator extends EventEmitter {

	constructor (context, routes, applicationStore) {
		this.router = new Router(routes.routes);
		this.context = context;

		this._globalMiddleware = routes.middleware;
		this._loading = false;
		this._currentRoute = null;
	}

	/**
	 * type is one of
	 *    History.events.PUSHSTATE: user clicked something to go forward but browser didn't do a
	 * full page load
	 *    History.events.POPSTATE: user clicked back button but browser didn't do a full page load
	 *    History.events.PAGELOAD: full browser page load, not using History API.
	 *
	 * Default is History.events.PAGELOAD.
	 */
	navigate (request, type) {
		logger.debug(`Navigating to ${request.getUrl()}`);
		type = type || History.events.PAGELOAD;

		var route = this.router.getRoute(request.getUrl(), {navigate: {path:request.getUrl(), type:type}});
		if (!route) {
			setTimeout( () => {
				this.emit('navigateDone', { status: 404, message: "No Route!" }, null, request.getUrl(), type);
			}, 0);
			return;
		}
		logger.debug(`Mapped ${request.getUrl()} to route ${route.name}`);

		this.startRoute(route);
		this.emit('navigateStart', route);

		/* Breathe... */

		route.config.page().done( pageConstructor => {
			if (request.setRoute) {
				request.setRoute(route);
			}
			this.handlePage(pageConstructor, request, type);

		}, err => {
			console.error("Error resolving page", err);
		});

	}

	handlePage(pageConstructor, request, type) {
		// instantiate the pages we need to fulfill this request.
		var pageClasses = [];

		this._addPageMiddlewareToArray(this._globalMiddleware, pageClasses);
		this._addPageMiddlewareToArray([pageConstructor], pageClasses);

		var pages = pageClasses.map((pageClass) => {
			if (Object.getOwnPropertyNames(pageClass).length === 0) {
				throw new Error("Tried to instantiate a page or middleware class that was an empty object. Did you forget to assign a class to module.exports?");
			}
			return new pageClass();
		});
		var page = PageUtil.createPageChain(pages);

		// call page.handleRoute(), and use the resulting code to decide how to
		// respond.
		page.handleRoute(request).then(handleRouteResult => {
			// TODO: I think that 3xx/4xx/5xx shouldn't be considered "errors" in navigateDone, but that's
			// how the code is structured right now, and I'm changing too many things at once at the moment. -sra.
			if (handleRouteResult.code && handleRouteResult.code / 100 !== 2) {
				this.emit("navigateDone", {status: handleRouteResult.code, redirectUrl: handleRouteResult.location}, null, request.getUrl(), type);
				return;
			}
			if (handleRouteResult.page) {
				// in this case, we should forward to a new page *without* changing the URL. Since we are already
				// in an async callback, we should schedule a new handlePage with the new page constructor and return
				// from this call.
				setTimeout(() => this.handlePage(handleRouteResult.page, request, type), 0);
				return;
			}

			this.finishRoute();
			this.emit('navigateDone', null, page, request.getUrl(), type);
		}).catch(err => {
			logger.error("Error while handling route.", err.stack);
			this.emit('navigateDone', {status: 500}, null, request.getUrl(), type);
		});

	}

	/**
	 * recursively adds the middleware in the pages array to array.
	 */
	_addPageMiddlewareToArray(pages, array) {
		if (!pages) return;
		pages.forEach((page) => {
			if (page.middleware) {
				this._addPageMiddlewareToArray(page.middleware(), array);
			}
			array.push(page);
		});
	}

	getState () {
		return {
			loading: this._loading,
			route: this._currentRoute
		}
	}

	getCurrentRoute () {
		return this._currentRoute;
	}

	getLoading () {
		return this._loading;
	}

	startRoute (route) {
		this._loading = true;
		this._currentRoute = route;
	}

	finishRoute () {
		this._loading = false;
	}

}

module.exports = Navigator;<|endoftext|>


<commit_before>;(function ($, window, document, undefined) {

    var pluginName = "shapeshift",
        defaults = {
          // Grid Properties
          centerGrid: true,
          gutterX: 10,
          gutterY: 10,
          paddingX: 10,
          paddingY: 10,

          // Others
          animateSpeed: 150,

          // Features
          enableAnimation: true,
          enableAnimationOnInit: false,
          enableResize: true
        };

    function Plugin(element, options) {
      var ss = this;
      ss.options = $.extend({}, defaults, options);

      // Set Globals
      ss.container = $(element);
      ss.grid_height = 100;
      ss.animate = false;

      ss.init();
    }

    Plugin.prototype = {

      // --------------------
      // Setup
      // --------------------

      init: function() {
        var ss = this;

        ss.detectDependencies();

        ss.enableFeatures();
        ss.createEvents();
        ss.render();

        ss.afterInit();
      },

      detectDependencies: function() {
        var ss = this;
      },

      enableFeatures: function() {
        var ss = this,
            options = ss.options;

        if(options.enableResize) { ss.resize(); }
        if(options.enableAnimation && options.enableAnimationOnInit) { ss.animate = true; }
      },
      createEvents: function() {
        var ss = this;

        ss.container.off("ss-arrange").on("ss-arrange", function() { ss.render(); });
        ss.container.off("ss-destroy").on("ss-destroy", function() { ss.destroy(); });
      },

      render: function() {
        var ss = this;

        ss.arrange();
      },

      afterInit: function() {
        var ss = this,
            options = ss.options;

        // Re-enable animation if it was canceled on init
        if(options.enableAnimation) { ss.animate = true; }
      },

      // --------------------
      // Core Shapeshift
      // --------------------

      // Physically move the child elements into
      // their determined positions.
      arrange: function() {
        var ss = this,
            $container = ss.container,
            $children = $container.children(":visible"),
            positions = ss.getPositions(),
            animate = ss.animate,
            animateSpeed = ss.options.animateSpeed;

        for(var i=0;i<positions.length;i++) {
          var $child = $children.eq(i);

          if(animate) {
            $child.stop(true, false).animate(positions[i], animateSpeed);
          } else {
            $child.css(positions[i]);
          }
        }

        $container.height(ss.grid_height)
      },

      // Returns an array containing all margin left/top
      // coordinates for each child.
      getPositions: function() {
        var ss = this,
            options = ss.options,
            gutterX = options.gutterX,
            gutterY = options.gutterY,
            paddingX = options.paddingX,
            paddingY = options.paddingY;

        // Get DOM properties
        var $container = ss.container,
            $children = $container.children(":visible"),
            container_width = $container.innerWidth() - (paddingX * 2),
            child_width = $children.first().outerWidth(),
            col_width = child_width + gutterX,
            columns = Math.floor((container_width + gutterX) / col_width);

        // Determine offset
        var offset = 0;
        if(options.centerGrid) {
          var grid_width = (columns * col_width) - gutterX;
          offset = (container_width - grid_width) / 2
        }

        // Store our column heights
        var col_heights = [];
        for(var i=0;i<columns;i++) { col_heights.push(paddingY); }

        // Calculate the child positions
        var positions = [];
        for(var i=0;i<$children.length;i++) {
          var $child = $children.eq(i),
              col = ss.lowestCol(col_heights),
              offsetX = (col * col_width) + paddingX + offset,
              offsetY = col_heights[col];

          col_heights[col] += $child.height() + gutterY;

          positions.push({left: offsetX, top: offsetY});
        }

        // Store the grid height
        var grid_height = col_heights[ss.highestCol(col_heights)]
        ss.grid_height = grid_height - gutterY + paddingY;

        return positions;
      },

      // --------------------
      // Features
      // --------------------

      // Listens to the resize event and then triggers
      // the arrange even on the container.
      resize: function () {
        var ss = this,
            resizing = false,
            animateSpeed = ss.options.animateSpeed;

        $(window).on("resize.shapeshift", function() {
          console.log("resize")
          if(!resizing) {
            resizing = true;

            setTimeout(function() { ss.container.trigger("ss-arrange"); }, animateSpeed / 2);
            setTimeout(function() { ss.container.trigger("ss-arrange"); }, animateSpeed);

            setTimeout(function() {
              ss.container.trigger("ss-arrange");
              resizing = false;
            }, animateSpeed * 1.5);
          }
        });
      },

      // --------------------
      // Helper Functions
      // --------------------

      lowestCol: function(array) {
        return $.inArray(Math.min.apply(window,array), array);
      },

      highestCol: function(array) {
        return $.inArray(Math.max.apply(window,array), array);
      },

      // --------------------
      // Other
      // --------------------

      destroy: function() {
        var ss = this;

        ss.container.children().each(function() {
          $(this).css({left: 0, top: 0})
        })

        ss.container.off("ss-arrange");
        ss.container.off("ss-destroy");
        $(window).off("resize.shapeshift");

        console.info("Shapeshift successfully destroyed")
      }

    };

    $.fn[pluginName] = function (options) {
      return this.each(function () {
        if (!$.data(this, "plugin_" + pluginName)) {
          $.data(this, "plugin_" + pluginName, new Plugin( this, options ));
        }
      });
    };

})( jQuery, window, document );<commit_msg>Brought in more properties, specifically container height<commit_after>;(function ($, window, document, undefined) {

    var pluginName = "shapeshift",
        defaults = {
          // Grid Properties
          autoHeight: true,
          centerGrid: true,
          columns: null,
          gutterX: 10,
          gutterY: 10,
          height: null,
          maxHeight: null,
          minHeight: 100,
          paddingX: 10,
          paddingY: 10,

          // Others
          animateSpeed: 150,

          // Features
          enableAnimation: true,
          enableAnimationOnInit: false,
          enableResize: true
        };

    function Plugin(element, options) {
      var ss = this;
      ss.options = $.extend({}, defaults, options);

      // Set Globals
      ss.container = $(element);
      ss.container_height = 100;
      ss.animate = false;

      ss.init();
    }

    Plugin.prototype = {

      // --------------------
      // Setup
      // --------------------

      init: function() {
        var ss = this;

        ss.detectDependencies();

        ss.enableFeatures();
        ss.createEvents();
        ss.render();

        ss.afterInit();
      },

      detectDependencies: function() {
        var ss = this;
      },

      enableFeatures: function() {
        var ss = this,
            options = ss.options;

        if(options.enableResize) { ss.resize(); }
        if(options.enableAnimation && options.enableAnimationOnInit) { ss.animate = true; }
      },

      createEvents: function() {
        var ss = this;

        ss.container.off("ss-arrange").on("ss-arrange", function() { ss.render(); });
        ss.container.off("ss-destroy").on("ss-destroy", function() { ss.destroy(); });
      },

      render: function() {
        var ss = this;

        ss.arrange();
      },

      afterInit: function() {
        var ss = this,
            options = ss.options;

        // Re-enable animation if it was canceled on init
        if(options.enableAnimation) { ss.animate = true; }
      },

      // --------------------
      // Core Shapeshift
      // --------------------

      // Physically move the child elements into
      // their determined positions.
      arrange: function() {
        var ss = this,
            options = ss.options,
            $container = ss.container,
            $children = $container.children(":visible"),
            positions = ss.getPositions(),
            animate = ss.animate,
            animateSpeed = options.animateSpeed;

        for(var i=0;i<positions.length;i++) {
          var $child = $children.eq(i);

          if(animate) {
            $child.stop(true, false).animate(positions[i], animateSpeed);
          } else {
            $child.css(positions[i]);
          }
        }

        // Set the container height
        if(ss.options.autoHeight) {
          var height = options.height;

          if(height) {
              $container.height(height);
          } else {
            var container_height = ss.container_height,
                max_height = options.maxHeight,
                min_height = options.minHeight;

            if(min_height && ((container_height < min_height) || (max_height && max_height < min_height))) {
              $container.height(min_height);
            } else if(max_height && container_height > max_height) {
              $container.height(max_height);
            } else {
              $container.height(container_height);
            }
          }
        }
      },

      // Returns an array containing all margin left/top
      // coordinates for each child.
      getPositions: function() {
        var ss = this,
            options = ss.options,
            gutterX = options.gutterX,
            gutterY = options.gutterY,
            paddingX = options.paddingX,
            paddingY = options.paddingY;

        // Get DOM properties
        var $container = ss.container,
            $children = $container.children(":visible"),
            container_width = $container.innerWidth() - (paddingX * 2),
            child_width = $children.first().outerWidth(),
            col_width = child_width + gutterX,
            columns = options.columns || Math.floor((container_width + gutterX) / col_width);

        // Determine offset
        var offset = 0;
        if(options.centerGrid) {
          var grid_width = (columns * col_width) - gutterX;
          offset = (container_width - grid_width) / 2
        }

        // Store our column heights
        var col_heights = [];
        for(var i=0;i<columns;i++) { col_heights.push(paddingY); }

        // Calculate the child positions
        var positions = [];
        for(var i=0;i<$children.length;i++) {
          var $child = $children.eq(i),
              col = ss.lowestCol(col_heights),
              offsetX = (col * col_width) + paddingX + offset,
              offsetY = col_heights[col];

          col_heights[col] += $child.height() + gutterY;

          positions.push({left: offsetX, top: offsetY});
        }

        // Store the grid height
        var grid_height = col_heights[ss.highestCol(col_heights)]
        ss.container_height = grid_height - gutterY + paddingY;

        return positions;
      },

      // --------------------
      // Features
      // --------------------

      // Listens to the resize event and then triggers
      // the arrange even on the container.
      resize: function () {
        var ss = this,
            resizing = false,
            animateSpeed = ss.options.animateSpeed;

        $(window).on("resize.shapeshift", function() {
          console.log("resize")
          if(!resizing) {
            resizing = true;

            setTimeout(function() { ss.container.trigger("ss-arrange"); }, animateSpeed / 2);
            setTimeout(function() { ss.container.trigger("ss-arrange"); }, animateSpeed);

            setTimeout(function() {
              ss.container.trigger("ss-arrange");
              resizing = false;
            }, animateSpeed * 1.5);
          }
        });
      },

      // --------------------
      // Helper Functions
      // --------------------

      lowestCol: function(array) {
        return $.inArray(Math.min.apply(window,array), array);
      },

      highestCol: function(array) {
        return $.inArray(Math.max.apply(window,array), array);
      },

      // --------------------
      // Other
      // --------------------

      destroy: function() {
        var ss = this;

        ss.container.children().each(function() {
          $(this).css({left: 0, top: 0})
        })

        ss.container.off("ss-arrange");
        ss.container.off("ss-destroy");
        $(window).off("resize.shapeshift");

        console.info("Shapeshift successfully destroyed")
      }

    };

    $.fn[pluginName] = function (options) {
      return this.each(function () {
        if (!$.data(this, "plugin_" + pluginName)) {
          $.data(this, "plugin_" + pluginName, new Plugin( this, options ));
        }
      });
    };

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


<commit_before>/*
  Angular Moment.js Moment Picker
*/

'use strict';

angular.module('moment')

.directive('momentPicker', ['$moment', '$log', 'indexOf', function inputDirective($moment, $log, indexOf) {
  var weekStartDay = $moment().startOf('week').format('d'),
      weekEndDay   = $moment().endOf('week')  .format('d');

  return {
    restrict: 'A',
    templateUrl: function(tElement, tAttrs) {
      var templateName = tAttrs.template || 'default',
          templateUrl  = $moment.$$pickerTemplates[templateName];
      if (templateUrl)
        return templateUrl;
      // Ya dun' goofed.
      $log.error('Error: [momentDatepicker] Picker template for \''+ templateName +'\' is undefined. Templates must be defined with \'$momentProvider.definePickerTemplate\'.');
    },
    scope: {
      dateModel:   '=momentPicker',
      format:      '=?',
      modelFormat: '=?',
      min:         '=?',
      max:         '=?',
      ngShow:      '=?'
    },
    link: function(scope, element, attr) {
      var format  = $moment.$defaultModelFormat,
          moments = {};

      // Initialize
      //////////////
      scope.hasNgShowAttr = !!attr.ngShow;

      if (!attr.ngShow)
        scope.ngShow = true;

      scope.displayMoment = $moment();
      scope.weekMoments   = [];

      var i = 7;
      while (i--)
        scope.weekMoments.unshift($moment().startOf('week').add(i, 'day'));

      // Process Format or modelFormat Attr
      //////////////////////////////////////

      var setFormat = function(newFormat) {
        if (angular.equals(format, newFormat))
          return;

        format = newFormat || $moment.$defaultModelFormat;
        parseDateModel(scope.dateModel);
      };

      if (attr.format && !attr.modelFormat) {
        format = scope.format || $moment.$defaultModelFormat;
        scope.$watch('format', setFormat);
      }
      else if (attr.modelFormat) {
        format = scope.modelFormat || $moment.$defaultModelFormat;
        scope.$watch('modelFormat', setFormat);
      }

      // Process Min/Max Attrs
      /////////////////////////

      var setMomentFromAttr = function(attrName, attrValue) {
        var moment;

        // Parse
        if (angular.isArray(attrValue) && attrValue.length == 2)
          moment = $moment(attrValue[0], attrValue[1], true);
        else if (attrValue && angular.isString(attrValue)) {
          if (attrValue == 'today')
            moment = $moment();
          else
            moment = $moment(attrValue, $moment.$defaultModelFormat, $moment.$strictModel);
        }
        else
          moment = $moment(null);

        // Set
        moments[attrName] = moment.isValid() ? moment : null;
      };

      if (attr.min) {
        scope.$watch('min', function(minValue) {
          setMomentFromAttr('min', minValue);
        }, true);
      }

      if (attr.max) {
        scope.$watch('max', function(maxValue) {
          setMomentFromAttr('max', maxValue);
        }, true);
      }

      // View helpers
      ////////////////

      scope.getClasses = function(moment, classes) {
        var isWeekend   = /0|6/.test(moment.isoWeekday()),
            isWeekday   = !isWeekend,
            classObject = {
              weekend: isWeekend,
              weekday: isWeekday
            };

        // Convenience classes: jan fri
        classObject[ moment.format('MMM ddd').toLowerCase() ] = true;

        if (!classes)
          return;

        angular.forEach(classes.split(' '), function(className) {
          switch(className) {
            case 'current':         // Same as current-day
            case 'current-day':     classObject[className +' current'] = moment.isSame(scope.today, 'day'); break;
            case 'current-week':    classObject[className +' current'] = moment.isSame(scope.today, 'week'); break;
            case 'current-month':   classObject[className +' current'] = moment.isSame(scope.today, 'month'); break;
            case 'current-year':    classObject[className +' current'] = moment.isSame(scope.today, 'year'); break;

            case 'picked': classObject[className] = moment.isSame(scope.dateMoment, 'day'); break;
            // case 'picked-day':   classObject[className] = moment.isSame(scope.dateMoment, 'day'); break;
            // case 'picked-week':  classObject[className] = moment.isSame(scope.dateMoment, 'week'); break;
            // case 'picked-month': classObject[className] = moment.isSame(scope.dateMoment, 'month'); break;
            // case 'picked-year':  classObject[className] = moment.isSame(scope.dateMoment, 'year'); break;

            // We'll only check this if a min or max is set
            case 'invalid':
            case 'invalid-day':
            case 'invalid-week':
            case 'invalid-month':
            case 'invalid-year':
              if (!moments.min && !moments.max)
                break;

              var unit = className.split('-')[1];

              if (moments.min && moment.isBefore(moments.min, unit || 'day'))
                classObject[className + ' invalid'] = true;
              else if (moments.max && moment.isAfter(moments.max, unit || 'day'))
                classObject[className + ' invalid'] = true;
              break;
          }
        });

        return classObject;
      };

      scope.setDateTo = function(moment) {
        if (moments.min && moment.isBefore(moments.min))
          return;
        if (moments.max && moment.isAfter(moments.max))
          return;
        scope.dateModel = moment.format(format);
      };

      // Core things
      ///////////////

      var parseDateModel = function(dateModel) {
        var moment = $moment(dateModel, format, $moment.$strictModel);

        if (dateModel && moment.isValid()) {
          scope.dateMoment    = moment.clone();
          scope.displayMoment = moment.clone();
        }
        else {
          scope.dateMoment    = $moment('');
          scope.displayMoment = $moment();
        }
      };

      scope.$watch('dateModel', parseDateModel);

      scope.$watch(function() { return scope.displayMoment.format('M/YYYY'); }, function(moment, oldMoment) {
        rebuild();
      });

      function rebuild() {
        scope.today = $moment();
        scope.lastMonthMoments = [];
        scope.thisMonthMoments = [];
        scope.nextMonthMoments = [];
        scope.monthsThisYearMoments = [];

        var lastMonthMoment = scope.displayMoment.clone().startOf('month'),
            thisMonthMoment = lastMonthMoment.clone(),
            nextMonthMoment = scope.displayMoment.clone().endOf('month'),
            thisMonth       = scope.displayMoment.format('M'),
            thisYear        = scope.displayMoment.format('YYYY');

        while (lastMonthMoment.format('d') !== weekStartDay)
          scope.lastMonthMoments.unshift(lastMonthMoment.subtract(1, 'day').clone());

        while (thisMonthMoment.format('M') === thisMonth) {
          scope.thisMonthMoments.push(thisMonthMoment.clone());
          thisMonthMoment.add(1, 'day');
        }

        while (scope.lastMonthMoments.length + scope.thisMonthMoments.length + scope.nextMonthMoments.length < 42)
          scope.nextMonthMoments.push(nextMonthMoment.add(1, 'day').clone());

        while (scope.monthsThisYearMoments.length < 12)
          scope.monthsThisYearMoments.push(moment({ year:thisYear, month:scope.monthsThisYearMoments.length }));

      }

    }
  };

}])

// Picker extends moment input directive with positioned momentPicker

.directive('picker', ['$moment', '$compile', 'getOffset', function inputDirective($moment, $compile, getOffset) {
  var defaultStyleAttr = 'style="position:absolute" class="input-picker"',
      copiedAttrs      = 'format modelFormat min max'.split(' ');

  var toSpinalCase = function(string) {
    return string.replace(/[a-z][A-Z]/g, function(w) { return w[0] +'-'+ w[1]; }).toLowerCase();
  };

  return {
    restrict: 'A',
    require: '?ngModel',
    link: function(scope, element, attr, ctrl) {
      if (!ctrl || attr.type !== 'moment')
        return;

      var pickerAttrs = [ defaultStyleAttr ];

      // Copy relevent attrs from input to picker
      if (attr.picker)
        pickerAttrs.push('template='+ attr.picker);

      angular.forEach(copiedAttrs, function(name) {
        if (attr[name])
          pickerAttrs.push(toSpinalCase(name) +'="'+ attr[name] +'"');
      });

      // 'ngShow' state tracking
      if (!scope.$momentPicker)
        scope.$momentPicker = {};
      scope.$momentPicker[attr.ngModel] = false;
      pickerAttrs.push('ng-show="$momentPicker[\''+ attr.ngModel +'\']"');

      // Compile/inject/bind events to picker
      var pickerElement = $compile('

')(scope);
      angular.element(document.body).append(pickerElement);

      pickerElement.on('mousedown', function(event) {
        event.preventDefault();
      });

      // Input event binding
      element.on('focus click', function(event) {
        var offset = getOffset(element[0]);

        pickerElement.css({
          left: offset.left + 'px',
          top: offset.bottom + 'px'
        });

        scope.$apply(function(scope) {
          scope.$momentPicker[attr.ngModel] = true;
        });
      });

      element.on('blur keydown', function(event) {
        if (event.type == 'keydown' && event.which !== 27)
          return;
        scope.$apply(function(scope) {
          scope.$momentPicker[attr.ngModel] = false;
        });
      });

      // Destruction cleanup
      scope.$on('$destroy', function() {
        pickerElement.off().remove();
      });

    }
  };
}]);

<commit_msg>Rework getClasses again<commit_after>/*
  Angular Moment.js Moment Picker
*/

'use strict';

angular.module('moment')

.directive('momentPicker', ['$moment', '$log', 'indexOf', function inputDirective($moment, $log, indexOf) {
  var weekStartDay = $moment().startOf('week').format('d'),
      weekEndDay   = $moment().endOf('week')  .format('d');

  return {
    restrict: 'A',
    templateUrl: function(tElement, tAttrs) {
      var templateName = tAttrs.template || 'default',
          templateUrl  = $moment.$$pickerTemplates[templateName];
      if (templateUrl)
        return templateUrl;
      // Ya dun' goofed.
      $log.error('Error: [momentDatepicker] Picker template for \''+ templateName +'\' is undefined. Templates must be defined with \'$momentProvider.definePickerTemplate\'.');
    },
    scope: {
      dateModel:   '=momentPicker',
      format:      '=?',
      modelFormat: '=?',
      min:         '=?',
      max:         '=?',
      ngShow:      '=?'
    },
    link: function(scope, element, attr) {
      var format  = $moment.$defaultModelFormat,
          moments = {};

      // Initialize
      //////////////
      scope.hasNgShowAttr = !!attr.ngShow;

      if (!attr.ngShow)
        scope.ngShow = true;

      scope.displayMoment = $moment();
      scope.weekMoments   = [];

      var i = 7;
      while (i--)
        scope.weekMoments.unshift($moment().startOf('week').add(i, 'day'));

      // Process Format or modelFormat Attr
      //////////////////////////////////////

      var setFormat = function(newFormat) {
        if (angular.equals(format, newFormat))
          return;

        format = newFormat || $moment.$defaultModelFormat;
        parseDateModel(scope.dateModel);
      };

      if (attr.format && !attr.modelFormat) {
        format = scope.format || $moment.$defaultModelFormat;
        scope.$watch('format', setFormat);
      }
      else if (attr.modelFormat) {
        format = scope.modelFormat || $moment.$defaultModelFormat;
        scope.$watch('modelFormat', setFormat);
      }

      // Process Min/Max Attrs
      /////////////////////////

      var setMomentFromAttr = function(attrName, attrValue) {
        var moment;

        // Parse
        if (angular.isArray(attrValue) && attrValue.length == 2)
          moment = $moment(attrValue[0], attrValue[1], true);
        else if (attrValue && angular.isString(attrValue)) {
          if (attrValue == 'today')
            moment = $moment();
          else
            moment = $moment(attrValue, $moment.$defaultModelFormat, $moment.$strictModel);
        }
        else
          moment = $moment(null);

        // Set
        moments[attrName] = moment.isValid() ? moment : null;
      };

      if (attr.min) {
        scope.$watch('min', function(minValue) {
          setMomentFromAttr('min', minValue);
        }, true);
      }

      if (attr.max) {
        scope.$watch('max', function(maxValue) {
          setMomentFromAttr('max', maxValue);
        }, true);
      }

      // View helpers
      ////////////////

      scope.getClasses = function(moment, classes) {
        var isWeekend   = /0|6/.test(moment.isoWeekday()),
            isWeekday   = !isWeekend,
            classObject = {
              weekend: isWeekend,
              weekday: isWeekday
            };

        // Convenience classes: jan fri
        classObject[ moment.format('MMM ddd').toLowerCase() ] = true;

        if (!classes)
          return;

        angular.forEach(classes.split(' '), function(className) {
          switch(className) {
            case 'picked': classObject[className] = moment.isSame(scope.dateMoment, 'day'); break;

            case 'current':       // Same as current-day
            case 'current-day':   classObject[className +' current'] = moment.isSame(scope.today, 'day'); break;
            case 'current-week':  classObject[className +' current'] = moment.isSame(scope.today, 'week'); break;
            case 'current-month': classObject[className +' current'] = moment.isSame(scope.today, 'month'); break;
            case 'current-year':  classObject[className +' current'] = moment.isSame(scope.today, 'year'); break;

            // We'll only check this if a min or max is set
            case 'invalid':
            case 'invalid-day':
            case 'invalid-week':
            case 'invalid-month':
            case 'invalid-year':
              if (!moments.min && !moments.max)
                break;
              var specificity = className.split('-')[1] || 'day';

              if (moments.min && moment.isBefore(moments.min, specificity))
                classObject[className + ' invalid'] = true;
              else if (moments.max && moment.isAfter(moments.max, specificity))
                classObject[className + ' invalid'] = true;
              break;
          }
        });

        return classObject;
      };

      scope.setDateTo = function(moment) {
        if (moments.min && moment.isBefore(moments.min))
          return;
        if (moments.max && moment.isAfter(moments.max))
          return;
        scope.dateModel = moment.format(format);
      };

      // Core things
      ///////////////

      var parseDateModel = function(dateModel) {
        var moment = $moment(dateModel, format, $moment.$strictModel);

        if (dateModel && moment.isValid()) {
          scope.dateMoment    = moment.clone();
          scope.displayMoment = moment.clone();
        }
        else {
          scope.dateMoment    = $moment('');
          scope.displayMoment = $moment();
        }
      };

      scope.$watch('dateModel', parseDateModel);

      scope.$watch(function() { return scope.displayMoment.format('M/YYYY'); }, function(moment, oldMoment) {
        rebuild();
      });

      function rebuild() {
        scope.today = $moment();
        scope.lastMonthMoments = [];
        scope.thisMonthMoments = [];
        scope.nextMonthMoments = [];
        scope.monthsThisYearMoments = [];

        var lastMonthMoment = scope.displayMoment.clone().startOf('month'),
            thisMonthMoment = lastMonthMoment.clone(),
            nextMonthMoment = scope.displayMoment.clone().endOf('month'),
            thisMonth       = scope.displayMoment.format('M'),
            thisYear        = scope.displayMoment.format('YYYY');

        while (lastMonthMoment.format('d') !== weekStartDay)
          scope.lastMonthMoments.unshift(lastMonthMoment.subtract(1, 'day').clone());

        while (thisMonthMoment.format('M') === thisMonth) {
          scope.thisMonthMoments.push(thisMonthMoment.clone());
          thisMonthMoment.add(1, 'day');
        }

        while (scope.lastMonthMoments.length + scope.thisMonthMoments.length + scope.nextMonthMoments.length < 42)
          scope.nextMonthMoments.push(nextMonthMoment.add(1, 'day').clone());

        while (scope.monthsThisYearMoments.length < 12)
          scope.monthsThisYearMoments.push(moment({ year:thisYear, month:scope.monthsThisYearMoments.length }));

      }

    }
  };

}])

// Picker extends moment input directive with positioned momentPicker

.directive('picker', ['$moment', '$compile', 'getOffset', function inputDirective($moment, $compile, getOffset) {
  var defaultStyleAttr = 'style="position:absolute" class="input-picker"',
      copiedAttrs      = 'format modelFormat min max'.split(' ');

  var toSpinalCase = function(string) {
    return string.replace(/[a-z][A-Z]/g, function(w) { return w[0] +'-'+ w[1]; }).toLowerCase();
  };

  return {
    restrict: 'A',
    require: '?ngModel',
    link: function(scope, element, attr, ctrl) {
      if (!ctrl || attr.type !== 'moment')
        return;

      var pickerAttrs = [ defaultStyleAttr ];

      // Copy relevent attrs from input to picker
      if (attr.picker)
        pickerAttrs.push('template='+ attr.picker);

      angular.forEach(copiedAttrs, function(name) {
        if (attr[name])
          pickerAttrs.push(toSpinalCase(name) +'="'+ attr[name] +'"');
      });

      // 'ngShow' state tracking
      if (!scope.$momentPicker)
        scope.$momentPicker = {};
      scope.$momentPicker[attr.ngModel] = false;
      pickerAttrs.push('ng-show="$momentPicker[\''+ attr.ngModel +'\']"');

      // Compile/inject/bind events to picker
      var pickerElement = $compile('

')(scope);
      angular.element(document.body).append(pickerElement);

      pickerElement.on('mousedown', function(event) {
        event.preventDefault();
      });

      // Input event binding
      element.on('focus click', function(event) {
        var offset = getOffset(element[0]);

        pickerElement.css({
          left: offset.left + 'px',
          top: offset.bottom + 'px'
        });

        scope.$apply(function(scope) {
          scope.$momentPicker[attr.ngModel] = true;
        });
      });

      element.on('blur keydown', function(event) {
        if (event.type == 'keydown' && event.which !== 27)
          return;
        scope.$apply(function(scope) {
          scope.$momentPicker[attr.ngModel] = false;
        });
      });

      // Destruction cleanup
      scope.$on('$destroy', function() {
        pickerElement.off().remove();
      });

    }
  };
}]);

<|endoftext|>


<commit_before>module.exports = function ( grunt ) {
  'use strict';

  var path = require( 'path' );
  var buildNumber = grunt.option( 'build-number' );

  return {
    options: {
      banner: grunt.file.read( path.resolve( __dirname, '../resources/license/license.txt' ) ),
      transforms: {
        consoleFilter: {
          keep: grunt.option( 'console-filter' ) || grunt.option( 'console-filter-keep' )
        }
      },
      consoleifyEnabled: buildNumber !== 'dev',
      useCache: !grunt.option( 'skip-cache' ),
      //the dev option is for development
      minimize: buildNumber !== 'dev' || grunt.option( 'bundle-min' ),
      revision: buildNumber
    }
  };
};
<commit_msg>FIX: enable consoleify only in dev mode<commit_after>module.exports = function ( grunt ) {
  'use strict';

  var path = require( 'path' );
  var buildNumber = grunt.option( 'build-number' );

  return {
    options: {
      banner: grunt.file.read( path.resolve( __dirname, '../resources/license/license.txt' ) ),
      transforms: {
        consoleFilter: {
          keep: grunt.option( 'console-filter' ) || grunt.option( 'console-filter-keep' )
        }
      },
      consoleifyEnabled: buildNumber === 'dev',
      useCache: !grunt.option( 'skip-cache' ),
      //the dev option is for development
      minimize: buildNumber !== 'dev' || grunt.option( 'bundle-min' ),
      revision: buildNumber
    }
  };
};
<|endoftext|>


<commit_before>let config = {
    loginUrl: 'api/auth',
    tokenName: 'cerberus_token',
    loginRedirect: '#/'
};

export default config;<commit_msg>Added more configs<commit_after>//TODO: Use proper values for dev and production
let config = {
    baseUrl: 'http://localhost:8080',
    loginUrl: 'api/auth',
    tokenName: 'cerberus_token',
    loginRedirect: '#/',
    responseTokenProp: 'token'
};

export default config;<|endoftext|>


<commit_before>"use strict"

var _         = require("lodash")
  , expect    = require("chai").expect
  , ubivar    = require("../ubivar")

describe("Ubivar", function() {

  var rootProps = ["_api"]
    , subProps  = ["auth","protocol","timeout","resources","revokedCerts","headers","request"]
    , methods   = ["create", "retrieve", "update", "del", "list"]
    , resources = ubivar.get("resources")

  this.timeout(20000)

  describe("Properties", function() {
    _.each(rootProps, function(key){
      it("Should have ."+key, function(){
        expect(ubivar[key]).to.exist
      })
    })

    _.each(subProps, function(key){
      it("Should have ._api."+key, function(){
        expect(ubivar._api[key]).to.exist
      })
    })

    it("Should have Resources", function() {
      var nResources = resources.length - rootProps.length
      expect(nResources > 0).to.be.true
    })
    describe("Timeout", function(){
      it("Should default to the one of node", function(){
        expect(ubivar.get('timeout')).to.equal(require('http').createServer().timeout)
      })

      it("Should allow to change default value", function(){
        ubivar.set("timeout", 900);
        expect(ubivar.get('timeout')).to.equal(900)
      })

      it("Should allow a reset to default by setting to 'null'", function(){
        ubivar.set("timeout", null)
        expect(ubivar.get('timeout')).to.equal(require('http').createServer().timeout)
      })
    })
  })

  describe("Methods", function() {
    it("Should have accessor methods", function() {
        expect(ubivar.get).to.exist
        expect(ubivar.set).to.exist
    })

    _.each(subProps, function(key){
      it("Should get and set property ._api."+key, function(){
        var oldValue  = ubivar.get(key)
          , newValue  = ""+Math.random()
        ubivar.set(key, newValue)
        expect(ubivar.get(key)).to.equal(newValue)
        ubivar.set(key, oldValue)
        expect(ubivar.get(key)).to.equal(oldValue)
      })
    })
  })

})
<commit_msg>Remove default value of node in test spec.<commit_after>"use strict"

var _         = require("lodash")
  , expect    = require("chai").expect
  , ubivar    = require("../ubivar")

describe("Ubivar", function() {

  var rootProps = ["_api"]
    , subProps  = ["auth","protocol","timeout","resources","revokedCerts","headers","request"]
    , methods   = ["create", "retrieve", "update", "del", "list"]
    , resources = ubivar.get("resources")

  this.timeout(20000)

  describe("Properties", function() {
    _.each(rootProps, function(key){
      it("Should have ."+key, function(){
        expect(ubivar[key]).to.exist
      })
    })

    _.each(subProps, function(key){
      it("Should have ._api."+key, function(){
        expect(ubivar._api[key]).to.exist
      })
    })

    it("Should have Resources", function() {
      var nResources = resources.length - rootProps.length
      expect(nResources > 0).to.be.true
    })
    describe("Timeout", function(){
      it("Should allow to change default value", function(){
        ubivar.set("timeout", 900);
        expect(ubivar.get('timeout')).to.equal(900)
      })

      it("Should allow a reset to default by setting to 'null'", function(){
        ubivar.set("timeout", null)
        expect(ubivar.get('timeout')).to.equal(require('http').createServer().timeout)
      })
    })
  })

  describe("Methods", function() {
    it("Should have accessor methods", function() {
        expect(ubivar.get).to.exist
        expect(ubivar.set).to.exist
    })

    _.each(subProps, function(key){
      it("Should get and set property ._api."+key, function(){
        var oldValue  = ubivar.get(key)
          , newValue  = ""+Math.random()
        ubivar.set(key, newValue)
        expect(ubivar.get(key)).to.equal(newValue)
        ubivar.set(key, oldValue)
        expect(ubivar.get(key)).to.equal(oldValue)
      })
    })
  })

})
<|endoftext|>


<commit_before>/**
 * The Tooltip Component
 *
 * @module aui-tooltip
 */

var BODY_CONTENT = 'bodyContent',
    BOUNDING_BOX = 'boundingBox',
    CONTENT_BOX = 'contentBox',
    DURATION = 'duration',
    FORMATTER = 'formatter',
    HOVER = 'hover',
    IN = 'in',
    MOUSEENTER = 'mouseenter',
    MOUSELEAVE = 'mouseleave',
    OPACITY = 'opacity',
    STICK_DURATION = 'stickDuration',
    TITLE = 'title',
    TOOLTIP = 'tooltip',
    TRIGGER = 'trigger',
    VISIBLE = 'visible',

    _DATA_TITLE = 'data-title',

    getClassName = A.getClassName,

    CSS_TOOLTIP_ARROW = getClassName('tooltip-arrow'),
    CSS_TOOLTIP_INNER = getClassName('tooltip-inner');

/**
 * A base class for Tooltip.
 *
 * Check the [live demo](http://alloyui.com/examples/tooltip/).
 *
 * @class A.Tooltip
 * @extends Widget
 * @uses A.WidgetCssClass, A.WidgetPosition, A.WidgetStdMod, A.WidgetToggle,
 *     A.WidgetAutohide, A.WidgetToolbars, A.WidgetPositionAlign,
 *     A.WidgetPositionAlignSuggestion, A.WidgetPositionConstrain, A.WidgetStack
 * @param {Object} config Object literal specifying widget configuration
 *     properties.
 * @constructor
 * @include http://alloyui.com/examples/tooltip/basic-markup.html
 * @include http://alloyui.com/examples/tooltip/basic.js
 */
A.Tooltip = A.Base.create(TOOLTIP, A.Widget, [
    A.WidgetCssClass,
    A.WidgetPosition,
    A.WidgetStdMod,
    A.WidgetToggle,
    A.WidgetAutohide,
    A.WidgetPositionAlign,
    A.WidgetPositionAlignSuggestion,
    A.WidgetPositionConstrain,
    A.WidgetStack,
    A.WidgetTrigger
], {
    /**
     * Stores the `Y.later` context object.
     *
     * @property _hideTimer
     * @type {Object}
     * @protected
     */
    _hideTimer: null,

    /**
     * Construction logic executed during Tooltip instantiation. Lifecycle.
     *
     * @method initializer
     * @protected
     */
    initializer: function() {
        var instance = this;

        A.after(instance._afterUiSetTrigger, instance, '_uiSetTrigger');
        A.after(instance._afterUiSetVisible, instance, '_uiSetVisible');
    },

    /**
     * Render the Tooltip component instance. Lifecycle.
     *
     * @method renderUI
     * @protected
     */
    renderUI: function() {
        var instance = this,
            boundingBox = instance.get(BOUNDING_BOX),
            contentBox = instance.get(CONTENT_BOX);

        contentBox.addClass(CSS_TOOLTIP_INNER);
        boundingBox.append(A.Tooltip.TEMPLATES.arrow);

        instance._afterUiSetVisible(instance.get(VISIBLE));
    },

    /**
     * Binds the events on the `Tooltip` UI. Lifecycle.
     *
     * @method bindUI
     * @protected
     */
    bindUI: function() {
        var instance = this;

        // Do not bind the synthetic hover event to the widget dom events
        // wrapper api. Hover bind method has a different method signature which
        // is not handled by widget yet. Bind to the `boundingBox` instead.
        instance.get(TRIGGER).on(
            HOVER,
            A.bind(this._onBoundingBoxMouseenter, this)
        );

        instance.get(BOUNDING_BOX).on(
            HOVER,
            A.bind(instance._onBoundingBoxMouseenter, instance),
            A.bind(instance._onBoundingBoxMouseleave, instance));
    },

    /**
     * Destructor lifecycle implementation for the Tooltip class.
     * Lifecycle.
     *
     * @method destructor
     * @protected
     */
    destructor: function() {
        var instance = this;

        instance._clearHideTimer();
    },

    /**
     * Fire after `boundingBox` style changes.
     *
     * @method _afterUiSetVisible
     * @param val
     * @protected
     */
    _afterUiSetVisible: function(val) {
        var instance = this;

        if (val) {
            instance._loadBodyContentFromTitle();
            instance._maybeShow();
        }
        else {
            instance._maybeHide();
        }
    },

    /**
     * Fire after `trigger` changes.
     *
     * @method _afterUiSetTrigger
     * @param val
     * @protected
     */
    _afterUiSetTrigger: function(val) {
        var instance = this;

        instance.suggestAlignment(val);
    },

    /**
     * Helper method called to clear the close timer.
     *
     * @method _clearHideTimer
     * @protected
     */
    _clearHideTimer: function() {
        if (this._hideTimer) {
            this._hideTimer.cancel();
            this._hideTimer = null;
        }
    },

    /**
     * Load tooltip content from trigger title attribute.
     *
     * @method _loadBodyContentFromTitle
     * @protected
     */
    _loadBodyContentFromTitle: function() {
        var instance = this,
            trigger,
            dataTitle,
            formatter,
            title;

        formatter = instance.get(FORMATTER);
        trigger = instance.get(TRIGGER);

        dataTitle = trigger.getAttribute(_DATA_TITLE);
        title = trigger.getAttribute(TITLE) || dataTitle;

        if (formatter) {
            title = formatter.call(instance, title);
        }

        if (!dataTitle) {
            trigger.removeAttribute(TITLE).setAttribute(_DATA_TITLE, title);
        }

        instance.setStdModContent(
            A.WidgetStdMod.BODY, trigger && title || instance.get(BODY_CONTENT));
    },

    /**
     * Maybe hides the tooltip if `stickDuration` do not prevent.
     *
     * @method _maybeHide
     * @protected
     */
    _maybeHide: function() {
        var instance = this,
            stickDuration;

        stickDuration = instance.get(STICK_DURATION);

        if (A.Lang.isNumber(stickDuration)) {
            instance._hideTimer = A.later(
                stickDuration, instance, instance._transition);
        }
        else {
            instance._transition();
        }
    },

    /**
     * Maybe shows the tooltip if `stickDuration` do not prevents.
     *
     * @method _maybeShow
     * @protected
     */
    _maybeShow: function() {
        this._transition(true);
    },

    /**
     * Handles `boundingBox` `mouseenter` events.
     *
     * @method _onBoundingBoxMouseenter
     * @param {EventFacade} event
     * @protected
     */
    _onBoundingBoxMouseenter: function() {
        this._clearHideTimer();
    },

    /**
     * Handles `boundingBox` `mouseleave` events.
     *
     * @method _onBoundingBoxMouseleave
     * @param {EventFacade} event
     * @protected
     */
    _onBoundingBoxMouseleave: function() {
        this._maybeHide();
    },

    /**
     * Shows or hides the tooltip depending on the passed parameter, when
     * no parameter is specified the default behavior is to hide the tooltip.
     *
     * @method _transition
     * @param  {Boolean} fadeIn When `true`, fades in the tooltip, otherwise
     *     fades out.
     * @protected
     */
    _transition: function(fadeIn) {
        var instance = this,
            boundingBox = instance.get(BOUNDING_BOX);

        boundingBox.transition({
                duration: instance.get(DURATION),
                opacity: fadeIn ? instance.get(OPACITY) : 0
            },
            function() {
                boundingBox.toggleClass(IN, fadeIn);
            }
        );
    }
}, {

    /**
     * Static property provides a string to identify the CSS prefix.
     *
     * @property CSS_PREFIX
     * @type String
     * @static
     */
    CSS_PREFIX: getClassName(TOOLTIP),

    /**
     * Static property used to define the default attribute
     * configuration for the Tooltip.
     *
     * @property ATTRS
     * @type Object
     * @static
     */
    ATTRS: {
        /**
         * Determine the tooltip constrain node.
         *
         * @attribute constrain
         * @default true
         * @type {Boolean|Node}
         */
        constrain: {
            value: true
        },

        /**
         * Determine the duration of the tooltip animation.
         *
         * @attribute duration
         * @default 0.15
         * @type {Number}
         */
        duration: {
            value: 0.15
        },

        /**
         * Format the title attribute before set the content of the tooltip.
         *
         * @attribute formatter
         * @type function
         */
        formatter: {
            validator: A.Lang.isFunction
        },

        /**
         * Determine the opacity of the tooltip.
         *
         * @attribute opacity
         * @default 0.8
         * @type {Number}
         */
        opacity: {
            value: 0.8
        },

        /**
         * Determine the duration for the tooltip to stick visibility after
         * the mouse leaves the trigger element. By default the stick duration
         * is not specified, therefore the tooltip starts the hide transition
         * synchronously.
         *
         * @attribute stickDuration
         * @type {Number}
         */
        stickDuration: {
            validator: A.Lang.isNumber
        },

        /**
         * DOM event to hide the tooltip.
         *
         * @attribute triggerHideEvent
         * @default mouseleave
         * @type String
         */
        triggerHideEvent: {
            value: MOUSELEAVE
        },

        /**
         * DOM event to show the tooltip.
         *
         * @attribute triggerShowEvent
         * @default mouseenter
         * @type String
         */
        triggerShowEvent: {
            value: MOUSEENTER
        }
    },

    /**
     * Static property provides a set of reusable templates.
     *
     * @property TEMPLATES
     * @type Object
     * @static
     */
    TEMPLATES: {
        arrow: '

'
    }
});
<commit_msg>AUI-1092 - SF<commit_after>/**
 * The Tooltip Component
 *
 * @module aui-tooltip
 */

var BODY_CONTENT = 'bodyContent',
    BOUNDING_BOX = 'boundingBox',
    CONTENT_BOX = 'contentBox',
    DURATION = 'duration',
    FORMATTER = 'formatter',
    HOVER = 'hover',
    IN = 'in',
    MOUSEENTER = 'mouseenter',
    MOUSELEAVE = 'mouseleave',
    OPACITY = 'opacity',
    STICK_DURATION = 'stickDuration',
    TITLE = 'title',
    TOOLTIP = 'tooltip',
    TRIGGER = 'trigger',
    VISIBLE = 'visible',

    _DATA_TITLE = 'data-title',

    getClassName = A.getClassName,

    CSS_TOOLTIP_ARROW = getClassName('tooltip-arrow'),
    CSS_TOOLTIP_INNER = getClassName('tooltip-inner');

/**
 * A base class for Tooltip.
 *
 * Check the [live demo](http://alloyui.com/examples/tooltip/).
 *
 * @class A.Tooltip
 * @extends Widget
 * @uses A.WidgetCssClass, A.WidgetPosition, A.WidgetStdMod, A.WidgetToggle,
 *     A.WidgetAutohide, A.WidgetToolbars, A.WidgetPositionAlign,
 *     A.WidgetPositionAlignSuggestion, A.WidgetPositionConstrain, A.WidgetStack
 * @param {Object} config Object literal specifying widget configuration
 *     properties.
 * @constructor
 * @include http://alloyui.com/examples/tooltip/basic-markup.html
 * @include http://alloyui.com/examples/tooltip/basic.js
 */
A.Tooltip = A.Base.create(TOOLTIP, A.Widget, [
    A.WidgetCssClass,
    A.WidgetPosition,
    A.WidgetStdMod,
    A.WidgetToggle,
    A.WidgetAutohide,
    A.WidgetPositionAlign,
    A.WidgetPositionAlignSuggestion,
    A.WidgetPositionConstrain,
    A.WidgetStack,
    A.WidgetTrigger
], {
    /**
     * Stores the `Y.later` context object.
     *
     * @property _hideTimer
     * @type {Object}
     * @protected
     */
    _hideTimer: null,

    /**
     * Construction logic executed during Tooltip instantiation. Lifecycle.
     *
     * @method initializer
     * @protected
     */
    initializer: function() {
        var instance = this;

        A.after(instance._afterUiSetTrigger, instance, '_uiSetTrigger');
        A.after(instance._afterUiSetVisible, instance, '_uiSetVisible');
    },

    /**
     * Render the Tooltip component instance. Lifecycle.
     *
     * @method renderUI
     * @protected
     */
    renderUI: function() {
        var instance = this,
            boundingBox = instance.get(BOUNDING_BOX),
            contentBox = instance.get(CONTENT_BOX);

        contentBox.addClass(CSS_TOOLTIP_INNER);
        boundingBox.append(A.Tooltip.TEMPLATES.arrow);

        instance._afterUiSetVisible(instance.get(VISIBLE));
    },

    /**
     * Binds the events on the `Tooltip` UI. Lifecycle.
     *
     * @method bindUI
     * @protected
     */
    bindUI: function() {
        var instance = this;

        // Do not bind the synthetic hover event to the widget dom events
        // wrapper api. Hover bind method has a different method signature which
        // is not handled by widget yet. Bind to the `boundingBox` instead.
        instance.get(TRIGGER).on(
            HOVER,
            A.bind(instance._onBoundingBoxMouseenter, this)
        );

        instance.get(BOUNDING_BOX).on(
            HOVER,
            A.bind(instance._onBoundingBoxMouseenter, instance),
            A.bind(instance._onBoundingBoxMouseleave, instance));
    },

    /**
     * Destructor lifecycle implementation for the Tooltip class.
     * Lifecycle.
     *
     * @method destructor
     * @protected
     */
    destructor: function() {
        var instance = this;

        instance._clearHideTimer();
    },

    /**
     * Fire after `boundingBox` style changes.
     *
     * @method _afterUiSetVisible
     * @param val
     * @protected
     */
    _afterUiSetVisible: function(val) {
        var instance = this;

        if (val) {
            instance._loadBodyContentFromTitle();
            instance._maybeShow();
        }
        else {
            instance._maybeHide();
        }
    },

    /**
     * Fire after `trigger` changes.
     *
     * @method _afterUiSetTrigger
     * @param val
     * @protected
     */
    _afterUiSetTrigger: function(val) {
        var instance = this;

        instance.suggestAlignment(val);
    },

    /**
     * Helper method called to clear the close timer.
     *
     * @method _clearHideTimer
     * @protected
     */
    _clearHideTimer: function() {
        var instance = this;

        if (instance._hideTimer) {
            instance._hideTimer.cancel();
            instance._hideTimer = null;
        }
    },

    /**
     * Load tooltip content from trigger title attribute.
     *
     * @method _loadBodyContentFromTitle
     * @protected
     */
    _loadBodyContentFromTitle: function() {
        var instance = this,
            trigger,
            dataTitle,
            formatter,
            title;

        formatter = instance.get(FORMATTER);
        trigger = instance.get(TRIGGER);

        dataTitle = trigger.getAttribute(_DATA_TITLE);
        title = trigger.getAttribute(TITLE) || dataTitle;

        if (formatter) {
            title = formatter.call(instance, title);
        }

        if (!dataTitle) {
            trigger.removeAttribute(TITLE).setAttribute(_DATA_TITLE, title);
        }

        instance.setStdModContent(
            A.WidgetStdMod.BODY, trigger && title || instance.get(BODY_CONTENT));
    },

    /**
     * Maybe hides the tooltip if `stickDuration` do not prevent.
     *
     * @method _maybeHide
     * @protected
     */
    _maybeHide: function() {
        var instance = this,
            stickDuration;

        stickDuration = instance.get(STICK_DURATION);

        if (A.Lang.isNumber(stickDuration)) {
            instance._hideTimer = A.later(
                stickDuration, instance, instance._transition);
        }
        else {
            instance._transition();
        }
    },

    /**
     * Maybe shows the tooltip if `stickDuration` do not prevents.
     *
     * @method _maybeShow
     * @protected
     */
    _maybeShow: function() {
        var instance = this;

        instance._transition(true);
    },

    /**
     * Handles `boundingBox` `mouseenter` events.
     *
     * @method _onBoundingBoxMouseenter
     * @param {EventFacade} event
     * @protected
     */
    _onBoundingBoxMouseenter: function() {
        var instance = this;

        instance._clearHideTimer();
    },

    /**
     * Handles `boundingBox` `mouseleave` events.
     *
     * @method _onBoundingBoxMouseleave
     * @param {EventFacade} event
     * @protected
     */
    _onBoundingBoxMouseleave: function() {
        var instance = this;

        instance._maybeHide();
    },

    /**
     * Shows or hides the tooltip depending on the passed parameter, when
     * no parameter is specified the default behavior is to hide the tooltip.
     *
     * @method _transition
     * @param  {Boolean} fadeIn When `true`, fades in the tooltip, otherwise
     *     fades out.
     * @protected
     */
    _transition: function(fadeIn) {
        var instance = this,
            boundingBox = instance.get(BOUNDING_BOX);

        boundingBox.transition({
                duration: instance.get(DURATION),
                opacity: fadeIn ? instance.get(OPACITY) : 0
            },
            function() {
                boundingBox.toggleClass(IN, fadeIn);
            }
        );
    }
}, {

    /**
     * Static property provides a string to identify the CSS prefix.
     *
     * @property CSS_PREFIX
     * @type String
     * @static
     */
    CSS_PREFIX: getClassName(TOOLTIP),

    /**
     * Static property used to define the default attribute
     * configuration for the Tooltip.
     *
     * @property ATTRS
     * @type Object
     * @static
     */
    ATTRS: {
        /**
         * Determine the tooltip constrain node.
         *
         * @attribute constrain
         * @default true
         * @type {Boolean|Node}
         */
        constrain: {
            value: true
        },

        /**
         * Determine the duration of the tooltip animation.
         *
         * @attribute duration
         * @default 0.15
         * @type {Number}
         */
        duration: {
            value: 0.15
        },

        /**
         * Format the title attribute before set the content of the tooltip.
         *
         * @attribute formatter
         * @type function
         */
        formatter: {
            validator: A.Lang.isFunction
        },

        /**
         * Determine the opacity of the tooltip.
         *
         * @attribute opacity
         * @default 0.8
         * @type {Number}
         */
        opacity: {
            value: 0.8
        },

        /**
         * Determine the duration for the tooltip to stick visibility after
         * the mouse leaves the trigger element. By default the stick duration
         * is not specified, therefore the tooltip starts the hide transition
         * synchronously.
         *
         * @attribute stickDuration
         * @type {Number}
         */
        stickDuration: {
            validator: A.Lang.isNumber
        },

        /**
         * DOM event to hide the tooltip.
         *
         * @attribute triggerHideEvent
         * @default mouseleave
         * @type String
         */
        triggerHideEvent: {
            value: MOUSELEAVE
        },

        /**
         * DOM event to show the tooltip.
         *
         * @attribute triggerShowEvent
         * @default mouseenter
         * @type String
         */
        triggerShowEvent: {
            value: MOUSEENTER
        }
    },

    /**
     * Static property provides a set of reusable templates.
     *
     * @property TEMPLATES
     * @type Object
     * @static
     */
    TEMPLATES: {
        arrow: '

'
    }
});
<|endoftext|>


<commit_before>// For Chrome. Use a input box in background page to interact with clipboard.
function copyByPastboardCommand(text) {
  /** @type {HTMLTextAreaElement} */
  const textBox = document.getElementById('clipboard-access');
  textBox.value = text;
  textBox.select();
  document.execCommand('Copy');
  textBox.value = '';
}

// For Firefox. Use native API. This won't work on Chrome since it requires focused document.
// See https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Interact_with_the_clipboard
async function copyByNativeAPI(text) {
  return await navigator.clipboard.writeText(text);
}

/**
 *
 * @param {MarkdownResponse} response generated from markdown.js
 * @return {Promise} contains original response
 */
export async function copyMarkdownResponse(response) {
  const text = response.markdown;

  try {
    await copyByNativeAPI(text);
  } catch (error) {
    await copyByPastboardCommand(text);
  }
}
<commit_msg>Fix typo<commit_after>// For Chrome. Use a input box in background page to interact with clipboard.
function copyByClipboardCommand(text) {
  /** @type {HTMLTextAreaElement} */
  const textBox = document.getElementById('clipboard-access');
  textBox.value = text;
  textBox.select();
  document.execCommand('Copy');
  textBox.value = '';
}

// For Firefox. Use native API. This won't work on Chrome since it requires focused document.
// See https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Interact_with_the_clipboard
async function copyByNativeAPI(text) {
  return await navigator.clipboard.writeText(text);
}

/**
 *
 * @param {MarkdownResponse} response generated from markdown.js
 * @return {Promise} contains original response
 */
export async function copyMarkdownResponse(response) {
  const text = response.markdown;

  try {
    await copyByNativeAPI(text);
  } catch (error) {
    await copyByClipboardCommand(text);
  }
}
<|endoftext|>


<commit_before>/**
 * Tangram
 * Copyright 2009 Baidu Inc. All rights reserved.
 *
 * path: ui/combox/Combox.js
 * author: rocy
 * version: 1.0.0
 * date: 2010-12-17
 */

///import baidu.ui.combox;
///import baidu.ui.createUI;
///import baidu.ui.menubar.Menubar;
///import baidu.ui.create;
///import baidu.ui.menubar.Menubar$click;
///import baidu.ui.smartPosition.set;

///import baidu.dom.insertHTML;
///import baidu.dom.g;
///import baidu.dom.remove;

///import baidu.array.each;

///import baidu.fn.bind;

///import baidu.event.on;
///import baidu.event.un;

///import baidu.string.format;

/**
 * Combox 下拉列表框
 * @param {Object} [options]                   配置选项
 * @param {String} [options.width]             宽度
 * @param {String} [options.height]            高度
 * @param {Number} [options.zIndex = 1200]     z-index
 * @param {Boolean} [options.editable = true] 输入框是否可编辑
 * @param {Function} [options.filter]          数据过滤方法
 * @param {Object} [options.data]              combox数据，格式见example
 * @param {Object} [options.menu]              可由用户传入创建好的menu对象
 * @param {Function} [options.onitemclick]     menu条目点击响应事件
 * @param {Function} [options.onbeforeclose]   下拉框beforeclose 事件
 * @param {Function} [options.onclose]         下拉框close事件
 * @param {Function} [options.onbeforeopen]    下拉框beforeopen事件
 * @param {Function} [options.onopen]          下拉框open事件
 *
 * @example
 *

 * var options = {
 *   width: 168,
 *   data: [{
 *       content: "复制",
 *       title: "复制当前单元格"
 *   }, {
 *       content: '粘贴',
 *       title: "粘贴当前单元格"
 *   }],
 *   element: 'combox',
 *   autoRender: true
 * };
 * var combox = baidu.ui.create(baidu.ui.combox.Combox, options);
 */
baidu.ui.combox.Combox = baidu.ui.createUI(function (options){
  var me = this;
  me.data = me.data || [];
  me.menu = me.menu || false; //下拉menu,用于判断menu是否已存在
}).extend({
    uiType: "combox",
    editable: true,
    width: '',
    height: '',
    zIndex: 1200,

    /**
     * 过滤方法
     * @param {String} filterStr 需检索的字符串值
     * @param {Array} data 目标数据
     */
    filter: function(filterStr,data){
        var ret = [];
        baidu.array.each(data || this.data, function(dataItem){
            var strIndex = (dataItem.value || dataItem.content).indexOf(filterStr);
            if (strIndex >= 0) {
                ret.push(dataItem);
            }
        });
        return ret;
    },

    tplBody : ['
',
                    '<input id="#{inputid}" class="#{inputClass}" autocomplete="off" readOnly="readOnly"/>',
                    '',
               '
'].join(''),

    /**
     * 生成combox的html字符串代码
     * @return {String} 生成html字符串
     */
    getString: function(){
        var me = this;
        return baidu.format(me.tplBody, {
            id: me.getId(),
            "class": me.getClass(),
            inputClass: me.getClass('input'),
            arrowClass: me.getClass('arrow'),
            inputid: me.getId("input"),
            arrowid: me.getId("arrow"),
            stateHandler: me.statable ? me._getStateHandlerString() : '' // 是否会有更好的方式来使用ui.statable
        });
    },

    /**
     * 渲染控件
     * @param {Object} target 目标渲染对象
     */
    render: function(target){
        var me = this;
        if(me.getMain()){
            return ;
        }
        baidu.dom.insertHTML(me.renderMain(target || me.target), "beforeEnd", me.getString());
        me._createMenu(); //创建下拉menu
        me._enterTipMode();
        me.position && baidu.ui.smartPosition.set(target, me.position);
        me.dispatchEvent("onload");
    },

    /**
     * 给input添加keyup、focus事件，当触发事件，下拉框弹出相关项
     * @private
     */
    _enterTipMode : function(){
        var me = this,
            input = me.getInput();

        me._showMenuHandler = baidu.fn.bind(function(){
            var me = this;
            var input = me.getInput();
            me.menu.open();
            me.menu.update({//如果开启input可编辑模式，则智能筛选数据
                data: me.editable ? me.filter(input.value, me.data) : me.data
            });
        }, me);

        baidu.on(input, "focus", me._showMenuHandler);

        if(me.editable){
            input.readOnly = '';
            baidu.on(input, "keyup", me._showMenuHandler);
		}
    },

    /**
     * 创建下拉菜单
     * @private
     * @return {baidu.ui.menubar.Menubar} Menubar对象
     */
    _createMenu : function(){
        var me = this,
            body = me.getBody(),
            arrow = me.getArrow(),
            menuOptions = {
                width: me.width || body.offsetWidth,
                onitemclick: me.onitemclick,
                element: body,
                autoRender: true,
                data: me.data,
                onbeforeclose: me.onbeforeclose,
                onclose: me.onclose,
                onbeforeopen: me.onbeforeopen,
                onopen: me.onopen
            };

        if (!me.menu) {
            me.menu = baidu.ui.create(baidu.ui.menubar.Menubar, menuOptions);
            baidu.un(body, 'click', me.targetOpenHandler);
            me.menu.addEventListener("onitemclick", function(data){
                me.chooseItem(data)
            });
        }
        me.menu.close(true);

        me._showAllMenuHandler = baidu.fn.bind(function(){
            var me = this;
            me.menu.open();
            me.menu.update({
                data: me.data
            });
        }, me);
        baidu.on(arrow, 'click', me._showAllMenuHandler);
        return me.menu;
    },

    /**
     * 获取input元素
     * @return {HTMLElement} input元素
     */
    getInput : function(){
        return baidu.g(this.getId("input"));
    },

    /**
     * 获取下拉箭头元素
     * @return {HTMLElement} arrow元素
     */
    getArrow : function(){
        return baidu.g(this.getId("arrow"));
    },

    /**
     * 响应条目被选择,并发出 onitemchosen 事件
     * @param {Object} data 选中的数据
     */
    chooseItem : function(data){
        var me = this;
        me.getInput().value = data.value.content;
    },

    /**
     * 设置input的值
     * @param {Object} value 值
     */
    setValue:function(value){
        this.getInput().value = value;
    },

    /**
     * 销毁Combox
     */
    dispose: function(){
        var me = this;
        baidu.un(me.getInput(), "keyup", me._showMenuHandler);
        baidu.un(me.getInput(), "focus", me._showMenuHandler);
        baidu.un(me.getArrow(), 'click', me._showAllMenuHandler);

        if (me.getMain()) {
            baidu.dom.remove(me.getMain());
        }
        baidu.lang.Class.prototype.dispose.call(me);
    }
});<commit_msg>修改baidu.object.extend到UI控件中自带的extend<commit_after>/**
 * Tangram
 * Copyright 2009 Baidu Inc. All rights reserved.
 *
 * path: ui/combox/Combox.js
 * author: rocy
 * version: 1.0.0
 * date: 2010-12-17
 */

///import baidu.ui.combox;
///import baidu.ui.createUI;
///import baidu.ui.menubar.Menubar;
///import baidu.ui.create;
///import baidu.ui.menubar.Menubar$click;
///import baidu.ui.smartPosition.set;

///import baidu.dom.insertHTML;
///import baidu.dom.g;
///import baidu.dom.remove;

///import baidu.array.each;

///import baidu.fn.bind;

///import baidu.event.on;
///import baidu.event.un;

///import baidu.string.format;

/**
 * Combox 下拉列表框
 * @param {Object} [options]                   配置选项
 * @param {String} [options.width]             宽度
 * @param {String} [options.height]            高度
 * @param {Number} [options.zIndex = 1200]     z-index
 * @param {Boolean} [options.editable = true] 输入框是否可编辑
 * @param {Function} [options.filter]          数据过滤方法
 * @param {Object} [options.data]              combox数据，格式见example
 * @param {Object} [options.menu]              可由用户传入创建好的menu对象
 * @param {Function} [options.onitemclick]     menu条目点击响应事件
 * @param {Function} [options.onbeforeclose]   下拉框beforeclose 事件
 * @param {Function} [options.onclose]         下拉框close事件
 * @param {Function} [options.onbeforeopen]    下拉框beforeopen事件
 * @param {Function} [options.onopen]          下拉框open事件
 *
 * @example
 *

 * var options = {
 *   width: 168,
 *   data: [{
 *       content: "复制",
 *       title: "复制当前单元格"
 *   }, {
 *       content: '粘贴',
 *       title: "粘贴当前单元格"
 *   }],
 *   element: 'combox',
 *   autoRender: true
 * };
 * var combox = baidu.ui.create(baidu.ui.combox.Combox, options);
 */
baidu.ui.combox.Combox = baidu.ui.createUI(function (options){
  var me = this;
  me.data = me.data || [];
  me.menu = me.menu || false; //下拉menu,用于判断menu是否已存在
}).extend({
    uiType: "combox",
    editable: true,
    width: '',
    height: '',
    zIndex: 1200,

    /**
     * 过滤方法
     * @param {String} filterStr 需检索的字符串值
     * @param {Array} data 目标数据
     */
    filter: function(filterStr,data){
        var ret = [];
        baidu.array.each(data || this.data, function(dataItem){
            var strIndex = (dataItem.value || dataItem.content).indexOf(filterStr);
            if (strIndex >= 0) {
                ret.push(dataItem);
            }
        });
        return ret;
    },

    tplBody : ['
',
                    '<input id="#{inputid}" class="#{inputClass}" autocomplete="off" readOnly="readOnly"/>',
                    '',
               '
'].join(''),

    /**
     * 生成combox的html字符串代码
     * @return {String} 生成html字符串
     */
    getString: function(){
        var me = this;
        return baidu.format(me.tplBody, {
            id: me.getId(),
            "class": me.getClass(),
            inputClass: me.getClass('input'),
            arrowClass: me.getClass('arrow'),
            inputid: me.getId("input"),
            arrowid: me.getId("arrow"),
            stateHandler: me.statable ? me._getStateHandlerString() : '' // 是否会有更好的方式来使用ui.statable
        });
    },

    /**
     * 渲染控件
     * @param {Object} target 目标渲染对象
     */
    render: function(target){
        var me = this;
        if(me.getMain()){
            return ;
        }
        baidu.dom.insertHTML(me.renderMain(target || me.target), "beforeEnd", me.getString());
        me._createMenu(); //创建下拉menu
        me._enterTipMode();
        me.position && baidu.ui.smartPosition.set(target, me.position);
        me.dispatchEvent("onload");
    },

    /**
     * 给input添加keyup、focus事件，当触发事件，下拉框弹出相关项
     * @private
     */
    _enterTipMode : function(){
        var me = this,
            input = me.getInput();

        me._showMenuHandler = baidu.fn.bind(function(){
            var me = this;
            var input = me.getInput();
            me.menu.open();
            me.menu.update({//如果开启input可编辑模式，则智能筛选数据
                data: me.editable ? me.filter(input.value, me.data) : me.data
            });
        }, me);

        baidu.on(input, "focus", me._showMenuHandler);

        if(me.editable){
            input.readOnly = '';
            baidu.on(input, "keyup", me._showMenuHandler);
		}
    },

    /**
     * 创建下拉菜单
     * @private
     * @return {baidu.ui.menubar.Menubar} Menubar对象
     */
    _createMenu : function(){
        var me = this,
            body = me.getBody(),
            arrow = me.getArrow(),
            menuOptions = {
                width: me.width || body.offsetWidth,
                onitemclick: me.onitemclick,
                element: body,
                autoRender: true,
                data: me.data,
                onbeforeclose: me.onbeforeclose,
                onclose: me.onclose,
                onbeforeopen: me.onbeforeopen,
                onopen: me.onopen
            };

        if (!me.menu) {
            me.menu = baidu.ui.create(baidu.ui.menubar.Menubar, menuOptions);
            baidu.un(body, 'click', me.targetOpenHandler);
            me.menu.addEventListener("onitemclick", function(data){
                me.chooseItem(data)
            });
        }
        me.menu.close(true);

        me._showAllMenuHandler = baidu.fn.bind(function(){
            var me = this;
            me.menu.open();
            me.menu.update({
                data: me.data
            });
        }, me);
        baidu.on(arrow, 'click', me._showAllMenuHandler);
        return me.menu;
    },

    /**
     * 获取input元素
     * @return {HTMLElement} input元素
     */
    getInput : function(){
        return baidu.g(this.getId("input"));
    },

    /**
     * 获取下拉箭头元素
     * @return {HTMLElement} arrow元素
     */
    getArrow : function(){
        return baidu.g(this.getId("arrow"));
    },

    /**
     * 响应条目被选择,并发出 onitemchosen 事件
     * @param {Object} data 选中的数据
     */
    chooseItem : function(data){
        var me = this;
        me.getInput().value = data.value.content;
    },

    /**
     * 设置input的值
     * @param {Object} value 值
     */
    setValue:function(value){
        this.getInput().value = value;
    },

    /**
     * 销毁Combox
     */
    dispose: function(){
        var me = this;
        baidu.un(me.getInput(), "keyup", me._showMenuHandler);
        baidu.un(me.getInput(), "focus", me._showMenuHandler);
        baidu.un(me.getArrow(), 'click', me._showAllMenuHandler);

        if (me.getMain()) {
            baidu.dom.remove(me.getMain());
        }
        baidu.lang.Class.prototype.dispose.call(me);
    }
});<|endoftext|>