<commit_before>var needle = require("needle")
var helpers = require("../helpers")

function getImdbResults(searchTerm, cb) {

    var url = 'https://sg.media-imdb.com/suggests/' + searchTerm.charAt(0).toLowerCase() + '/' + encodeURIComponent(searchTerm)  + '.json'

    needle.get(url, function(err, res) {

        if (!err && res.statusCode == 200 && res.body) {

            var imdbParse = JSON.parse(res.body.toString().match(/{.*}/g))

            var results = imdbParse.d

            cb(results && results.length ? results : false, url)

        } else
            cb(false)
    })
}

function imdbFind(task, cb, loose) {
    var fail = function() { cb(null, null) }
    // we first search imdb with a name + year query (if we have a year set)
    // if it fails we search by name query only (looser)
    // we shouldn't retry if there's no year, because it searched
    // without a year in the first attempt already
    var shouldRetry = !loose && task.year

    var retry = function() {
        return shouldRetry ? imdbFind(task, cb, true) : fail()
    }

    var searchTerm = shouldRetry ? task.name + ' ' + task.year : task.name

    getImdbResults(searchTerm, function(results, url) {
        if (results)
            matchSimilar(results, function(result) {
                if (result)
                    cb(null, result, { match: url })
                else
                    retry()
            })
        else
            retry()
    })

    var matchSimilar = function(results, callback) {

        // similarity target for levenshtein distance
        var similarityGoal = 0.6

        var pick, secondBest, firstResult

        results.some(function(result) {

            // make result readable, the imdb result keys make no sense otherwise
            var res = {
                id: result.id,
                name: result.l,
                year: result.y,
                type: result.q,
                yearRange: result.yr,
                image: result.i ? {
                    src: result.i[0],
                    width: result.i[1],
                    height: result.i[2]
                } : undefined,
                starring: result.s,
            }

            var movieMatch = task.type == 'movie' && res.type == 'feature'

            var seriesMatch = task.type == 'series' && ['TV series', 'TV mini-series'].indexOf(res.type) > -1

            if (!task.type || movieMatch || seriesMatch) {

                if (helpers.yearSimilar(task.year, res.year)) {

                    // try to match by levenshtein distance
                    var similarity = helpers.nameSimilar(task.name, res.name)

                    if (similarity > similarityGoal) {
                        if (!pick || (pick && similarity > pick.similarity)) {
                            pick = res
                            pick.similarity = similarity
                        }
                    }

                    // fallback to non-levenshtein distance logic:
                    // if the result name includes the task name or vice-versa (at end or start)
                    if (!secondBest && helpers.nameAlmostSimilar(task.name, res.name))
                        secondBest = res

                    // if nothing else is found, pick first result
                    // (because what we're searching for might be the alternative name of the first result)
                    // this is ignored if strict mode enabled
                    if (!firstResult && !task.strict)
                        firstResult = res
                }

            }
        })

        // if pick doesn't include the task name (because it's only the most similar textually)
        // then pick the second best result (because it does)
        // example scenario:
        // task.name = 'Ghost Hound'
        // pick.name = 'Ghost Hunt'
        // secondBest.name = 'Shinreigari: Ghost Hound'

        if (secondBest && pick) {
            if (!helpers.nameAlmostSimilar(task.name, pick.name))
                pick = secondBest
        }

        callback(pick || secondBest || firstResult || null)
    }

}

 module.exports = imdbFind
<commit_msg>Add Support for "TV Special"<commit_after>var needle = require("needle")
var helpers = require("../helpers")

function getImdbResults(searchTerm, cb) {

    var url = 'https://sg.media-imdb.com/suggests/' + searchTerm.charAt(0).toLowerCase() + '/' + encodeURIComponent(searchTerm)  + '.json'

    needle.get(url, function(err, res) {

        if (!err && res.statusCode == 200 && res.body) {

            var imdbParse = JSON.parse(res.body.toString().match(/{.*}/g))

            var results = imdbParse.d

            cb(results && results.length ? results : false, url)

        } else
            cb(false)
    })
}

function imdbFind(task, cb, loose) {
    var fail = function() { cb(null, null) }
    // we first search imdb with a name + year query (if we have a year set)
    // if it fails we search by name query only (looser)
    // we shouldn't retry if there's no year, because it searched
    // without a year in the first attempt already
    var shouldRetry = !loose && task.year

    var retry = function() {
        return shouldRetry ? imdbFind(task, cb, true) : fail()
    }

    var searchTerm = shouldRetry ? task.name + ' ' + task.year : task.name

    getImdbResults(searchTerm, function(results, url) {
        if (results)
            matchSimilar(results, function(result) {
                if (result)
                    cb(null, result, { match: url })
                else
                    retry()
            })
        else
            retry()
    })

    var matchSimilar = function(results, callback) {

        // similarity target for levenshtein distance
        var similarityGoal = 0.6

        var pick, secondBest, firstResult

        results.some(function(result) {

            // make result readable, the imdb result keys make no sense otherwise
            var res = {
                id: result.id,
                name: result.l,
                year: result.y,
                type: result.q,
                yearRange: result.yr,
                image: result.i ? {
                    src: result.i[0],
                    width: result.i[1],
                    height: result.i[2]
                } : undefined,
                starring: result.s,
            }

            var movieMatch = task.type == 'movie' && ['feature', 'TV special'].indexOf(res.type) > -1

            var seriesMatch = task.type == 'series' && ['TV series', 'TV mini-series'].indexOf(res.type) > -1

            if (!task.type || movieMatch || seriesMatch) {

                if (helpers.yearSimilar(task.year, res.year)) {

                    // try to match by levenshtein distance
                    var similarity = helpers.nameSimilar(task.name, res.name)

                    if (similarity > similarityGoal) {
                        if (!pick || (pick && similarity > pick.similarity)) {
                            pick = res
                            pick.similarity = similarity
                        }
                    }

                    // fallback to non-levenshtein distance logic:
                    // if the result name includes the task name or vice-versa (at end or start)
                    if (!secondBest && helpers.nameAlmostSimilar(task.name, res.name))
                        secondBest = res

                    // if nothing else is found, pick first result
                    // (because what we're searching for might be the alternative name of the first result)
                    // this is ignored if strict mode enabled
                    if (!firstResult && !task.strict)
                        firstResult = res
                }

            }
        })

        // if pick doesn't include the task name (because it's only the most similar textually)
        // then pick the second best result (because it does)
        // example scenario:
        // task.name = 'Ghost Hound'
        // pick.name = 'Ghost Hunt'
        // secondBest.name = 'Shinreigari: Ghost Hound'

        if (secondBest && pick) {
            if (!helpers.nameAlmostSimilar(task.name, pick.name))
                pick = secondBest
        }

        callback(pick || secondBest || firstResult || null)
    }

}

 module.exports = imdbFind
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import autobind from 'core-decorators/es/autobind';
import DateFormat from 'dateformat';
import { Button, Intent } from '@blueprintjs/core';
import { graphql } from 'react-apollo';
import MarkdownRenderer from '../../../../../../lib/react/components/MarkdownRenderer';
import NotificationToaster from '../../../components/NotificationToaster';
import Story from '../../../components/Story';
import { setStoryPublished } from '../../../../graphql/story';
import StoryDelete from './StoryDelete';

@graphql(setStoryPublished('ok'), { name: 'MutationSetStoryPublished' })
@autobind
class StoryCard extends React.Component {
	static propTypes = {
		story: PropTypes.shape({
			_id: PropTypes.string.isRequired,
			type: PropTypes.string.isRequired,
			createdBy: PropTypes.string.isRequired,
			published: PropTypes.bool.isRequired,
			publishDate: PropTypes.bool,
			iconName: PropTypes.string.isRequired,
			intent: PropTypes.string.isRequired,
			content: PropTypes.string.isRequired,
			likes: PropTypes.array.isRequired,
		}),
		setPublishing: PropTypes.func.isRequired,
		publishing: PropTypes.bool.isRequired,
		refetch: PropTypes.func.isRequired
	}

	state = {
		publishing: false,
		deleting: false
	}

	openProfile(e) {
		this.props.renderProfile(this.props.story);
	}

	_setPublishing(publishing) {
		this.setState({publishing});
		this.props.setPublishing(publishing);
	}

	setDeleting(deleting) {
		this.setState({ deleting });
	}

	async togglePublish() {
		this._setPublishing(true);
		try {
			await this.props.MutationSetStoryPublished({
				variables: {
					storyId: this.props.story._id,
					publish: !this.props.story.published
				}
			});
			await this.props.refetch();
		}
		catch (err) {
			NotificationToaster.show({
				intent: Intent.DANGER,
				message: err.toString()
			});
		}
		this._setPublishing(false);
	}

	render() {
		const { _id, type, createdBy, published, publishDate, iconName, intent, content, likes } = this.props.story;
		let publishLabel;

		if (this.state.publishing) {
			if (published) publishLabel = 'Unpublishing...';
			else publishLabel = 'Publishing...';
		}
		else {
			if (published) publishLabel = 'Unpublish';
			else publishLabel = 'Publish';
		}

		return (

					<Story story={this.props.story}/>

						{published ? 'Published' : 'Not published'} | {DateFormat(publishDate ? new Date(publishDate) : new Date(), 'mmm dd hh:MM TT')}

						<Button className='pt-minimal pt-small pt-intent-primary' text={publishLabel} disabled={this.props.publishing || this.state.deleting} onClick={this.togglePublish}/>
						<Button className='pt-minimal pt-small pt-intent-primary' text='Edit' disabled={this.state.deleting}/>
						<StoryDelete storyId={this.props.story._id} refetch={this.props.refetch} setDeleting={this.setDeleting}/>

		);
	}
}

export default StoryCard;
<commit_msg>[FIX] Fix wrong proptype declaration<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import autobind from 'core-decorators/es/autobind';
import DateFormat from 'dateformat';
import { Button, Intent } from '@blueprintjs/core';
import { graphql } from 'react-apollo';
import MarkdownRenderer from '../../../../../../lib/react/components/MarkdownRenderer';
import NotificationToaster from '../../../components/NotificationToaster';
import Story from '../../../components/Story';
import { setStoryPublished } from '../../../../graphql/story';
import StoryDelete from './StoryDelete';

@graphql(setStoryPublished('ok'), { name: 'MutationSetStoryPublished' })
@autobind
class StoryCard extends React.Component {
	static propTypes = {
		story: PropTypes.shape({
			_id: PropTypes.string.isRequired,
			type: PropTypes.string.isRequired,
			createdBy: PropTypes.string.isRequired,
			published: PropTypes.bool.isRequired,
			publishDate: PropTypes.string,
			iconName: PropTypes.string.isRequired,
			intent: PropTypes.string.isRequired,
			content: PropTypes.string.isRequired,
			likes: PropTypes.array.isRequired,
		}),
		setPublishing: PropTypes.func.isRequired,
		publishing: PropTypes.bool.isRequired,
		refetch: PropTypes.func.isRequired
	}

	state = {
		publishing: false,
		deleting: false
	}

	openProfile(e) {
		this.props.renderProfile(this.props.story);
	}

	_setPublishing(publishing) {
		this.setState({publishing});
		this.props.setPublishing(publishing);
	}

	setDeleting(deleting) {
		this.setState({ deleting });
	}

	async togglePublish() {
		this._setPublishing(true);
		try {
			await this.props.MutationSetStoryPublished({
				variables: {
					storyId: this.props.story._id,
					publish: !this.props.story.published
				}
			});
			await this.props.refetch();
		}
		catch (err) {
			NotificationToaster.show({
				intent: Intent.DANGER,
				message: err.toString()
			});
		}
		this._setPublishing(false);
	}

	render() {
		const { _id, type, createdBy, published, publishDate, iconName, intent, content, likes } = this.props.story;
		let publishLabel;

		if (this.state.publishing) {
			if (published) publishLabel = 'Unpublishing...';
			else publishLabel = 'Publishing...';
		}
		else {
			if (published) publishLabel = 'Unpublish';
			else publishLabel = 'Publish';
		}

		return (

					<Story story={this.props.story}/>

						{published ? 'Published' : 'Not published'} | {DateFormat(publishDate ? new Date(publishDate) : new Date(), 'mmm dd hh:MM TT')}

						<Button className='pt-minimal pt-small pt-intent-primary' text={publishLabel} disabled={this.props.publishing || this.state.deleting} onClick={this.togglePublish}/>
						<Button className='pt-minimal pt-small pt-intent-primary' text='Edit' disabled={this.state.deleting}/>
						<StoryDelete storyId={this.props.story._id} refetch={this.props.refetch} setDeleting={this.setDeleting}/>

		);
	}
}

export default StoryCard;
<|endoftext|>


<commit_before>// chapter 3 tests
//

describe("Chapter 3 Specs", function(){

  describe("show the 5 Javascript data types", function(){

    // test by creating variable, then checking "typeof" for that variable

    xit("Undefined", function(){});
    xit("Boolean",  function(){});
    xit("String", function(){});
    xit("Number", function(){});
    xit("Object", function(){});
    xit("Function", function(){});
  });

  describe("apply use strict when declaring variables", function(){
    xit("page 29 - declare variable without 'use strict'");
    xit("page 29 - declare variable with 'use strict'");
  });

  describe("Boolean data types", function(){
    xit("page 34 - convert boolean types to other data types", function(){});
  });

  describe("Number data types", function(){
    xit("page 35 - init integer", function(){});
    xit("page 35 - init hex", function(){});
    xit("page 36 - init floating point", function(){});
    xit("page 37 - init NaN", function(){});
  });

  describe("String data types", function(){
    it("page 42 - can use single quotes", function(){
      var one = '1';
      expect(one).toBe("1");
    });
    it("page 42 - can use double quotes", function(){
      var two = "2";
      expect(two).toBe('2');
    });
    it("page 43 - get the length of a string", function(){
      var string_of_3_letters = "abc";
      expect(string_of_3_letters.length).toBe(3);

      var empty_string = "";
      expect(empty_string.length).toBe(0);
    });

    describe("page 43 - toString() conversions of non Strings", function(){
      var number = 11;
      it("toString() should return base10 string", function() {
        expect(number.toString()).toBe("11");
      });
      it("toString() with radix 2 should return base2", function() {
        expect(number.toString(2)).toBe("1011");
      });
      it("toString() with radix 11 should return base11", function() {
        expect(number.toString(11)).toBe("10");
      });
      it("toString() with radix 16 should return hexidecimal", function() {
        expect(number.toString(16)).toBe("b");
      });
    });

    describe("page 44 - String() conversions", function(){
    });
  });

  describe("Object data type", function() {

    describe("page 45 - default object properities", function() {
    });

    describe("page 45 - default object methods", function() {
    });
  });

  describe("Operators", function() {

    describe("Unary operators", function() {
      it("page 46: ++ should increment by 1", function() {
        var i = 0;
        i++;
        expect(i).toBe(1);
      });

      it("page 46: -- should decrement by 1", function() {
        var i = 0;
        i--;
        expect(i).toBe(-1);
      });
      xit("page 47: ++ increment before", function() {});
      xit("page 47: ++ increment after", function() {});
    });

    describe("Bitwise operators", function() {
    });

    describe("Boolean operators", function() {
    });

    describe("Multiplicative operators", function() {
    });

    describe("Additive operators", function() {
    });

    describe("Equality operators", function() {
    });

    describe("Conditional or ternary operators", function() {
      xit("should how it's the same as if ... else", function() {});
    });

    describe("If statement", function() {});

    describe("Do-While statement", function() {});

    describe("While statement", function() {});

    describe("For statement", function() {});

    describe("For-In statement", function() {});

    describe("Break in loop functions", function() {});

    describe("Conintue in loop functions", function() {});

    describe("Breaks AND Conintues in loop functions", function() {});

    describe("With statement", function() {});

    describe("Switch statement", function() {});

    describe("Functions", function(){
      describe("argments", function() {});
      describe("overloading", function() {});
    });

  });
})
<commit_msg>Tests for JavaScript data types<commit_after>// chapter 3 tests
//

describe("Chapter 3 Specs", function(){

  describe("show the 5 Javascript data types", function(){

    // test by creating variable, then checking "typeof" for that variable

    it("Undefined", function(){
      expect(typeof(undefined)).toBe("undefined");
    });
    it("Boolean",  function(){
      var isBoolean = true;
      expect(typeof(isBoolean)).toBe("boolean");
    });
    it("String", function(){
      var name = "John Doe";
      expect(typeof(name)).toBe("string");
    });
    it("Number", function(){
      var age = 36;
      expect(typeof(age)).toBe("number");
    });
    it("Object", function(){
      var person = new Object();
      expect(typeof(person)).toBe("object");
    });
    it("Function", function(){
      function isOld(age)
      {
        alert("Dude you're aging!");
      }
      expect(typeof(isOld)).toBe("function");
    });
  });

  describe("apply use strict when declaring variables", function(){
    xit("page 29 - declare variable without 'use strict'");
    xit("page 29 - declare variable with 'use strict'");
  });

  describe("Boolean data types", function(){
    xit("page 34 - convert boolean types to other data types", function(){});
  });

  describe("Number data types", function(){
    xit("page 35 - init integer", function(){});
    xit("page 35 - init hex", function(){});
    xit("page 36 - init floating point", function(){});
    xit("page 37 - init NaN", function(){});
  });

  describe("String data types", function(){
    it("page 42 - can use single quotes", function(){
      var one = '1';
      expect(one).toBe("1");
    });
    it("page 42 - can use double quotes", function(){
      var two = "2";
      expect(two).toBe('2');
    });
    it("page 43 - get the length of a string", function(){
      var string_of_3_letters = "abc";
      expect(string_of_3_letters.length).toBe(3);

      var empty_string = "";
      expect(empty_string.length).toBe(0);
    });

    describe("page 43 - toString() conversions of non Strings", function(){
      var number = 11;
      it("toString() should return base10 string", function() {
        expect(number.toString()).toBe("11");
      });
      it("toString() with radix 2 should return base2", function() {
        expect(number.toString(2)).toBe("1011");
      });
      it("toString() with radix 11 should return base11", function() {
        expect(number.toString(11)).toBe("10");
      });
      it("toString() with radix 16 should return hexidecimal", function() {
        expect(number.toString(16)).toBe("b");
      });
    });

    describe("page 44 - String() conversions", function(){
    });
  });

  describe("Object data type", function() {

    describe("page 45 - default object properities", function() {
    });

    describe("page 45 - default object methods", function() {
    });
  });

  describe("Operators", function() {

    describe("Unary operators", function() {
      it("page 46: ++ should increment by 1", function() {
        var i = 0;
        i++;
        expect(i).toBe(1);
      });

      it("page 46: -- should decrement by 1", function() {
        var i = 0;
        i--;
        expect(i).toBe(-1);
      });
      xit("page 47: ++ increment before", function() {});
      xit("page 47: ++ increment after", function() {});
    });

    describe("Bitwise operators", function() {
    });

    describe("Boolean operators", function() {
    });

    describe("Multiplicative operators", function() {
    });

    describe("Additive operators", function() {
    });

    describe("Equality operators", function() {
    });

    describe("Conditional or ternary operators", function() {
      xit("should how it's the same as if ... else", function() {});
    });

    describe("If statement", function() {});

    describe("Do-While statement", function() {});

    describe("While statement", function() {});

    describe("For statement", function() {});

    describe("For-In statement", function() {});

    describe("Break in loop functions", function() {});

    describe("Conintue in loop functions", function() {});

    describe("Breaks AND Conintues in loop functions", function() {});

    describe("With statement", function() {});

    describe("Switch statement", function() {});

    describe("Functions", function(){
      describe("argments", function() {});
      describe("overloading", function() {});
    });

  });
})
<|endoftext|>


<commit_before>// unauthenticated requests are rate-limited to 6/min
var githubSearchURL = 'https://api.github.com/search/repositories';
var sort = 'sort=stars&order=desc';
var githubOrg = 'org:Esri';

function assembleSearchUrl(keywords, language) {
    var query = '?q=';

    if (keywords) {
        keywords = keywords.trim();
        if (keywords.length > 0) {
            query += keywords.replace(/ +/g, '+');
        }
    }
    if (language) {
        language = language.trim();
        if (language.length > 0) {
            query += 'language:' + language.replace(/ +/g, '+');
        }
    }
    query += '+NOT+deprecated ';
    query += githubOrg;
    return githubSearchURL + query + '&' + sort;
}

function showQueryResults(tags, response) {
    if (!response.message) {
        var items = response.items,
        i,
        dateUpdated,
        html,
        project;

        if (Array.isArray(items) && items.length > 0) {
            html = '';
            for (i = 0; i < items.length; i ++) {
                project = items[i];
                dateUpdated = new Date(project.updated_at).toLocaleDateString();
                html += '

' + project.name + '

' + (project.language || '') + '' + dateUpdated + '

' + (project.description || '') + '

 ' + project.forks_count + '  ' + project.stargazers_count + '
View it on github

';
            }
            $('#content').html('
' + html + '
');
        } else {
            $('#content').html('
No projects found with tags ' + tags + '
');
        }
    } else if (response.message && response.message.indexOf('API rate limit exceeded') > -1) {
        $('#content').html('
Hold your horses. GitHub only allows six searches per minute.
');
    }
}

function showQueryError(tags, error) {
    $('#content').html('
Error ' + error.message + ' searching for ' + tags + '
');
}

function redirectIfQuery() {
    var pageName = window.location.toString(),
        delimiterPosition,
        redirectURL = 'https://github.com/Esri?q=',
        redirected = false;

    delimiterPosition = pageName.indexOf('#');
    if (delimiterPosition > 0) {
        switch (pageName.substr(delimiterPosition + 1)) {
            case 'webdevelopment':
            case 'datamanagement':
            case 'spatialanalysis':
            case 'publishingsharing':
                break;
            default:
                pageName = redirectURL + pageName.substr(delimiterPosition + 1);
                redirected = true;
                window.location = pageName;
        }
    } else {
        delimiterPosition = pageName.indexOf('?q=');
        if (delimiterPosition > 0) {
            pageName = redirectURL + pageName.substr(delimiterPosition + 3);
            redirected = true;
            window.location = pageName;
        }
    }
    return redirected;
}

$(function() {
    var inlineLinks = document.getElementsByClassName("subdued-links");
    for (i = 0; i < inlineLinks.length; ++i) {
        inlineLinks[i].addEventListener("click", function(){
        window.location.hash = this.id;
        });
    }

    if (redirectIfQuery()) {
        return;
    }
    // $(".chzn-select").chosen();
    // $(".repo-language").click(function() {
    //     var e = this.innerHTML.replace(/ /g, "");
    //     return $("select").val(e).trigger("liszt:updated").change(),
    //         window.location.hash = e,
    //         !1
    // }),
    // $("select").change(function() {
    //     var selectedOptions,
    //         selectedTags = [];
    //     selectedOptions = $("select option:selected"),
    //         selectedOptions.each(function() {
    //             selectedTags.push($(this).data("filter"));
    //         });
    //     if (selectedTags.length > 0) {
    //         var tags = selectedTags.join(" ");
    //         var searchURL = assembleSearchUrl(tags, '');
    //         $('#content').html('
Looking for projects matching ' + tags + '
');
    //         fetch(searchURL).then(function (response) {
    //            response.json().then(function(json) {
    //                showQueryResults(tags, json)
    //            }, function (error) {
    //                showQueryError(tags, error);
    //            });
    //         },
    //         function (error) {
    //             showQueryError(tags, error);
    //         });
    //     } else {
    //         $('#content').html('');
    //     }
    //     window.location.hash = selectedTags.join(",");
    // }),
    // $(document).ready(function() {
    //     var e = window.location.hash;
    //     null !== e && "" != e && ($("select").val(e.replace(/#/, "").split(",")),
    //         $("select").trigger("liszt:updated").change())
    // }),
    // $(document).ready(function() {
    //     var n, i = !1, o = 3e3, a = 0, c = ["JavaScript", "ActionScript", "Objective-C", "Java", "Python", "DotNet", "iOS", "C-Sharp", "Android", "QuickStart", "Local-Government", "Bootstrap", "Mapping", "GeoJSON", "Mobile", "Code-Challenge", "Utility", "Storytelling", "Geocoding", "ArcGIS", "Hadoop", "Web", "Social", "Analysis", "Offline", "Runtime", "Dashboard", "Public", ""];
    //     function e(e) {
    //         window.clearInterval(n),
    //         e && (n = setInterval(t, o))
    //     }
    //     function t() {
    //         $("select").val(c[a]),
    //             $("select").trigger("liszt:updated").change(),
    //             a < c.length - 1 ? a++ : a = 0
    //     }
    //     $(document).keydown(function(t) {
    //         38 === t.which && t.shiftKey && i ? o > 1e3 && (o -= 1e3) : 40 === t.which && t.shiftKey && i && 5e3 > o && (o += 1e3),
    //         i && e(i)
    //     })
    // })
});
<commit_msg>i knew id forget something<commit_after>// unauthenticated requests are rate-limited to 6/min
var githubSearchURL = 'https://api.github.com/search/repositories';
var sort = 'sort=stars&order=desc';
var githubOrg = 'org:Esri';

function assembleSearchUrl(keywords, language) {
    var query = '?q=';

    if (keywords) {
        keywords = keywords.trim();
        if (keywords.length > 0) {
            query += keywords.replace(/ +/g, '+');
        }
    }
    if (language) {
        language = language.trim();
        if (language.length > 0) {
            query += 'language:' + language.replace(/ +/g, '+');
        }
    }
    query += '+NOT+deprecated ';
    query += githubOrg;
    return githubSearchURL + query + '&' + sort;
}

function showQueryResults(tags, response) {
    if (!response.message) {
        var items = response.items,
        i,
        dateUpdated,
        html,
        project;

        if (Array.isArray(items) && items.length > 0) {
            html = '';
            for (i = 0; i < items.length; i ++) {
                project = items[i];
                dateUpdated = new Date(project.updated_at).toLocaleDateString();
                html += '

' + project.name + '

' + (project.language || '') + '' + dateUpdated + '

' + (project.description || '') + '

 ' + project.forks_count + '  ' + project.stargazers_count + '
View it on github

';
            }
            $('#content').html('
' + html + '
');
        } else {
            $('#content').html('
No projects found with tags ' + tags + '
');
        }
    } else if (response.message && response.message.indexOf('API rate limit exceeded') > -1) {
        $('#content').html('
Hold your horses. GitHub only allows six searches per minute.
');
    }
}

function showQueryError(tags, error) {
    $('#content').html('
Error ' + error.message + ' searching for ' + tags + '
');
}

function redirectIfQuery() {
    var pageName = window.location.toString(),
        delimiterPosition,
        redirectURL = 'https://github.com/Esri?q=',
        redirected = false;

    delimiterPosition = pageName.indexOf('#');
    if (delimiterPosition > 0) {
        switch (pageName.substr(delimiterPosition + 1)) {
            case 'web-development':
            case 'data-management':
            case 'spatial-analysis':
            case 'publishing-sharing':
                break;
            default:
                pageName = redirectURL + pageName.substr(delimiterPosition + 1);
                redirected = true;
                window.location = pageName;
        }
    } else {
        delimiterPosition = pageName.indexOf('?q=');
        if (delimiterPosition > 0) {
            pageName = redirectURL + pageName.substr(delimiterPosition + 3);
            redirected = true;
            window.location = pageName;
        }
    }
    return redirected;
}

$(function() {
    var inlineLinks = document.getElementsByClassName("subdued-links");
    for (i = 0; i < inlineLinks.length; ++i) {
        inlineLinks[i].addEventListener("click", function(){
        window.location.hash = this.id;
        });
    }

    if (redirectIfQuery()) {
        return;
    }
    // $(".chzn-select").chosen();
    // $(".repo-language").click(function() {
    //     var e = this.innerHTML.replace(/ /g, "");
    //     return $("select").val(e).trigger("liszt:updated").change(),
    //         window.location.hash = e,
    //         !1
    // }),
    // $("select").change(function() {
    //     var selectedOptions,
    //         selectedTags = [];
    //     selectedOptions = $("select option:selected"),
    //         selectedOptions.each(function() {
    //             selectedTags.push($(this).data("filter"));
    //         });
    //     if (selectedTags.length > 0) {
    //         var tags = selectedTags.join(" ");
    //         var searchURL = assembleSearchUrl(tags, '');
    //         $('#content').html('
Looking for projects matching ' + tags + '
');
    //         fetch(searchURL).then(function (response) {
    //            response.json().then(function(json) {
    //                showQueryResults(tags, json)
    //            }, function (error) {
    //                showQueryError(tags, error);
    //            });
    //         },
    //         function (error) {
    //             showQueryError(tags, error);
    //         });
    //     } else {
    //         $('#content').html('');
    //     }
    //     window.location.hash = selectedTags.join(",");
    // }),
    // $(document).ready(function() {
    //     var e = window.location.hash;
    //     null !== e && "" != e && ($("select").val(e.replace(/#/, "").split(",")),
    //         $("select").trigger("liszt:updated").change())
    // }),
    // $(document).ready(function() {
    //     var n, i = !1, o = 3e3, a = 0, c = ["JavaScript", "ActionScript", "Objective-C", "Java", "Python", "DotNet", "iOS", "C-Sharp", "Android", "QuickStart", "Local-Government", "Bootstrap", "Mapping", "GeoJSON", "Mobile", "Code-Challenge", "Utility", "Storytelling", "Geocoding", "ArcGIS", "Hadoop", "Web", "Social", "Analysis", "Offline", "Runtime", "Dashboard", "Public", ""];
    //     function e(e) {
    //         window.clearInterval(n),
    //         e && (n = setInterval(t, o))
    //     }
    //     function t() {
    //         $("select").val(c[a]),
    //             $("select").trigger("liszt:updated").change(),
    //             a < c.length - 1 ? a++ : a = 0
    //     }
    //     $(document).keydown(function(t) {
    //         38 === t.which && t.shiftKey && i ? o > 1e3 && (o -= 1e3) : 40 === t.which && t.shiftKey && i && 5e3 > o && (o += 1e3),
    //         i && e(i)
    //     })
    // })
});
<|endoftext|>


<commit_before>// Schema for Projects collection
ProjectsSchema = new SimpleSchema({
  "name": {
    type: String,
    label: "Project Name"
  },
  "projectDescription": {
    type: String,
    label: "Project Description"
  },
  "projectSummary": {
    type: String,
    label: "Project Summary"
  },
  "contactPersonId": {
    type: String,
    label: "Contact Person ID",
    regEx: SimpleSchema.RegEx.Id,
    optional: true
  },
  "problemDescription": {
    type: String,
    label: "Problem Description"
  },
  "problemCategories": {
    type: [String],
    label: "Problem Categories"
  },
  "tags": {
    type: [String],
    label: "Tags"
  },
  "teamIds": {
    type: [String],
    label: "Team IDs",
    regEx: SimpleSchema.RegEx.Id,
    optional: true
  },
  "teamDescription": {
    type: String,
    label: "Team Description"
  },
  "currentStage": {
    type: String,
    label: "Current Stage"
  },
  "projectShort": {
    type: String,
    label: "Project Short"
  },
  "dateListed": {
    type: Date,
    label: "Date Listed",
    autoValue: function() {
      if (this.isInsert) {
        return new Date();
      } else if (this.isUpsert) {
        return {$setOnInsert: new Date()};
      } else {
        this.unset();  // Prevent user from supplying their own value
      }
    }
  },
  "startupDate": {
    type: Date,
    label: "Startup Date"
  },
  "targetGroups": {
    type: [String],
    label: "Target Groups"
  },
  "targetLocations": {
    type: [String],
    label: "Target Locations"
  },
  "targetPlatforms": {
    type: [String],
    label: "Target Platforms"
  },
  "address": {
    type: String,
    label: "Address"
  },
  "links": {
    type: [String],
    label: "Links"
  },
  "requestIds": {
    type: [String],
    label: "Request IDs",
    regEx: SimpleSchema.RegEx.Id,
    optional: true
  }
});

Projects.attachSchema(ProjectsSchema);
<commit_msg>Add a 140 char limit on the project short field<commit_after>// Schema for Projects collection
ProjectsSchema = new SimpleSchema({
  "name": {
    type: String,
    label: "Project Name"
  },
  "projectDescription": {
    type: String,
    label: "Project Description"
  },
  "projectSummary": {
    type: String,
    label: "Project Summary"
  },
  "contactPersonId": {
    type: String,
    label: "Contact Person ID",
    regEx: SimpleSchema.RegEx.Id,
    optional: true
  },
  "problemDescription": {
    type: String,
    label: "Problem Description"
  },
  "problemCategories": {
    type: [String],
    label: "Problem Categories"
  },
  "tags": {
    type: [String],
    label: "Tags"
  },
  "teamIds": {
    type: [String],
    label: "Team IDs",
    regEx: SimpleSchema.RegEx.Id,
    optional: true
  },
  "teamDescription": {
    type: String,
    label: "Team Description"
  },
  "currentStage": {
    type: String,
    label: "Current Stage"
  },
  "projectShort": {
    type: String,
    label: "Project Short",
    max: 140
  },
  "dateListed": {
    type: Date,
    label: "Date Listed",
    autoValue: function() {
      if (this.isInsert) {
        return new Date();
      } else if (this.isUpsert) {
        return {$setOnInsert: new Date()};
      } else {
        this.unset();  // Prevent user from supplying their own value
      }
    }
  },
  "startupDate": {
    type: Date,
    label: "Startup Date"
  },
  "targetGroups": {
    type: [String],
    label: "Target Groups"
  },
  "targetLocations": {
    type: [String],
    label: "Target Locations"
  },
  "targetPlatforms": {
    type: [String],
    label: "Target Platforms"
  },
  "address": {
    type: String,
    label: "Address"
  },
  "links": {
    type: [String],
    label: "Links"
  },
  "requestIds": {
    type: [String],
    label: "Request IDs",
    regEx: SimpleSchema.RegEx.Id,
    optional: true
  }
});

Projects.attachSchema(ProjectsSchema);
<|endoftext|>


<commit_before>var vows = require('vows');
var assert = require('assert');
var suite = vows.describe('jStat.distribution');

require('../env.js');

suite.addBatch({
  'beta pdf': {
    'topic': function() {
      return jStat;
    },
    'check pdf calculation': function(jStat) {
      // Non-log form of the Beta pdf
      function pdf(x, alpha, beta) {
        if (x > 1 || x < 0)
          return 0;
        return (Math.pow(x, alpha - 1) * Math.pow(1 - x, beta - 1)) /
            jStat.betafn(alpha, beta);
      }

      var tol = 0.0000001;
      var args = [0, 0.1, 0.3, 0.5, 0.7, 0.9, 1];
      var arg;

      for (var i = 0; i < args.length; i++) {
        arg = args[i];
        assert.epsilon(tol, jStat.beta.pdf(arg, 0.1, 0.1), pdf(arg, 0.1, 0.1));
        assert.epsilon(tol, jStat.beta.pdf(arg, 1, 1), pdf(arg, 1, 1));
        assert.epsilon(tol, jStat.beta.pdf(arg, 10, 50), pdf(arg, 10, 50));

        // Show that the log form of the pdf performs better for
        // large parameter values.
        assert(!isNaN(jStat.beta.pdf(arg, 1000, 5000)),
               'New Beta pdf is NaN for large parameter values.');
        assert(isNaN(pdf(arg, 1000, 5000)),
               'Old Beta pdf is not NaN for large parameter values.');
      }

      assert.equal(jStat.beta.pdf(0, 1, 4), 4);
      assert.equal(jStat.beta.pdf(1, 4, 1), 4);
    }
  },

  'gamma pdf': {
    'topic': function() {
      return jStat;
    },
    'check instance and static pdf method': function (jStat) {
      var shape = 5;
      var scale = 1;
      var gamma = jStat.gamma(shape, scale);
      var xValues = [-1, 0, 1];
      var x;
      for (var i = 0; i < xValues.length; i++) {
        x = xValues[i];
        pStatic = jStat.gamma.pdf(x, shape, scale);
        pInstance = gamma.pdf(x);
        if (isNaN(pStatic)) {
          assert(isNaN(pInstance));
        } else {
          assert(pStatic === pInstance,
                 'Gamma pdf evaluated at ' +
                   x + ' should be equal for instance and static methods.');
        }
      }
    }
  },

  'binomial pdf': {
    'topic': function() {
      return jStat;
    },
    //checked against R's dbinom(k, n, p)
    'check pdf calculation': function(jStat) {
      var tol = 0.0000001;
      assert.epsilon(tol, jStat.binomial.pdf(10, 25, 0.5), 0.09741664);
      assert.epsilon(tol, jStat.binomial.pdf(50, 1000, 0.05), 0.05778798);
    },
    //Checked against r's pbinom(k, n, p)
    'check cdf calculation': function(jStat) {
      var tol = 0.0000001;
      assert.epsilon(tol, jStat.binomial.cdf(10, 25, 0.5), 0.2121781);
      assert.epsilon(tol, jStat.binomial.cdf(50, 1000, 0.05), 0.537529);
    }
  },

  'binomial pdf': {
    'topic': function() {
      return jStat;
    },
    //checked against R's dbinom(k, n, p)
    'check pdf calculation': function(jStat) {
      var tol = 0.0000001;
      assert.epsilon(tol, jStat.binomial.pdf(10, 25, 0.5), 0.09741664);
      assert.epsilon(tol, jStat.binomial.pdf(50, 1000, 0.05), 0.05778798);
    },
    //Checked against r's pbinom(k, n, p)
    'check cdf calculation': function(jStat) {
      var tol = 0.0000001;
      assert.epsilon(tol, jStat.binomial.cdf(10, 25, 0.5), 0.2121781);
      assert.epsilon(tol, jStat.binomial.cdf(50, 1000, 0.05), 0.537529);
    }
  },

  'studentt pdf': {
    'topic': function() {
      return jStat;
    },
    //Checked against R dt(x,df)
    'check pdf calculation': function(jStat) {
      var tol = 0.0000001;
      var x = 0.5;
      var df = 40;
      assert.epsilon(tol, jStat.studentt.pdf(x, df), 0.3489195);
    },
    //Checked against R's pt(x, df)
    'check cdf calculation': function(jStat) {
      var tol = 0.0000001;
      var x = 0.5;
      var df = 40;
      assert.epsilon(tol, jStat.studentt.cdf(x, df), 0.6900926);
    },
    //Checked against R's qt(x, df)
    'check inv calculation': function(jStat) {
      var tol = 0.0000001;
      assert.epsilon(tol, jStat.studentt.inv(0.5, 40), 0);
      assert.epsilon(tol, jStat.studentt.inv(0.7, 20), 0.5328628);
      assert.epsilon(tol, jStat.studentt.inv(0.2, 10), -0.8790578);
    }
  },

suite.addBatch({
  'negbin pdf': {
    'topic': function() {
      return jStat;
    },
    'check pdf calculation': function(jStat) {
      var k = 10; // number of failures
      var r = 5; // threshold number of successes
      var p = 0.25; // probability of a success
      assert(jStat.negbin.pdf(k, r, p), 0.05504866037517786);
    }
  },

  'negbin cdf': {
    'topic': function() {
      return jStat;
    },
    'check pdf calculation': function(jStat) {
      var k = 10; // number of failures
      var r = 5; // threshold number of successes
      var p = 0.25; // probability of a success
      assert(jStat.negbin.cdf(k, r, p), 0.3135140584781766);
    }
  },
});

suite.addBatch({
  'hypergeometric pdf': {
    'topic': function() {
      return jStat;
    },
    'check pdf calculation': function(jStat) {
      var tol = 0.0000001;
      // How many 1s were obtained by sampling?
      var successes = [10, 16];
      // How big was the source population?
      var population = [100, 3589];
      // How many 1s were in it?
      var available = [20, 16];
      // How big a sample was taken?
      var draws = [15, 2290];
      // What was the probability of exactly this many 1s?
      // Obtained from the calculator at
      // <http://www.geneprof.org/GeneProf/tools/hypergeometric.jsp>
      var answers = [0.000017532028090435493, 0.0007404996809672229];

      for (var i = 0; i < answers.length; i++) {
        // See if we get the right answer for each calculation.
        var calculated = jStat.hypgeom.pdf(successes[i],
                                           population[i],
                                           available[i],
                                           draws[i]);
        // None of the answers should be NaN
        assert(!isNaN(calculated));
        // And they should all match
        assert.epsilon(tol, calculated, answers[i]);
      }
    }
  },

suite.addBatch({
  'hypergeometric pdf': {
    'topic': function() {
      return jStat;
    },
    'check pdf agreement': function(jStat) {
      var tol = 0.0000001;

      // Define an easy pdf
      var easyPDF = function pdf(k, N, m, n) {
        if (k !== (k | 0))
          return false;
        if (k < 0)
          return 0;
        return jStat.combination(m, k) * jStat.combination(N - m , n - k) /
            jStat.combination(N, n);
      }

      // Now test the easy PDF agaisnt the real one.
      for (var N = 0; N < 10; N++) {
        // For several population sizes
        for (var m = 0; m < N; m++) {
          // For all possible success counts
          for (var n = 0; n < N; n++) {
            // For all possible sample sizes
            for (var x = 0; x < n; x++) {
              // For all subset sizes of the sampled set

              // Get the probability be each method
              var probEasy = easyPDF(x, N, m, n);

              if (!isNaN(probEasy)) {
                // The easy PDF worked for this situation. Compare it to the
                // real one.

                var probReal = jStat.hypgeom.pdf(x, N, m, n);

                assert(!isNaN(probReal), 'Hypergeometric PDF returned NaN');
                assert.epsilon(tol,
                    probReal,
                    probEasy,
                    'Hypergeometric PDF didn\'t match naive implementation');
              }
            }
          }
        }
      }

    },

    'check cdf agreement': function(jStat) {
      var tol = 0.0000001;

      // Define an easy pdf
      function easyPDF(k, N, m, n) {
        if (k !== (k | 0))
          return false;
        if (k < 0)
          return 0;
        return jStat.combination(m, k) * jStat.combination(N - m , n - k) /
            jStat.combination(N, n);
      }

      // Define a summation cdf based on it
      function easyCDF(x, N, m, n) {
        // Sum up the total probability of every number of successes <= x, and
        // put it here.
        var sum = 0;
        for (var i = 0; i <= x; i++)
          sum += easyPDF(i, N, m, n);
        }
        return sum;
      }

      // Now test the easy CDF agaisnt the real one.
      for (var N = 0; N < 10; N++) {
        // For several population sizes
        for (var m = 0; m < N; m++) {
          // For all possible success counts
          for (var n = 0; n < N; n++) {
            // For all possible sample sizes
            for (var x = 0; x < n; x++) {
              // For all subset sizes of the sampled set

              // Get the probability be each method
              var probEasy = easyCDF(x, N, m, n);

              if (!isNaN(probEasy)) {
                // The easy CDF worked for this situation. Compare it to the
                // real one.

                var probReal = jStat.hypgeom.cdf(x, N, m, n);
                assert(!isNaN(probReal), 'Hypergeometric CDF returned NaN');
                assert.epsilon(tol,
                    probReal,
                    probEasy,
                    'Hypergeometric CDF didn\'t match naive implementation');
              }
            }
          }
        }
      }
    },

    'check pdf calculation': function(jStat) {
      var tol = 0.0000001;
      // How many 1s were obtained by sampling?
      var successes = [ 10, 16, 252 ];
      // How big was the source population?
      var population = [ 100, 3589, 3589 ];
      // How many 1s were in it?
      var available = [ 20, 16, 252 ];
      // How big a sample was taken?
      var draws = [ 15, 2290, 252 ];
      // What was the probability of exactly this many 1s?
      // Obtained from the calculator at
      // <http://www.geneprof.org/GeneProf/tools/hypergeometric.jsp>
      var answers = [ 0.000017532028090435493, 0.0007404996809672229, 0 ];

      for (var i = 0; i < answers.length; i++) {
        // See if we get the right answer for each calculation.

        var calculated = jStat.hypgeom.pdf(successes[i], population[i],
                                           available[i], draws[i]);

        // None of the answers should be NaN
        assert(!isNaN(calculated), "Hypergeometric PDF returned NaN");

        // And they should all match
        assert.epsilon(tol, calculated, answers[i],
          "Hypergeometric PDF returned incorrect answer");
      }
    },
    'check cdf calculation': function(jStat) {
      var tol = 0.0000001;
      // How many 1s were obtained by sampling?
      var successes = [ 10, 16, 10 ];
      // How big was the source population?
      var population = [ 100, 3589, 3589 ];
      // How many 1s were in it?
      var available = [ 20, 16, 16 ];
      // How big a sample was taken?
      var draws = [ 15, 2290, 2290 ];
      // What was the probability of this many 1s or fewer?
      // Obtained from the calculator at
      // <http://www.geneprof.org/GeneProf/tools/hypergeometric.jsp>
      var answers = [ 0.9999989096, 1, 0.55047323949 ];

      for (var i = 0; i < answers.length; i++) {
        // See if we get the right answer for each calculation.

        var calculated = jStat.hypgeom.cdf(successes[i], population[i],
                                           available[i], draws[i]);

        // None of the answers should be NaN
        assert(!isNaN(calculated), "Hypergeometric CDF returned NaN");

        // And they should all match
        assert.epsilon(tol, calculated, answers[i],
          "Hypergeometric CDF returned incorrect answer");
      }
    }
  }
});

suite.export(module);
<commit_msg>fixes because tests don't pass?<commit_after>var vows = require('vows');
var assert = require('assert');
var suite = vows.describe('jStat.distribution');

require('../env.js');

suite.addBatch({
  'beta pdf': {
    'topic': function() {
      return jStat;
    },
    'check pdf calculation': function(jStat) {
      // Non-log form of the Beta pdf
      function pdf(x, alpha, beta) {
        if (x > 1 || x < 0)
          return 0;
        return (Math.pow(x, alpha - 1) * Math.pow(1 - x, beta - 1)) /
            jStat.betafn(alpha, beta);
      }

      var tol = 0.0000001;
      var args = [0, 0.1, 0.3, 0.5, 0.7, 0.9, 1];
      var arg;

      for (var i = 0; i < args.length; i++) {
        arg = args[i];
        assert.epsilon(tol, jStat.beta.pdf(arg, 0.1, 0.1), pdf(arg, 0.1, 0.1));
        assert.epsilon(tol, jStat.beta.pdf(arg, 1, 1), pdf(arg, 1, 1));
        assert.epsilon(tol, jStat.beta.pdf(arg, 10, 50), pdf(arg, 10, 50));

        // Show that the log form of the pdf performs better for
        // large parameter values.
        assert(!isNaN(jStat.beta.pdf(arg, 1000, 5000)),
               'New Beta pdf is NaN for large parameter values.');
        assert(isNaN(pdf(arg, 1000, 5000)),
               'Old Beta pdf is not NaN for large parameter values.');
      }

      assert.equal(jStat.beta.pdf(0, 1, 4), 4);
      assert.equal(jStat.beta.pdf(1, 4, 1), 4);
    }
  },

  'gamma pdf': {
    'topic': function() {
      return jStat;
    },
    'check instance and static pdf method': function (jStat) {
      var shape = 5;
      var scale = 1;
      var gamma = jStat.gamma(shape, scale);
      var xValues = [-1, 0, 1];
      var x;
      for (var i = 0; i < xValues.length; i++) {
        x = xValues[i];
        pStatic = jStat.gamma.pdf(x, shape, scale);
        pInstance = gamma.pdf(x);
        if (isNaN(pStatic)) {
          assert(isNaN(pInstance));
        } else {
          assert(pStatic === pInstance,
                 'Gamma pdf evaluated at ' +
                   x + ' should be equal for instance and static methods.');
        }
      }
    }
  },

  'binomial pdf': {
    'topic': function() {
      return jStat;
    },
    //checked against R's dbinom(k, n, p)
    'check pdf calculation': function(jStat) {
      var tol = 0.0000001;
      assert.epsilon(tol, jStat.binomial.pdf(10, 25, 0.5), 0.09741664);
      assert.epsilon(tol, jStat.binomial.pdf(50, 1000, 0.05), 0.05778798);
    },
    //Checked against r's pbinom(k, n, p)
    'check cdf calculation': function(jStat) {
      var tol = 0.0000001;
      assert.epsilon(tol, jStat.binomial.cdf(10, 25, 0.5), 0.2121781);
      assert.epsilon(tol, jStat.binomial.cdf(50, 1000, 0.05), 0.537529);
    }
  },

  'binomial pdf': {
    'topic': function() {
      return jStat;
    },
    //checked against R's dbinom(k, n, p)
    'check pdf calculation': function(jStat) {
      var tol = 0.0000001;
      assert.epsilon(tol, jStat.binomial.pdf(10, 25, 0.5), 0.09741664);
      assert.epsilon(tol, jStat.binomial.pdf(50, 1000, 0.05), 0.05778798);
    },
    //Checked against r's pbinom(k, n, p)
    'check cdf calculation': function(jStat) {
      var tol = 0.0000001;
      assert.epsilon(tol, jStat.binomial.cdf(10, 25, 0.5), 0.2121781);
      assert.epsilon(tol, jStat.binomial.cdf(50, 1000, 0.05), 0.537529);
    }
  },

  'studentt pdf': {
    'topic': function() {
      return jStat;
    },
    //Checked against R dt(x,df)
    'check pdf calculation': function(jStat) {
      var tol = 0.0000001;
      var x = 0.5;
      var df = 40;
      assert.epsilon(tol, jStat.studentt.pdf(x, df), 0.3489195);
    },
    //Checked against R's pt(x, df)
    'check cdf calculation': function(jStat) {
      var tol = 0.0000001;
      var x = 0.5;
      var df = 40;
      assert.epsilon(tol, jStat.studentt.cdf(x, df), 0.6900926);
    },
    //Checked against R's qt(x, df)
    'check inv calculation': function(jStat) {
      var tol = 0.0000001;
      assert.epsilon(tol, jStat.studentt.inv(0.5, 40), 0);
      assert.epsilon(tol, jStat.studentt.inv(0.7, 20), 0.5328628);
      assert.epsilon(tol, jStat.studentt.inv(0.2, 10), -0.8790578);
    }
  },
});

suite.addBatch({
  'negbin pdf': {
    'topic': function() {
      return jStat;
    },
    'check pdf calculation': function(jStat) {
      var k = 10; // number of failures
      var r = 5; // threshold number of successes
      var p = 0.25; // probability of a success
      assert(jStat.negbin.pdf(k, r, p), 0.05504866037517786);
    }
  },

  'negbin cdf': {
    'topic': function() {
      return jStat;
    },
    'check pdf calculation': function(jStat) {
      var k = 10; // number of failures
      var r = 5; // threshold number of successes
      var p = 0.25; // probability of a success
      assert(jStat.negbin.cdf(k, r, p), 0.3135140584781766);
    }
  },
});

suite.addBatch({
  'hypergeometric pdf': {
    'topic': function() {
      return jStat;
    },
    'check pdf calculation': function(jStat) {
      var tol = 0.0000001;
      // How many 1s were obtained by sampling?
      var successes = [10, 16];
      // How big was the source population?
      var population = [100, 3589];
      // How many 1s were in it?
      var available = [20, 16];
      // How big a sample was taken?
      var draws = [15, 2290];
      // What was the probability of exactly this many 1s?
      // Obtained from the calculator at
      // <http://www.geneprof.org/GeneProf/tools/hypergeometric.jsp>
      var answers = [0.000017532028090435493, 0.0007404996809672229];

      for (var i = 0; i < answers.length; i++) {
        // See if we get the right answer for each calculation.
        var calculated = jStat.hypgeom.pdf(successes[i],
                                           population[i],
                                           available[i],
                                           draws[i]);
        // None of the answers should be NaN
        assert(!isNaN(calculated));
        // And they should all match
        assert.epsilon(tol, calculated, answers[i]);
      }
    }
  }
});

suite.addBatch({
  'hypergeometric pdf': {
    'topic': function() {
      return jStat;
    },
    'check pdf agreement': function(jStat) {
      var tol = 0.0000001;

      // Define an easy pdf
      var easyPDF = function pdf(k, N, m, n) {
        if (k !== (k | 0))
          return false;
        if (k < 0)
          return 0;
        return jStat.combination(m, k) * jStat.combination(N - m , n - k) /
            jStat.combination(N, n);
      }

      // Now test the easy PDF agaisnt the real one.
      for (var N = 0; N < 10; N++) {
        // For several population sizes
        for (var m = 0; m < N; m++) {
          // For all possible success counts
          for (var n = 0; n < N; n++) {
            // For all possible sample sizes
            for (var x = 0; x < n; x++) {
              // For all subset sizes of the sampled set

              // Get the probability be each method
              var probEasy = easyPDF(x, N, m, n);

              if (!isNaN(probEasy)) {
                // The easy PDF worked for this situation. Compare it to the
                // real one.

                var probReal = jStat.hypgeom.pdf(x, N, m, n);

                assert(!isNaN(probReal), 'Hypergeometric PDF returned NaN');
                assert.epsilon(tol,
                    probReal,
                    probEasy,
                    'Hypergeometric PDF didn\'t match naive implementation');
              }
            }
          }
        }
      }

    },

    'check cdf agreement': function(jStat) {
      var tol = 0.0000001;

      // Define an easy pdf
      function easyPDF(k, N, m, n) {
        if (k !== (k | 0))
          return false;
        if (k < 0)
          return 0;
        return jStat.combination(m, k) * jStat.combination(N - m , n - k) /
            jStat.combination(N, n);
      }

      // Define a summation cdf based on it
      function easyCDF(x, N, m, n) {
        // Sum up the total probability of every number of successes <= x, and
        // put it here.
        var sum = 0;
        for (var i = 0; i <= x; i++)
          sum += easyPDF(i, N, m, n);
        return sum;
      }

      // Now test the easy CDF agaisnt the real one.
      for (var N = 0; N < 10; N++) {
        // For several population sizes
        for (var m = 0; m < N; m++) {
          // For all possible success counts
          for (var n = 0; n < N; n++) {
            // For all possible sample sizes
            for (var x = 0; x < n; x++) {
              // For all subset sizes of the sampled set

              // Get the probability be each method
              var probEasy = easyCDF(x, N, m, n);

              if (!isNaN(probEasy)) {
                // The easy CDF worked for this situation. Compare it to the
                // real one.

                var probReal = jStat.hypgeom.cdf(x, N, m, n);
                assert(!isNaN(probReal), 'Hypergeometric CDF returned NaN');
                assert.epsilon(tol,
                    probReal,
                    probEasy,
                    'Hypergeometric CDF didn\'t match naive implementation');
              }
            }
          }
        }
      }
    },

    'check pdf calculation': function(jStat) {
      var tol = 0.0000001;
      // How many 1s were obtained by sampling?
      var successes = [ 10, 16, 252 ];
      // How big was the source population?
      var population = [ 100, 3589, 3589 ];
      // How many 1s were in it?
      var available = [ 20, 16, 252 ];
      // How big a sample was taken?
      var draws = [ 15, 2290, 252 ];
      // What was the probability of exactly this many 1s?
      // Obtained from the calculator at
      // <http://www.geneprof.org/GeneProf/tools/hypergeometric.jsp>
      var answers = [ 0.000017532028090435493, 0.0007404996809672229, 0 ];

      for (var i = 0; i < answers.length; i++) {
        // See if we get the right answer for each calculation.

        var calculated = jStat.hypgeom.pdf(successes[i], population[i],
                                           available[i], draws[i]);

        // None of the answers should be NaN
        assert(!isNaN(calculated), "Hypergeometric PDF returned NaN");

        // And they should all match
        assert.epsilon(tol, calculated, answers[i],
          "Hypergeometric PDF returned incorrect answer");
      }
    },
    'check cdf calculation': function(jStat) {
      var tol = 0.0000001;
      // How many 1s were obtained by sampling?
      var successes = [ 10, 16, 10 ];
      // How big was the source population?
      var population = [ 100, 3589, 3589 ];
      // How many 1s were in it?
      var available = [ 20, 16, 16 ];
      // How big a sample was taken?
      var draws = [ 15, 2290, 2290 ];
      // What was the probability of this many 1s or fewer?
      // Obtained from the calculator at
      // <http://www.geneprof.org/GeneProf/tools/hypergeometric.jsp>
      var answers = [ 0.9999989096, 1, 0.55047323949 ];

      for (var i = 0; i < answers.length; i++) {
        // See if we get the right answer for each calculation.

        var calculated = jStat.hypgeom.cdf(successes[i], population[i],
                                           available[i], draws[i]);

        // None of the answers should be NaN
        assert(!isNaN(calculated), "Hypergeometric CDF returned NaN");

        // And they should all match
        assert.epsilon(tol, calculated, answers[i],
          "Hypergeometric CDF returned incorrect answer");
      }
    }
  }
});

suite.export(module);
<|endoftext|>


<commit_before>
//////////////////////////////////////////////////////////////////////////////////////////

// all modules, prefix with "-" to signify library; <name>_init etc. won't be called
var MODULES=[
  "-util",
  "-animation",

  "speech",

  "get",

  "tutorial",

  "base",

  "game",

  "input",

  "airline",

  "aircraft",
  "airport",

  "canvas",

  "ui",

  "load"
];

// saved as prop.version and prop.version_string
var VERSION=[2, 1, 7];

// are you using a main loop? (you must call update() afterward disable/reenable)
var UPDATE=true;

// the framerate is updated this often (seconds)
var FRAME_DELAY=1;

// is this a release build?
var RELEASE=false;

// Usage of async() etc:

// async("image") // call async() once for every async_loaded() you'll call
// $.get(...,function() {async_loaded("image");}) // call async_loaded once for each
//                                                // image once it's loaded
// if async_loaded() is NOT called the same number of times as async() the page will
// NEVER load!

// === CALLBACKS (all optional and do not need to be defined) ===

// INIT:
// module_init_pre()
// module_init()
// module_init_post()

// module_done()
// -- wait until all async has finished (could take a long time) --
// module_ready()
// -- wait until first frame is ready (only triggered if UPDATE == true) --
// module_complete()

// UPDATE:
// module_update_pre()
// module_update()
// module_update_post()

// RESIZE (called at least once during init and whenever page changes size)
// module_resize()

//////////////////////////////////////////////////////////////////////////////////////////

var async_modules={};
var async_done_callback=null;

var LOG_DEBUG=0;
var LOG_INFO=1;
var LOG_WARNING=2;
var LOG_ERROR=3;
var LOG_FATAL=4;

var log_strings={
  0:"DEBUG",
  1:"INFO ",
  2:"WARN ",
  3:"ERROR",
  4:"FATAL",
};

// PROP

function prop_init() {
  prop={};
  prop.complete=false;
  prop.temp="nothing here";
  prop.version=VERSION;
  prop.version_string="v"+VERSION.join(".");
  prop.time={};
  prop.time.start=time();
  prop.time.frames=0;
  prop.time.frame={};
  prop.time.frame.start=time();
  prop.time.frame.delay=FRAME_DELAY;
  prop.time.frame.count=0;
  prop.time.frame.last=time();
  prop.time.frame.delta=0;
  prop.time.fps=0;
  prop.log=LOG_DEBUG;
  prop.loaded=false;
  if(RELEASE)
    prop.log=LOG_WARNING;
}

// MISC

function log(message,level) {
  if(level == undefined)
    level=LOG_INFO;
  if(prop.log <= level) {
    var text="[ "+log_strings[level]+" ]";
    if(level >= LOG_WARNING)
      console.warn(text,message);
    else
      console.log(text,message);
  }
}

// ASYNC (AJAX etc.)

function async(name) {
  if(name in async_modules)
    async_modules[name]+=1;
  else
    async_modules[name]=1;
}

function async_loaded(name) {
  async_modules[name]-=1;
  async_check();
}

function async_wait(callback) {
  async_done_callback=callback;
  async_check();
}

function async_check() {
  for(var i in async_modules) {
    if(async_modules[i] != 0)
      return;
  }
  if(async_done_callback)
    async_done_callback();
}

// UTIL

function time() {
  return new Date().getTime()*0.001;
}

function s(number,single,multiple) {
  if(!single) single="";
  if(!multiple) multiple="s";
  if(single == 1) return single;
  return multiple;
}

// MODULES

function load_module(name) {
  var filename;
  if(name[0] == "-") {
    modules[name].library=true;
    filename="assets/scripts/"+name.substr(1)+".js";
  } else {
    filename="assets/scripts/"+name+".js";
  }
  var el=document.createElement("script");
  el.src=filename;
  document.head.appendChild(el);
  el.onload=function() {
    modules[name].script=true;
    //    if(modules[name].library)
    //      log("Loaded library "+name.substr(1));
    //    else
    //      log("Loaded module "+name);
    for(var i in modules) {
      var m=modules[i];
      if(!m.script)
        return;
    }
    call_module("*","init_pre");
    call_module("*","init");
    call_module("*","init_post");
    done();
  };
}

function load_modules() {
  // inserts each module's <script> into <head>
  for(var i in modules) {
    load_module(i);
  }
}

function call_module(name,func,args) {
  if(!args) args=[];
  if(name == "*") {
    for(var i=0;i<MODULES.length;i++)
      call_module(MODULES[i],func,args);
    return null;
  }
  if(name+"_"+func in window && name[0] != "-") {
    return window[name+"_"+func].apply(window,args);
  }
  return null;
}

$(document).ready(function() {
  modules={};
  for(var i=0;i<MODULES.length;i++) {
    modules[MODULES[i]]={
      library:false,
      script:false,
    };
  }
  prop_init();
  log("Version "+prop.version_string);
  load_modules();
});

function done() {
  var e=time()-prop.time.start;
  e=e.toPrecision(2);
  log("Finished loading "+MODULES.length+" module"+s(MODULES.length)+" in "+e+"s");
  $(window).resize(resize);
  resize();
  call_module("*","done");
  async_wait(function() {
    prop.loaded=true;
    call_module("*","ready");
    if(UPDATE)
      requestAnimationFrame(update);
  });
}

function resize() {
  call_module("*","resize");
}

function update() {
  if(!prop.complete) {
    call_module("*","complete");
    prop.complete=true;
  }
//  call_module("*","update_pre");
//  call_module("*","update");
//  call_module("*","update_post");

  game_update_pre();
  aircraft_update();

  canvas_update_post();

  if(UPDATE)
    requestAnimationFrame(update);
  prop.time.frames+=1;
  prop.time.frame.count+=1;
  var elapsed=time()-prop.time.frame.start;
  if(elapsed > prop.time.frame.delay) {
    prop.time.fps=prop.time.frame.count/elapsed;
    prop.time.frame.count=0;
    prop.time.frame.start=time();
  }
  prop.time.frame.delta=Math.min(time()-prop.time.frame.last,1/20);
  prop.time.frame.last=time();
}

function delta() {
  return prop.time.frame.delta;
}
<commit_msg>bump version<commit_after>
//////////////////////////////////////////////////////////////////////////////////////////

// all modules, prefix with "-" to signify library; <name>_init etc. won't be called
var MODULES=[
  "-util",
  "-animation",

  "speech",

  "get",

  "tutorial",

  "base",

  "game",

  "input",

  "airline",

  "aircraft",
  "airport",

  "canvas",

  "ui",

  "load"
];

// saved as prop.version and prop.version_string
var VERSION=[2, 1, 8];

// are you using a main loop? (you must call update() afterward disable/reenable)
var UPDATE=true;

// the framerate is updated this often (seconds)
var FRAME_DELAY=1;

// is this a release build?
var RELEASE=false;

// Usage of async() etc:

// async("image") // call async() once for every async_loaded() you'll call
// $.get(...,function() {async_loaded("image");}) // call async_loaded once for each
//                                                // image once it's loaded
// if async_loaded() is NOT called the same number of times as async() the page will
// NEVER load!

// === CALLBACKS (all optional and do not need to be defined) ===

// INIT:
// module_init_pre()
// module_init()
// module_init_post()

// module_done()
// -- wait until all async has finished (could take a long time) --
// module_ready()
// -- wait until first frame is ready (only triggered if UPDATE == true) --
// module_complete()

// UPDATE:
// module_update_pre()
// module_update()
// module_update_post()

// RESIZE (called at least once during init and whenever page changes size)
// module_resize()

//////////////////////////////////////////////////////////////////////////////////////////

var async_modules={};
var async_done_callback=null;

var LOG_DEBUG=0;
var LOG_INFO=1;
var LOG_WARNING=2;
var LOG_ERROR=3;
var LOG_FATAL=4;

var log_strings={
  0:"DEBUG",
  1:"INFO ",
  2:"WARN ",
  3:"ERROR",
  4:"FATAL",
};

// PROP

function prop_init() {
  prop={};
  prop.complete=false;
  prop.temp="nothing here";
  prop.version=VERSION;
  prop.version_string="v"+VERSION.join(".");
  prop.time={};
  prop.time.start=time();
  prop.time.frames=0;
  prop.time.frame={};
  prop.time.frame.start=time();
  prop.time.frame.delay=FRAME_DELAY;
  prop.time.frame.count=0;
  prop.time.frame.last=time();
  prop.time.frame.delta=0;
  prop.time.fps=0;
  prop.log=LOG_DEBUG;
  prop.loaded=false;
  if(RELEASE)
    prop.log=LOG_WARNING;
}

// MISC

function log(message,level) {
  if(level == undefined)
    level=LOG_INFO;
  if(prop.log <= level) {
    var text="[ "+log_strings[level]+" ]";
    if(level >= LOG_WARNING)
      console.warn(text,message);
    else
      console.log(text,message);
  }
}

// ASYNC (AJAX etc.)

function async(name) {
  if(name in async_modules)
    async_modules[name]+=1;
  else
    async_modules[name]=1;
}

function async_loaded(name) {
  async_modules[name]-=1;
  async_check();
}

function async_wait(callback) {
  async_done_callback=callback;
  async_check();
}

function async_check() {
  for(var i in async_modules) {
    if(async_modules[i] != 0)
      return;
  }
  if(async_done_callback)
    async_done_callback();
}

// UTIL

function time() {
  return new Date().getTime()*0.001;
}

function s(number,single,multiple) {
  if(!single) single="";
  if(!multiple) multiple="s";
  if(single == 1) return single;
  return multiple;
}

// MODULES

function load_module(name) {
  var filename;
  if(name[0] == "-") {
    modules[name].library=true;
    filename="assets/scripts/"+name.substr(1)+".js";
  } else {
    filename="assets/scripts/"+name+".js";
  }
  var el=document.createElement("script");
  el.src=filename;
  document.head.appendChild(el);
  el.onload=function() {
    modules[name].script=true;
    //    if(modules[name].library)
    //      log("Loaded library "+name.substr(1));
    //    else
    //      log("Loaded module "+name);
    for(var i in modules) {
      var m=modules[i];
      if(!m.script)
        return;
    }
    call_module("*","init_pre");
    call_module("*","init");
    call_module("*","init_post");
    done();
  };
}

function load_modules() {
  // inserts each module's <script> into <head>
  for(var i in modules) {
    load_module(i);
  }
}

function call_module(name,func,args) {
  if(!args) args=[];
  if(name == "*") {
    for(var i=0;i<MODULES.length;i++)
      call_module(MODULES[i],func,args);
    return null;
  }
  if(name+"_"+func in window && name[0] != "-") {
    return window[name+"_"+func].apply(window,args);
  }
  return null;
}

$(document).ready(function() {
  modules={};
  for(var i=0;i<MODULES.length;i++) {
    modules[MODULES[i]]={
      library:false,
      script:false,
    };
  }
  prop_init();
  log("Version "+prop.version_string);
  load_modules();
});

function done() {
  var e=time()-prop.time.start;
  e=e.toPrecision(2);
  log("Finished loading "+MODULES.length+" module"+s(MODULES.length)+" in "+e+"s");
  $(window).resize(resize);
  resize();
  call_module("*","done");
  async_wait(function() {
    prop.loaded=true;
    call_module("*","ready");
    if(UPDATE)
      requestAnimationFrame(update);
  });
}

function resize() {
  call_module("*","resize");
}

function update() {
  if(!prop.complete) {
    call_module("*","complete");
    prop.complete=true;
  }
//  call_module("*","update_pre");
//  call_module("*","update");
//  call_module("*","update_post");

  game_update_pre();
  aircraft_update();

  canvas_update_post();

  if(UPDATE)
    requestAnimationFrame(update);
  prop.time.frames+=1;
  prop.time.frame.count+=1;
  var elapsed=time()-prop.time.frame.start;
  if(elapsed > prop.time.frame.delay) {
    prop.time.fps=prop.time.frame.count/elapsed;
    prop.time.frame.count=0;
    prop.time.frame.start=time();
  }
  prop.time.frame.delta=Math.min(time()-prop.time.frame.last,1/20);
  prop.time.frame.last=time();
}

function delta() {
  return prop.time.frame.delta;
}
<|endoftext|>


<commit_before>(function($) {
    // All of our color classes
    var CNAMES = ['default', 'primary', 'success', 'info', 'warning', 'danger'];

    // Get classes for a label
    function classes(cname) {
        if (!~CNAMES.indexOf(cname)) cname = 'default';
        return 'tagger-label label label-' + cname;
    }

    // Get width of text in elem
    function textWidth(elem) {
        if (!this.fakeEl) this.fakeEl = $('<span>').hide().appendTo(document.body);
        this.fakeEl.text(elem.val() || elem.text()).css('font', elem.css('font'));
        return this.fakeEl.width();
    }

    // Set defaults on options object
    function setDefaultOptions(options) {
        options.terminators = options.terminators || [
                13 // return
            ];
        options.lowerCase = options.lowerCase || false;
        options.color = options.color || 'info';
        options.duplicates = options.duplicates || false;
    }

    // Trigger multiple events
    function triggerMany(elem, events, args){
        events.split(' ').forEach(function(event){
            elem.trigger(event, args);
        });
    }

    // Exten jquery here
    $.fn.extend({
        tagger: function(options) {
            options = options || {};
            setDefaultOptions(options);

            var $input = $($(this)[0]);
            var $container = $input.parent();
            var self = this;

            // Tag removal handler
            this.remove = function(tag){
                tag.remove();
                triggerMany($input, 'tagger:change tagger:remove', [self.getValues()]);
            };

            // Value getter
            this.getValues = function(){
                var output = [];
                $container.find('.tagger-content').each(function(){
                    output.push($(this).text());
                });
                return output;
            };

            // Add tag
            this.add = function(value){
                if(Array.isArray(value)){
                    return value.forEach(function(val){
                        self.add(val)
                    });
                }
                if(options.duplicates === false && !self.getValues().indexOf(value)) return;
                // Create tag
                var tag = $('<span>', {class: classes(options.color)}).append(
                    $('<span>', {class: 'tagger-content', 'contenteditable': true}).text(value).blur(function(){
                            var text = $(this).text();
                            // Remove tag if it's empty
                            if(!text) return self.remove(tag);
                            // Trigger change if the tag has changed
                            if(value !== text) triggerMany($input, 'tagger:change tagger:edit', [self.getValues()]);
                        })
                        .keydown(function(e){
                            // Return key
                            if(e.which === 13){
                                // Block it
                                e.preventDefault();
                                // Focus out input
                                $input.focus();
                            }
                        }),
                    ' ',
                    $('<a>', {class: 'glyphicon glyphicon-remove tagger-remove-tag'}).click(function(){
                        self.remove(tag);
                    })
                );
                // Add tag
                $container.append(tag);
                // Move input to the end
                $container.append($input);
                // Fire add event
                triggerMany($input, 'tagger:change tagger:add', [self.getValues()]);
            };

            // Remove last tag
            this.removeLast = function(){
                self.remove($container.find('.label').last());
            };

            // Remove tag by index
            this.removeIndex = function(i){
                self.remove($container.find('.label')[i]);
            };

            // Remove tag(s) by value
            this.removeValue = function(value){
                $container.find('.label').each(function(){
                    if($(this).find('.tagger-content').text() === value)  self.remove($(this));
                });
            };

            // Check for input
            if($input.prop('tagName') !== 'INPUT'){
                // Convert to input
                var input = $('<input>');
                $input.replaceWith(input);
                $input = input;
            }

            var starting = $input.val();
            if(starting){
                $input.val('');
                self.add(starting.split(','));
            }

            // Set input classes
            $input.removeClass().addClass('tagger-input');

            // Set input type to text
            $input.attr('type', 'text');

            // Update on each key press
            $input.keyup(function(e) {
                // Set width based on text width in $input
                $input.css('width', (textWidth($input) + 15) + 'px');

                // Get the new value
                var value = $input.val();

                if(options.lowerCase){
                    value = value.toLowerCase();
                    $input.val(value);
                }

                if(options.upperCase){
                    value = value.toUpperCase();
                    $input.val(value);
                }

                // Check for terminators
                if (~options.terminators.indexOf(e.which)) {
                     // Trim our value of spaces cause we about to save it
                    value = value.trim();
                    // If they don't have a value yet stop here
                    if(!value) return;
                    // Reset input box
                    $input.val('').keyup();
                    // Add our tag
                    self.add(value);
                    // Refocus the input
                    $input.focus();
                }

                if(($input.previous === 17 || $input.previous === 91) && e.which === 65) $input.select();
                $input.previous = e.which;

            });

            // Check for delete on each keydown
            $input.keydown(function(e){
                // Delete key and no value in text box and remove last label
                if(e.which === 8 && !$(this).val().trim()) self.removeLast();
                // Prevent submitting form on enter
                if(e.which === 13) e.preventDefault();
            });

            // Focus input when you click inside its container
            $container.click(function(e) {
                if($(e.target).is('.tagger-content')) return;
                $input.focus();
            });

            // Add hover class
            $input.focus(function() {
                $container.addClass('tagger-focused');
            });

            // Remove hover class
            $input.blur(function() {
                $container.removeClass('tagger-focused');
            });

            return this;
        }
    });

}(jQuery));
<commit_msg>Reset cursor<commit_after>(function($) {
    // All of our color classes
    var CNAMES = ['default', 'primary', 'success', 'info', 'warning', 'danger'];

    // Get classes for a label
    function classes(cname) {
        if (!~CNAMES.indexOf(cname)) cname = 'default';
        return 'tagger-label label label-' + cname;
    }

    // Get width of text in elem
    function textWidth(elem) {
        if (!this.fakeEl) this.fakeEl = $('<span>').hide().appendTo(document.body);
        this.fakeEl.text(elem.val() || elem.text()).css('font', elem.css('font'));
        return this.fakeEl.width();
    }

    // Set defaults on options object
    function setDefaultOptions(options) {
        options.terminators = options.terminators || [
                13 // return
            ];
        options.lowerCase = options.lowerCase || false;
        options.color = options.color || 'info';
        options.duplicates = options.duplicates || false;
    }

    // Trigger multiple events
    function triggerMany(elem, events, args){
        events.split(' ').forEach(function(event){
            elem.trigger(event, args);
        });
    }

    // Exten jquery here
    $.fn.extend({
        tagger: function(options) {
            options = options || {};
            setDefaultOptions(options);

            var $input = $($(this)[0]);
            var $container = $input.parent();
            var self = this;

            // Tag removal handler
            this.remove = function(tag){
                tag.remove();
                triggerMany($input, 'tagger:change tagger:remove', [self.getValues()]);
            };

            // Value getter
            this.getValues = function(){
                var output = [];
                $container.find('.tagger-content').each(function(){
                    output.push($(this).text());
                });
                return output;
            };

            // Add tag
            this.add = function(value){
                if(Array.isArray(value)){
                    return value.forEach(function(val){
                        self.add(val)
                    });
                }
                if(options.duplicates === false && !self.getValues().indexOf(value)) return;
                // Create tag
                var tag = $('<span>', {class: classes(options.color)}).append(
                    $('<span>', {class: 'tagger-content', 'contenteditable': true}).text(value).blur(function(){
                            var text = $(this).text();
                            // Remove tag if it's empty
                            if(!text) return self.remove(tag);
                            // Trigger change if the tag has changed
                            if(value !== text) triggerMany($input, 'tagger:change tagger:edit', [self.getValues()]);
                        })
                        .keydown(function(e){
                            // Return key
                            if(e.which === 13){
                                // Block it
                                e.preventDefault();
                                // Focus out input
                                $input.focus();
                            }
                        }),
                    ' ',
                    $('<a>', {class: 'glyphicon glyphicon-remove tagger-remove-tag'}).click(function(){
                        self.remove(tag);
                    })
                );
                // Add tag
                $container.append(tag);
                // Move input to the end
                $container.append($input);
                // Fire add event
                triggerMany($input, 'tagger:change tagger:add', [self.getValues()]);
            };

            // Remove last tag
            this.removeLast = function(){
                self.remove($container.find('.label').last());
            };

            // Remove tag by index
            this.removeIndex = function(i){
                self.remove($container.find('.label')[i]);
            };

            // Remove tag(s) by value
            this.removeValue = function(value){
                $container.find('.label').each(function(){
                    if($(this).find('.tagger-content').text() === value)  self.remove($(this));
                });
            };

            // Check for input
            if($input.prop('tagName') !== 'INPUT'){
                // Convert to input
                var input = $('<input>');
                $input.replaceWith(input);
                $input = input;
            }

            var starting = $input.val();
            if(starting){
                $input.val('');
                self.add(starting.split(','));
            }

            // Set input classes
            $input.removeClass().addClass('tagger-input');

            // Set input type to text
            $input.attr('type', 'text');

            // Update on each key press
            $input.keyup(function(e) {
                // Set width based on text width in $input
                $input.css('width', (textWidth($input) + 15) + 'px');

                // Get the new value
                var value = $input.val();

                if(options.lowerCase){
                    value = value.toLowerCase();
                    $input.val(value);
                }

                if(options.upperCase){
                    value = value.toUpperCase();
                    $input.val(value);
                }

                // Check for terminators
                if (~options.terminators.indexOf(e.which)) {
                     // Trim our value of spaces cause we about to save it
                    value = value.trim();
                    // If they don't have a value yet stop here
                    if(!value) return;
                    // Reset input box
                    $input.val('').keyup();
                    // Add our tag
                    self.add(value);
                    // Refocus the input
                    $input.focus();
                }

                if(($input.previous === 17 || $input.previous === 91) && e.which === 65) $input.select();
                $input.previous = e.which;

            });

            // Check for delete on each keydown
            $input.keydown(function(e){
                // Delete key and no value in text box and remove last label
                if(e.which === 8 && !$(this).val().trim()) self.removeLast();
                // Prevent submitting form on enter
                if(e.which === 13) e.preventDefault();
            });

            // Focus input when you click inside its container
            $container.click(function(e) {
                // Don't trigger if they're clicking the content because it's editable
                if($(e.target).is('.tagger-content')) return;
                // Focus our input field
                $input.focus();
                // Reset text and subsequently the cursor
                $input.text($input.text());
            });

            // Add hover class
            $input.focus(function() {
                $container.addClass('tagger-focused');
            });

            // Remove hover class
            $input.blur(function() {
                $container.removeClass('tagger-focused');
            });

            return this;
        }
    });

}(jQuery));
<|endoftext|>