├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── TODO.md
├── config.mk
├── src
├── wtplan-web
│ ├── calendar_item.js
│ ├── component.js
│ ├── day_list.js
│ ├── day_list_day.js
│ ├── day_list_item.js
│ ├── dev_build_restart.sh
│ ├── edit_item_view.js
│ ├── flow_view.js
│ ├── include_text
│ │ └── includetext.go
│ ├── index.html
│ ├── lib
│ │ ├── ZEPTO_LICENCE
│ │ └── zepto.js
│ ├── login_view.js
│ ├── main.go
│ ├── main_page.js
│ ├── remote_commands.js
│ └── utility_functions.js
└── wtplan
│ ├── generate_version_str
│ └── generate_version_str.go
│ └── main.go
└── wtplan.1
/.gitignore:
--------------------------------------------------------------------------------
1 | .#*
2 | *~
3 | /wtplan
4 | /wtplan-web
5 | /bin
6 | src/wtplan-web/textfiles.go
7 | src/wtplan-web/wtplan-web
8 | src/wtplan/wtplan
9 | src/wtplan/version.go
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2017 Kjell Winblad (kjellwinblad@gmail.com, http://winsh.me)
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # wtplan
2 | # See LICENSE file for copyright and license details.
3 |
4 | include config.mk
5 |
6 |
7 | export GOPATH=$(shell pwd)
8 | export VERSIONSTR=${VERSION}
9 |
10 | all: wtplan wtplan-web
11 |
12 | wtplan: src/wtplan/main.go
13 | ${GO} generate ./src/wtplan/main.go
14 | ${GO} build ./src/wtplan/main.go ./src/wtplan/version.go
15 | @mv main wtplan
16 |
17 | wtplan-web: $(wildcard src/wtplan-web/*.go src/wtplan-web/*.js)
18 | ${GO} generate ./src/wtplan-web/main.go
19 | ${GO} build ./src/wtplan-web/main.go ./src/wtplan-web/textfiles.go
20 | @mv main wtplan-web
21 |
22 | clean:
23 | @echo cleaning
24 | @rm -f wtplan
25 | @rm -f wtplan-web
26 |
27 | dist: clean
28 | @echo creating dist tarball
29 | @mkdir -p wtplan-${VERSION}
30 | @cp -R src LICENSE Makefile config.mk README.md wtplan.1 wtplan-${VERSION}
31 | @tar -cf wtplan-${VERSION}.tar wtplan-${VERSION}
32 | @gzip wtplan-${VERSION}.tar
33 | @rm -rf wtplan-${VERSION}
34 |
35 | install: all
36 | @echo installing executable files wtplan and wtplan-web to ${DESTDIR}${PREFIX}/bin
37 | @mkdir -p ${DESTDIR}${PREFIX}/bin
38 | @cp -f wtplan ${DESTDIR}${PREFIX}/bin
39 | @chmod 755 ${DESTDIR}${PREFIX}/bin/wtplan
40 | @cp -f wtplan-web ${DESTDIR}${PREFIX}/bin
41 | @chmod 755 ${DESTDIR}${PREFIX}/bin/wtplan-web
42 | @echo installing manual page to ${DESTDIR}${MANPREFIX}/man1
43 | @mkdir -p ${DESTDIR}${MANPREFIX}/man1
44 | @sed "s/VERSION/${VERSION}/g" < wtplan.1 > ${DESTDIR}${MANPREFIX}/man1/wtplan.1
45 | @chmod 644 ${DESTDIR}${MANPREFIX}/man1/wtplan.1
46 |
47 | uninstall:
48 | @echo removing executable files from ${DESTDIR}${PREFIX}/bin
49 | @rm -f ${DESTDIR}${PREFIX}/bin/wtplan
50 | @rm -f ${DESTDIR}${PREFIX}/bin/wtplan-web
51 | @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
52 | @rm -f ${DESTDIR}${MANPREFIX}/man1/wtplan.1
53 |
54 | .PHONY: all clean dist install uninstall
55 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | wtplan - web terminal planner
2 | =============================
3 |
4 | wtplan helps you manage a calendar. The calendar can be managed from a
5 | command line interface and/or a web interface. The calendar data is
6 | stored in a simple text file (located in `~/.wtplan` by
7 | default). wtplan integrates with git to make backup and
8 | synchronization between multiple computers convenient.
9 |
10 |
11 | See the man page ([wtplan.1](http://wtplan.winsh.me/man_page.html)) for more details
12 |
13 | * [Website](http://wtplan.winsh.me/)
14 | * [Screenshots](http://wtplan.winsh.me/screenshots.html)
15 |
16 | Features
17 | --------
18 |
19 | * command line interface
20 | * web interface
21 | * optional password authentication
22 | * git integration
23 | * simple json based data format
24 |
25 | Requirements
26 | ------------
27 |
28 | * golang is required to compile wtplan.
29 | * (optional) git is required if you want to use wtplan's git integration.
30 | * (optional) make makes building and installing more convenient but is not
31 | required. See the Makefile for how to build without make.
32 |
33 | Install
34 | -------
35 |
36 | 1. Edit the paths in config.mk to fit your system if needed. (wtplan is
37 | installed into `/usr/local/{bin,share/man/man1}` by default.)
38 | 2. Run `make install` as root. (This will build and install wtplan)
39 |
40 | License
41 | -------
42 |
43 | The MIT/X Consortium License. See the LICENSE file for details.
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | * Make the web interface usable even when there is no connection to
2 | the server. This can be done by adding a command queue to the web
3 | interface that can be flushed when the server is accessible again.
4 |
5 | * Make it possible to read calendar data from multiple config
6 | folders. This can be useful if one wants to share a kid's calendar
7 | with the other parent. This could be implemented by letting one
8 | specify additional calendar data files when starting the web
9 | interface. The calendar data from the additional calendar data files
10 | would then be viewable in a read-only way in the web interface.
11 |
12 | * Support for importing calendar data from an iCal formatted calendar
13 | data file.
--------------------------------------------------------------------------------
/config.mk:
--------------------------------------------------------------------------------
1 | # wtplan version
2 | VERSION = 0.1
3 |
4 | # Customize below to fit your system
5 |
6 | # paths
7 | PREFIX = /usr/local
8 | MANPREFIX = ${PREFIX}/share/man
9 |
10 | # Compiler and build tool
11 | GO = go
12 |
--------------------------------------------------------------------------------
/src/wtplan-web/calendar_item.js:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Kjell Winblad (kjellwinblad@gmail.com, http://winsh.me)
2 | // License: MIT License, see the LICENSE file
3 |
4 | var WTPLAN = WTPLAN || {};
5 |
6 | (function() {
7 |
8 | //Constructor
9 | WTPLAN.CalendarItem = function(calendarItemData, id) {
10 | this.date = calendarItemData.date;
11 | this.duration = calendarItemData.duration;
12 | this.description = calendarItemData.description;
13 | this.id = id;
14 | };
15 |
16 | WTPLAN.CalendarItem.prototype.startDate = function() {
17 | return new Date(new Date(this.date).valueOf() + 1);
18 | // (One ms is added to avoid being bwtween days)
19 | };
20 |
21 | WTPLAN.CalendarItem.prototype.endDate = function() {
22 | var result = WTPLAN.durationRegExp.exec(this.duration);
23 | if (result[1] === "NA") {
24 | return this.startDate();
25 | }
26 | var hours = 0;
27 | var minutes = 0;
28 | if (result[2] != undefined) {
29 | hours = parseInt(result[2], 10);
30 | }
31 | if (result[3] != undefined) {
32 | minutes = parseInt(result[3], 10);
33 | }
34 | var endDate = new Date(this.startDate());
35 | endDate.setHours(endDate.getHours() + hours);
36 | endDate.setMinutes(endDate.getMinutes() + minutes);
37 | return endDate;
38 | };
39 |
40 | WTPLAN.CalendarItem.prototype.toPlainObject = function() {
41 | return {
42 | date: this.date,
43 | duration: this.duration,
44 | description: this.description
45 | };
46 | };
47 |
48 | WTPLAN.CalendarItem.prototype.toString = function() {
49 | return this.date;
50 | };
51 |
52 | })();
53 |
--------------------------------------------------------------------------------
/src/wtplan-web/component.js:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Kjell Winblad (kjellwinblad@gmail.com, http://winsh.me)
2 | // License: MIT License, see the LICENSE file
3 |
4 | var WTPLAN = WTPLAN || {};
5 |
6 | (function() {
7 | var componentId = 0;
8 |
9 | WTPLAN.Component = function(props) {
10 | if (props === undefined) {
11 | this.state = {};
12 | } else {
13 | this.state = props;
14 | }
15 | this.componentId = componentId;
16 | componentId = componentId + 1;
17 | }
18 |
19 | WTPLAN.Component.prototype.renderAt = function(element) {
20 | var newElement = $(this.render());
21 | newElement.attr("data-wtplanid", "" + this.componentId);
22 | element.replaceWith(newElement);
23 | var outerThis = this;
24 | outerThis.componentDidMount(newElement);
25 | };
26 |
27 | WTPLAN.Component.prototype.setState = function(newState) {
28 | this.state = newState;
29 | var element = this.getRenderedComponent();
30 | var renderedHtml = this.render();
31 | var newElement = $(renderedHtml);
32 | newElement.attr("data-wtplanid", "" + this.componentId);
33 | element.replaceWith(newElement);
34 | var outerThis = this;
35 | outerThis.componentDidMount(newElement);
36 | };
37 |
38 | WTPLAN.Component.prototype.render = function() {
39 | console.log("WARNING: render has not been implemented for component");
40 | }
41 |
42 | WTPLAN.Component.prototype.componentDidMount = function(element) {
43 |
44 | }
45 |
46 | WTPLAN.Component.prototype.getRenderedComponent = function() {
47 | return $('[data-wtplanid="' + this.componentId + '"]');
48 | }
49 |
50 | // Object.create() polyfill from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
51 |
52 | if (typeof Object.create != 'function') {
53 | Object.create = (function(undefined) {
54 | var Temp = function() {};
55 | return function(prototype, propertiesObject) {
56 | if (prototype !== Object(prototype) && prototype !== null) {
57 | throw TypeError('Argument must be an object, or null');
58 | }
59 | Temp.prototype = prototype || {};
60 | var result = new Temp();
61 | Temp.prototype = null;
62 | if (propertiesObject !== undefined) {
63 | Object.defineProperties(result, propertiesObject);
64 | }
65 |
66 | // to imitate the case of Object.create(null)
67 | if (prototype === null) {
68 | result.__proto__ = null;
69 | }
70 | return result;
71 | };
72 | })();
73 | }
74 |
75 | })();
76 |
--------------------------------------------------------------------------------
/src/wtplan-web/day_list.js:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Kjell Winblad (kjellwinblad@gmail.com, http://winsh.me)
2 | // License: MIT License, see the LICENSE file
3 |
4 | var WTPLAN = WTPLAN || {};
5 |
6 | (function() {
7 |
8 | //Constructor
9 | WTPLAN.DayList = function(params) {
10 | WTPLAN.Component.call(this);
11 | this.state = params;
12 | }
13 | //Extending Component
14 | WTPLAN.DayList.prototype = Object.create(WTPLAN.Component.prototype);
15 | WTPLAN.DayList.prototype.constructor = WTPLAN.DayList;
16 |
17 | //Methods
18 | WTPLAN.DayList.prototype.render = function() {
19 | var dayDivs = "";
20 | var i = 0;
21 | for (var i = 0; i <= this.state.numberOfDaysAfterToday; i++) {
22 | dayDivs = dayDivs + '
';
23 | }
24 | return '' + dayDivs + '
';
25 | };
26 |
27 | WTPLAN.DayList.prototype.componentDidMount = function(component) {
28 | var currentDay = WTPLAN.getLocalStartOfDay(this.state.currentDay);
29 | var i = 0;
30 | for (; i <= this.state.numberOfDaysAfterToday; i++) {
31 | new WTPLAN.DayListDay({
32 | 'currentDay': new Date(currentDay.getTime()),
33 | 'calendarItems': this.state.calendarItems,
34 | 'openAddDialog': this.state.openAddDialog,
35 | 'openEditDialog': this.state.openEditDialog,
36 | 'removeItemAction': this.state.removeItemAction
37 | }).renderAt($('#day' + i));
38 | currentDay.setDate(currentDay.getDate() + 1);;
39 | }
40 | };
41 |
42 | })()
43 |
--------------------------------------------------------------------------------
/src/wtplan-web/day_list_day.js:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Kjell Winblad (kjellwinblad@gmail.com, http://winsh.me)
2 | // License: MIT License, see the LICENSE file
3 |
4 | var WTPLAN = WTPLAN || {};
5 |
6 | (function() {
7 |
8 | //Constructor
9 | WTPLAN.DayListDay = function(state) {
10 | WTPLAN.Component.call(this);
11 | this.state = state;
12 | }
13 | //Extending Component
14 | WTPLAN.DayListDay.prototype = Object.create(WTPLAN.Component.prototype);
15 | WTPLAN.DayListDay.prototype.constructor = WTPLAN.DayListDay;
16 |
17 | //Methods
18 | WTPLAN.DayListDay.prototype.render = function() {
19 | var currentDay = this.state.currentDay;
20 | var nextDay = new Date(currentDay.getTime());
21 | nextDay.setDate(currentDay.getDate() + 1);
22 | var itemsToday = this.state.calendarItems.filter(function(item) {
23 | var itemStartDate = item.startDate();
24 | var itemEndDate = item.endDate();
25 | return (itemStartDate.getTime() >= currentDay.getTime() &&
26 | itemStartDate < nextDay.getTime()) ||
27 | (itemEndDate.getTime() >= currentDay.getTime() &&
28 | itemEndDate < nextDay.getTime()) ||
29 | (itemStartDate.getTime() < currentDay.getTime() &&
30 | itemEndDate >= nextDay.getTime());
31 | });
32 | this.tmpItemItemDivIdList = itemsToday.map(function(item, index) {
33 | return {
34 | 'item': item,
35 | itemId: "item" + index
36 | };
37 | });
38 | var itemDivs = this.tmpItemItemDivIdList.reduce(function(acc, curr) {
39 | return acc + '';
40 | }, "");
41 | var dayHeader = WTPLAN.template(
42 | '\
43 | <%localDateString%> <%localDayString%>\
44 |
\
45 |
',
46 | {localDateString: WTPLAN.getLocalDayString(currentDay),
47 | localDayString: WTPLAN.dayStringFromDate(currentDay),
48 | componentId: this.componentId});
49 | return WTPLAN.template(
50 | '\
51 | <%dayHeader%>\
52 |
<%itemDivs%>
\
53 |
',
54 | {dayHeader: dayHeader,
55 | itemDivs: itemDivs});
56 | };
57 |
58 | WTPLAN.DayListDay.prototype.componentDidMount = function(component) {
59 | var outerThis = this;
60 | var currentDay = this.state.currentDay;
61 | var nextDay = new Date(currentDay.getTime());
62 | nextDay.setDate(currentDay.getDate() + 1);
63 | this.tmpItemItemDivIdList.forEach(function(tuple) {
64 | new WTPLAN.DayListItem({
65 | calendarItem: tuple.item,
66 | 'currentDay': currentDay,
67 | 'nextDay': nextDay,
68 | 'openEditDialog': outerThis.state.openEditDialog,
69 | 'removeItemAction': outerThis.state.removeItemAction
70 | }).renderAt(component.find('[data-itemId="' + tuple.itemId +'"]'));
71 | });
72 | $('#addButton' + outerThis.componentId).click(function() {
73 | outerThis.state.openAddDialog(WTPLAN.dateToRfc3339String(
74 | outerThis.state.currentDay));
75 | });
76 | };
77 |
78 | })()
79 |
--------------------------------------------------------------------------------
/src/wtplan-web/day_list_item.js:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Kjell Winblad (kjellwinblad@gmail.com, http://winsh.me)
2 | // License: MIT License, see the LICENSE file
3 |
4 | var WTPLAN = WTPLAN || {};
5 |
6 | (function() {
7 |
8 | var dayListItemIdCounter = 0;
9 |
10 | //Constructor
11 | WTPLAN.DayListItem = function(state) {
12 | WTPLAN.Component.call(this);
13 | this.dayListItemId = dayListItemIdCounter;
14 | dayListItemIdCounter = dayListItemIdCounter + 1;
15 | this.state = state;
16 | }
17 | //Extending Component
18 | WTPLAN.DayListItem.prototype = Object.create(WTPLAN.Component.prototype);
19 | WTPLAN.DayListItem.prototype.constructor = WTPLAN.DayListItem;
20 |
21 | //Methods
22 | WTPLAN.DayListItem.prototype.render = function() {
23 | var currentDayTime = this.state.currentDay.getTime();
24 | var nextDayTime = this.state.nextDay.getTime();
25 | var startDate = this.state.calendarItem.startDate();
26 | var startDateTime = startDate.getTime();
27 | var endDate = this.state.calendarItem.endDate();
28 | var endDateTime = endDate.getTime();
29 | var hoursStart = startDate.getHours();
30 | var minutesStart = startDate.getMinutes();
31 | var hoursEnd = endDate.getHours();
32 | var minutesEnd = endDate.getMinutes();
33 |
34 | function pad(i) {
35 | var str = "" + i;
36 | return ('00' + str).substring(str.length);
37 | }
38 | var timeString = undefined;
39 | if ((startDateTime >= currentDayTime && startDateTime < nextDayTime) &&
40 | (endDateTime >= currentDayTime && endDateTime < nextDayTime)) {
41 | //Both end and start date are inside day
42 | timeString = pad(hoursStart) + ":" + pad(minutesStart);
43 | if (startDate.getTime() != endDate.getTime()) {
44 | timeString = timeString + "-" + pad(hoursEnd) + ":" + pad(minutesEnd);
45 | }
46 | } else if (startDateTime >= currentDayTime && startDateTime < nextDayTime) {
47 | // Start date in day
48 | timeString = pad(hoursStart) + ":" + pad(minutesStart) + "-(->)";
49 | } else if (endDateTime >= currentDayTime && endDateTime < nextDayTime) {
50 | // End date in day
51 | timeString = "(<-)-" + pad(hoursEnd) + ":" + pad(minutesEnd);
52 | } else if (startDateTime < currentDayTime && endDateTime >= nextDayTime) {
53 | //Start date before day and end date after day
54 | timeString = "(<-)-(->)";
55 | }
56 | return WTPLAN.template(
57 | '\
58 | \
63 | <%description%>\
64 |
',
65 | {timeString: timeString,
66 | dayListItemId: this.dayListItemId,
67 | description: this.state.calendarItem.description});
68 |
69 | };
70 |
71 | WTPLAN.DayListItem.prototype.componentDidMount = function(component) {
72 | var outerThis = this;
73 | $('#editButton' +outerThis.dayListItemId).click(function() {
74 | outerThis.state.openEditDialog(outerThis.state.calendarItem);
75 | });
76 | $('#removeButton' + outerThis.dayListItemId).click(function() {
77 | outerThis.state.removeItemAction(outerThis.state.calendarItem.id);
78 | });
79 | };
80 |
81 | })();
82 |
--------------------------------------------------------------------------------
/src/wtplan-web/dev_build_restart.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | sigint_handler()
4 | {
5 | kill $PID
6 | exit
7 | }
8 |
9 | trap sigint_handler SIGINT
10 |
11 | while true; do
12 | go generate
13 | go build
14 | ./wtplan-web &
15 | PID=$!
16 | inotifywait -e modify -e move -e create -e delete -e attrib -r `pwd`
17 | kill $PID
18 | done
19 |
--------------------------------------------------------------------------------
/src/wtplan-web/edit_item_view.js:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Kjell Winblad (kjellwinblad@gmail.com, http://winsh.me)
2 | // License: MIT License, see the LICENSE file
3 |
4 | var WTPLAN = WTPLAN || {};
5 |
6 | (function() {
7 |
8 | function validateDurationFiled() {
9 | var str = $('#durationField').val();
10 | var result = WTPLAN.durationRegExp.exec(str);
11 | return result != null && str == result[0];
12 | }
13 |
14 | function validateDateFiled() {
15 | var str = $('#dateField').val();
16 | var result = WTPLAN.rfc3339regexp.exec(str);
17 | return result != null && str == result[0];
18 | }
19 |
20 | function validateDescriptionFiled() {
21 | var str = $('#descriptionField').val();
22 | return str != "";
23 | }
24 |
25 | //Constructor
26 | WTPLAN.EditItemView = function(calendarItem, saveCallback, cancelCallback) {
27 | WTPLAN.Component.call(this);
28 | this.state.calendarItem = calendarItem;
29 | this.saveCallback = saveCallback;
30 | this.cancelCallback = cancelCallback;
31 | }
32 | //Extending Component
33 | WTPLAN.EditItemView.prototype = Object.create(WTPLAN.Component.prototype);
34 | WTPLAN.EditItemView.prototype.constructor = WTPLAN.EditItemView;
35 |
36 | //Methods
37 | WTPLAN.EditItemView.prototype.render = function() {
38 | return WTPLAN.template(
39 | '\\
40 | \
41 | \
42 |
\
43 | Date (RFC3339):\
44 | \
45 |
\
46 | Duration (e.g. NA, 2h, 2h20m):\
47 | \
48 |
\
49 | Description:\
50 | \
51 | ',
52 | {date: this.state.calendarItem.date,
53 | duration: this.state.calendarItem.duration,
54 | description: this.state.calendarItem.description});
55 |
56 | };
57 |
58 | WTPLAN.EditItemView.prototype.componentDidMount = function(component) {
59 | var outerThis = this;
60 | $("#saveButton").click(function() {
61 | if (!validateDateFiled()) {
62 | alert("The date filed contains an invalid date.");
63 | return;
64 | }
65 | if (!validateDurationFiled()) {
66 | alert("The duration filed contains an invalid date.");
67 | return;
68 | }
69 | if (!validateDescriptionFiled()) {
70 | alert("The description filed is empty.");
71 | return;
72 | }
73 | outerThis.saveCallback(new WTPLAN.CalendarItem({
74 | date: $("#dateField").val(),
75 | duration: $("#durationField").val(),
76 | description: $("#descriptionField").val()
77 | }));
78 | });
79 | $("#cancelButton").click(function() {
80 | outerThis.cancelCallback();
81 | });
82 | $("#dateField").on('change textInput input', function() {
83 | if (!validateDateFiled()) {
84 | $(this).css({
85 | backgroundColor: 'red'
86 | });
87 | } else {
88 | $(this).css({
89 | backgroundColor: ''
90 | });
91 | }
92 | });
93 | $("#durationField").on('change textInput input', function() {
94 | if (!validateDurationFiled()) {
95 | $(this).css({
96 | backgroundColor: 'red'
97 | });
98 | } else {
99 | $(this).css({
100 | backgroundColor: ''
101 | });
102 | }
103 | });
104 | };
105 |
106 | })()
107 |
--------------------------------------------------------------------------------
/src/wtplan-web/flow_view.js:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Kjell Winblad (kjellwinblad@gmail.com, http://winsh.me)
2 | // License: MIT License, see the LICENSE file
3 |
4 | var WTPLAN = WTPLAN || {};
5 |
6 | (function() {
7 |
8 | //Constructor
9 | WTPLAN.FlowView = function(openAddDialog, openEditDialog, removeItemAction) {
10 | WTPLAN.Component.call(this);
11 | this.state = {
12 | currentDay: WTPLAN.getLocalDayString(),
13 | numberOfDaysAfterToday: 9,
14 | calendarItems: [],
15 | 'openAddDialog': openAddDialog,
16 | 'openEditDialog': openEditDialog,
17 | 'removeItemAction': removeItemAction
18 | }
19 |
20 | this._dayList = new WTPLAN.DayList(this.state);
21 | }
22 |
23 | //Extending Component
24 | WTPLAN.FlowView.prototype = Object.create(WTPLAN.Component.prototype);
25 | WTPLAN.FlowView.prototype.constructor = WTPLAN.FlowView;
26 |
27 | //Mathods
28 | WTPLAN.FlowView.prototype.render = function() {
29 | return WTPLAN.template(
30 | '\
31 |
\
32 | and the next
day(s):\
33 |
\
34 |
\
35 |
\
36 |
',
37 | {currentDay: this.state.currentDay,
38 | numberOfDaysAfterToday: this.state.numberOfDaysAfterToday}
39 | );
40 | };
41 |
42 | WTPLAN.FlowView.prototype.componentDidMount = function(component) {
43 | var outerThis = this;
44 | $(component).find("#dateInput").on('change textInput input', function() {
45 | var date = new Date($(this).val());
46 | if (isNaN(date.getTime())) {
47 | $(this).css({
48 | backgroundColor: 'red'
49 | });
50 | } else {
51 | $(this).css({
52 | backgroundColor: ''
53 | });
54 | var newCurrentDay = WTPLAN.getLocalDayString(date);
55 | if (newCurrentDay != outerThis.state.currentDay) {
56 | outerThis.state.currentDay = newCurrentDay;
57 | outerThis._dayList.setState(outerThis.state);
58 | }
59 | }
60 |
61 | });
62 | $(component).find("#daysInput").on('change textInput input', function() {
63 | var days = parseInt($(this).val(), 10);
64 | if (isNaN(days)) {
65 | $(this).css({
66 | backgroundColor: 'red'
67 | });
68 | } else {
69 | $(this).css({
70 | backgroundColor: ''
71 | });
72 | if (days != outerThis.state.numberOfDaysAfterToday) {
73 | outerThis.state.numberOfDaysAfterToday = days;
74 | outerThis._dayList.setState(outerThis.state);
75 | }
76 | }
77 |
78 | });
79 | this._dayList.renderAt($('#dayListDiv'));
80 | };
81 |
82 | })()
83 |
--------------------------------------------------------------------------------
/src/wtplan-web/include_text/includetext.go:
--------------------------------------------------------------------------------
1 | //Copyright 2017 Kjell Winblad (kjellwinblad@gmail.com, http://winsh.me)
2 | //License: MIT License, see the LICENSE file
3 |
4 | // Code created with the help of Stack Overflow question
5 | // http://stackoverflow.com/questions/17796043/golang-embedding-text-file-into-compiled-executable
6 | // Question by Zvika:
7 | // http://stackoverflow.com/users/1543290/zvika
8 | // Answer by Johan Wikström:
9 | // http://stackoverflow.com/users/702065/johan-wikstr%c3%b6m
10 |
11 |
12 | package main
13 |
14 | import (
15 | "bytes"
16 | "fmt"
17 | "io"
18 | "io/ioutil"
19 | "os"
20 | "path/filepath"
21 | "strings"
22 | )
23 |
24 | // Reads all .html and .js files in the current folder
25 | // and encodes them as strings literals in textfiles.go
26 | func main() {
27 | fs1, _ := ioutil.ReadDir(".")
28 | fs2, _ := ioutil.ReadDir("./lib")
29 | var fs []string
30 | for i := range fs1 {
31 | fs = append(fs, fs1[i].Name())
32 | }
33 | for i := range fs2 {
34 | fs = append(fs, filepath.Join("./lib", fs2[i].Name()))
35 | }
36 | out, _ := os.Create("textfiles.go")
37 | out.Write([]byte("package main \n\nconst (\n"))
38 | for _, f := range fs {
39 | if strings.HasSuffix(f, ".html") || strings.HasSuffix(f, ".js") {
40 | stringsToReplace := []string{"/", "_", "-", "."}
41 | fieldName := f
42 | for i := range stringsToReplace {
43 | fieldName = strings.Replace(fieldName, stringsToReplace[i], "D", -1)
44 | }
45 | out.Write([]byte(fieldName + " = `"))
46 | fileContent, err := ioutil.ReadFile(f)
47 | _, err = io.Copy(out, bytes.NewBufferString(strings.Replace(string(fileContent), "`", "`+\"`\"+`", -1)))
48 | if err != nil {
49 | fmt.Println(err)
50 | }
51 | out.Write([]byte("`\n"))
52 | }
53 | }
54 | out.Write([]byte(")\n"))
55 | }
56 |
--------------------------------------------------------------------------------
/src/wtplan-web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |
11 |
14 |
15 |
16 | Loading...
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/wtplan-web/lib/ZEPTO_LICENCE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2010-2017 Thomas Fuchs
2 | http://zeptojs.com/
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining
5 | a copy of this software and associated documentation files (the
6 | "Software"), to deal in the Software without restriction, including
7 | without limitation the rights to use, copy, modify, merge, publish,
8 | distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to
10 | the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/wtplan-web/lib/zepto.js:
--------------------------------------------------------------------------------
1 | /* Zepto v1.2.0 - zepto event ajax form ie - zeptojs.com/license */
2 | (function(global, factory) {
3 | if (typeof define === 'function' && define.amd)
4 | define(function() { return factory(global) })
5 | else
6 | factory(global)
7 | }(this, function(window) {
8 | var Zepto = (function() {
9 | var undefined, key, $, classList, emptyArray = [], concat = emptyArray.concat, filter = emptyArray.filter, slice = emptyArray.slice,
10 | document = window.document,
11 | elementDisplay = {}, classCache = {},
12 | cssNumber = { 'column-count': 1, 'columns': 1, 'font-weight': 1, 'line-height': 1,'opacity': 1, 'z-index': 1, 'zoom': 1 },
13 | fragmentRE = /^\s*<(\w+|!)[^>]*>/,
14 | singleTagRE = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
15 | tagExpanderRE = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
16 | rootNodeRE = /^(?:body|html)$/i,
17 | capitalRE = /([A-Z])/g,
18 |
19 | // special attributes that should be get/set via method calls
20 | methodAttributes = ['val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset'],
21 |
22 | adjacencyOperators = [ 'after', 'prepend', 'before', 'append' ],
23 | table = document.createElement('table'),
24 | tableRow = document.createElement('tr'),
25 | containers = {
26 | 'tr': document.createElement('tbody'),
27 | 'tbody': table, 'thead': table, 'tfoot': table,
28 | 'td': tableRow, 'th': tableRow,
29 | '*': document.createElement('div')
30 | },
31 | readyRE = /complete|loaded|interactive/,
32 | simpleSelectorRE = /^[\w-]*$/,
33 | class2type = {},
34 | toString = class2type.toString,
35 | zepto = {},
36 | camelize, uniq,
37 | tempParent = document.createElement('div'),
38 | propMap = {
39 | 'tabindex': 'tabIndex',
40 | 'readonly': 'readOnly',
41 | 'for': 'htmlFor',
42 | 'class': 'className',
43 | 'maxlength': 'maxLength',
44 | 'cellspacing': 'cellSpacing',
45 | 'cellpadding': 'cellPadding',
46 | 'rowspan': 'rowSpan',
47 | 'colspan': 'colSpan',
48 | 'usemap': 'useMap',
49 | 'frameborder': 'frameBorder',
50 | 'contenteditable': 'contentEditable'
51 | },
52 | isArray = Array.isArray ||
53 | function(object){ return object instanceof Array }
54 |
55 | zepto.matches = function(element, selector) {
56 | if (!selector || !element || element.nodeType !== 1) return false
57 | var matchesSelector = element.matches || element.webkitMatchesSelector ||
58 | element.mozMatchesSelector || element.oMatchesSelector ||
59 | element.matchesSelector
60 | if (matchesSelector) return matchesSelector.call(element, selector)
61 | // fall back to performing a selector:
62 | var match, parent = element.parentNode, temp = !parent
63 | if (temp) (parent = tempParent).appendChild(element)
64 | match = ~zepto.qsa(parent, selector).indexOf(element)
65 | temp && tempParent.removeChild(element)
66 | return match
67 | }
68 |
69 | function type(obj) {
70 | return obj == null ? String(obj) :
71 | class2type[toString.call(obj)] || "object"
72 | }
73 |
74 | function isFunction(value) { return type(value) == "function" }
75 | function isWindow(obj) { return obj != null && obj == obj.window }
76 | function isDocument(obj) { return obj != null && obj.nodeType == obj.DOCUMENT_NODE }
77 | function isObject(obj) { return type(obj) == "object" }
78 | function isPlainObject(obj) {
79 | return isObject(obj) && !isWindow(obj) && Object.getPrototypeOf(obj) == Object.prototype
80 | }
81 |
82 | function likeArray(obj) {
83 | var length = !!obj && 'length' in obj && obj.length,
84 | type = $.type(obj)
85 |
86 | return 'function' != type && !isWindow(obj) && (
87 | 'array' == type || length === 0 ||
88 | (typeof length == 'number' && length > 0 && (length - 1) in obj)
89 | )
90 | }
91 |
92 | function compact(array) { return filter.call(array, function(item){ return item != null }) }
93 | function flatten(array) { return array.length > 0 ? $.fn.concat.apply([], array) : array }
94 | camelize = function(str){ return str.replace(/-+(.)?/g, function(match, chr){ return chr ? chr.toUpperCase() : '' }) }
95 | function dasherize(str) {
96 | return str.replace(/::/g, '/')
97 | .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
98 | .replace(/([a-z\d])([A-Z])/g, '$1_$2')
99 | .replace(/_/g, '-')
100 | .toLowerCase()
101 | }
102 | uniq = function(array){ return filter.call(array, function(item, idx){ return array.indexOf(item) == idx }) }
103 |
104 | function classRE(name) {
105 | return name in classCache ?
106 | classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)'))
107 | }
108 |
109 | function maybeAddPx(name, value) {
110 | return (typeof value == "number" && !cssNumber[dasherize(name)]) ? value + "px" : value
111 | }
112 |
113 | function defaultDisplay(nodeName) {
114 | var element, display
115 | if (!elementDisplay[nodeName]) {
116 | element = document.createElement(nodeName)
117 | document.body.appendChild(element)
118 | display = getComputedStyle(element, '').getPropertyValue("display")
119 | element.parentNode.removeChild(element)
120 | display == "none" && (display = "block")
121 | elementDisplay[nodeName] = display
122 | }
123 | return elementDisplay[nodeName]
124 | }
125 |
126 | function children(element) {
127 | return 'children' in element ?
128 | slice.call(element.children) :
129 | $.map(element.childNodes, function(node){ if (node.nodeType == 1) return node })
130 | }
131 |
132 | function Z(dom, selector) {
133 | var i, len = dom ? dom.length : 0
134 | for (i = 0; i < len; i++) this[i] = dom[i]
135 | this.length = len
136 | this.selector = selector || ''
137 | }
138 |
139 | // `$.zepto.fragment` takes a html string and an optional tag name
140 | // to generate DOM nodes from the given html string.
141 | // The generated DOM nodes are returned as an array.
142 | // This function can be overridden in plugins for example to make
143 | // it compatible with browsers that don't support the DOM fully.
144 | zepto.fragment = function(html, name, properties) {
145 | var dom, nodes, container
146 |
147 | // A special case optimization for a single tag
148 | if (singleTagRE.test(html)) dom = $(document.createElement(RegExp.$1))
149 |
150 | if (!dom) {
151 | if (html.replace) html = html.replace(tagExpanderRE, "<$1>$2>")
152 | if (name === undefined) name = fragmentRE.test(html) && RegExp.$1
153 | if (!(name in containers)) name = '*'
154 |
155 | container = containers[name]
156 | container.innerHTML = '' + html
157 | dom = $.each(slice.call(container.childNodes), function(){
158 | container.removeChild(this)
159 | })
160 | }
161 |
162 | if (isPlainObject(properties)) {
163 | nodes = $(dom)
164 | $.each(properties, function(key, value) {
165 | if (methodAttributes.indexOf(key) > -1) nodes[key](value)
166 | else nodes.attr(key, value)
167 | })
168 | }
169 |
170 | return dom
171 | }
172 |
173 | // `$.zepto.Z` swaps out the prototype of the given `dom` array
174 | // of nodes with `$.fn` and thus supplying all the Zepto functions
175 | // to the array. This method can be overridden in plugins.
176 | zepto.Z = function(dom, selector) {
177 | return new Z(dom, selector)
178 | }
179 |
180 | // `$.zepto.isZ` should return `true` if the given object is a Zepto
181 | // collection. This method can be overridden in plugins.
182 | zepto.isZ = function(object) {
183 | return object instanceof zepto.Z
184 | }
185 |
186 | // `$.zepto.init` is Zepto's counterpart to jQuery's `$.fn.init` and
187 | // takes a CSS selector and an optional context (and handles various
188 | // special cases).
189 | // This method can be overridden in plugins.
190 | zepto.init = function(selector, context) {
191 | var dom
192 | // If nothing given, return an empty Zepto collection
193 | if (!selector) return zepto.Z()
194 | // Optimize for string selectors
195 | else if (typeof selector == 'string') {
196 | selector = selector.trim()
197 | // If it's a html fragment, create nodes from it
198 | // Note: In both Chrome 21 and Firefox 15, DOM error 12
199 | // is thrown if the fragment doesn't begin with <
200 | if (selector[0] == '<' && fragmentRE.test(selector))
201 | dom = zepto.fragment(selector, RegExp.$1, context), selector = null
202 | // If there's a context, create a collection on that context first, and select
203 | // nodes from there
204 | else if (context !== undefined) return $(context).find(selector)
205 | // If it's a CSS selector, use it to select nodes.
206 | else dom = zepto.qsa(document, selector)
207 | }
208 | // If a function is given, call it when the DOM is ready
209 | else if (isFunction(selector)) return $(document).ready(selector)
210 | // If a Zepto collection is given, just return it
211 | else if (zepto.isZ(selector)) return selector
212 | else {
213 | // normalize array if an array of nodes is given
214 | if (isArray(selector)) dom = compact(selector)
215 | // Wrap DOM nodes.
216 | else if (isObject(selector))
217 | dom = [selector], selector = null
218 | // If it's a html fragment, create nodes from it
219 | else if (fragmentRE.test(selector))
220 | dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null
221 | // If there's a context, create a collection on that context first, and select
222 | // nodes from there
223 | else if (context !== undefined) return $(context).find(selector)
224 | // And last but no least, if it's a CSS selector, use it to select nodes.
225 | else dom = zepto.qsa(document, selector)
226 | }
227 | // create a new Zepto collection from the nodes found
228 | return zepto.Z(dom, selector)
229 | }
230 |
231 | // `$` will be the base `Zepto` object. When calling this
232 | // function just call `$.zepto.init, which makes the implementation
233 | // details of selecting nodes and creating Zepto collections
234 | // patchable in plugins.
235 | $ = function(selector, context){
236 | return zepto.init(selector, context)
237 | }
238 |
239 | function extend(target, source, deep) {
240 | for (key in source)
241 | if (deep && (isPlainObject(source[key]) || isArray(source[key]))) {
242 | if (isPlainObject(source[key]) && !isPlainObject(target[key]))
243 | target[key] = {}
244 | if (isArray(source[key]) && !isArray(target[key]))
245 | target[key] = []
246 | extend(target[key], source[key], deep)
247 | }
248 | else if (source[key] !== undefined) target[key] = source[key]
249 | }
250 |
251 | // Copy all but undefined properties from one or more
252 | // objects to the `target` object.
253 | $.extend = function(target){
254 | var deep, args = slice.call(arguments, 1)
255 | if (typeof target == 'boolean') {
256 | deep = target
257 | target = args.shift()
258 | }
259 | args.forEach(function(arg){ extend(target, arg, deep) })
260 | return target
261 | }
262 |
263 | // `$.zepto.qsa` is Zepto's CSS selector implementation which
264 | // uses `document.querySelectorAll` and optimizes for some special cases, like `#id`.
265 | // This method can be overridden in plugins.
266 | zepto.qsa = function(element, selector){
267 | var found,
268 | maybeID = selector[0] == '#',
269 | maybeClass = !maybeID && selector[0] == '.',
270 | nameOnly = maybeID || maybeClass ? selector.slice(1) : selector, // Ensure that a 1 char tag name still gets checked
271 | isSimple = simpleSelectorRE.test(nameOnly)
272 | return (element.getElementById && isSimple && maybeID) ? // Safari DocumentFragment doesn't have getElementById
273 | ( (found = element.getElementById(nameOnly)) ? [found] : [] ) :
274 | (element.nodeType !== 1 && element.nodeType !== 9 && element.nodeType !== 11) ? [] :
275 | slice.call(
276 | isSimple && !maybeID && element.getElementsByClassName ? // DocumentFragment doesn't have getElementsByClassName/TagName
277 | maybeClass ? element.getElementsByClassName(nameOnly) : // If it's simple, it could be a class
278 | element.getElementsByTagName(selector) : // Or a tag
279 | element.querySelectorAll(selector) // Or it's not simple, and we need to query all
280 | )
281 | }
282 |
283 | function filtered(nodes, selector) {
284 | return selector == null ? $(nodes) : $(nodes).filter(selector)
285 | }
286 |
287 | $.contains = document.documentElement.contains ?
288 | function(parent, node) {
289 | return parent !== node && parent.contains(node)
290 | } :
291 | function(parent, node) {
292 | while (node && (node = node.parentNode))
293 | if (node === parent) return true
294 | return false
295 | }
296 |
297 | function funcArg(context, arg, idx, payload) {
298 | return isFunction(arg) ? arg.call(context, idx, payload) : arg
299 | }
300 |
301 | function setAttribute(node, name, value) {
302 | value == null ? node.removeAttribute(name) : node.setAttribute(name, value)
303 | }
304 |
305 | // access className property while respecting SVGAnimatedString
306 | function className(node, value){
307 | var klass = node.className || '',
308 | svg = klass && klass.baseVal !== undefined
309 |
310 | if (value === undefined) return svg ? klass.baseVal : klass
311 | svg ? (klass.baseVal = value) : (node.className = value)
312 | }
313 |
314 | // "true" => true
315 | // "false" => false
316 | // "null" => null
317 | // "42" => 42
318 | // "42.5" => 42.5
319 | // "08" => "08"
320 | // JSON => parse if valid
321 | // String => self
322 | function deserializeValue(value) {
323 | try {
324 | return value ?
325 | value == "true" ||
326 | ( value == "false" ? false :
327 | value == "null" ? null :
328 | +value + "" == value ? +value :
329 | /^[\[\{]/.test(value) ? $.parseJSON(value) :
330 | value )
331 | : value
332 | } catch(e) {
333 | return value
334 | }
335 | }
336 |
337 | $.type = type
338 | $.isFunction = isFunction
339 | $.isWindow = isWindow
340 | $.isArray = isArray
341 | $.isPlainObject = isPlainObject
342 |
343 | $.isEmptyObject = function(obj) {
344 | var name
345 | for (name in obj) return false
346 | return true
347 | }
348 |
349 | $.isNumeric = function(val) {
350 | var num = Number(val), type = typeof val
351 | return val != null && type != 'boolean' &&
352 | (type != 'string' || val.length) &&
353 | !isNaN(num) && isFinite(num) || false
354 | }
355 |
356 | $.inArray = function(elem, array, i){
357 | return emptyArray.indexOf.call(array, elem, i)
358 | }
359 |
360 | $.camelCase = camelize
361 | $.trim = function(str) {
362 | return str == null ? "" : String.prototype.trim.call(str)
363 | }
364 |
365 | // plugin compatibility
366 | $.uuid = 0
367 | $.support = { }
368 | $.expr = { }
369 | $.noop = function() {}
370 |
371 | $.map = function(elements, callback){
372 | var value, values = [], i, key
373 | if (likeArray(elements))
374 | for (i = 0; i < elements.length; i++) {
375 | value = callback(elements[i], i)
376 | if (value != null) values.push(value)
377 | }
378 | else
379 | for (key in elements) {
380 | value = callback(elements[key], key)
381 | if (value != null) values.push(value)
382 | }
383 | return flatten(values)
384 | }
385 |
386 | $.each = function(elements, callback){
387 | var i, key
388 | if (likeArray(elements)) {
389 | for (i = 0; i < elements.length; i++)
390 | if (callback.call(elements[i], i, elements[i]) === false) return elements
391 | } else {
392 | for (key in elements)
393 | if (callback.call(elements[key], key, elements[key]) === false) return elements
394 | }
395 |
396 | return elements
397 | }
398 |
399 | $.grep = function(elements, callback){
400 | return filter.call(elements, callback)
401 | }
402 |
403 | if (window.JSON) $.parseJSON = JSON.parse
404 |
405 | // Populate the class2type map
406 | $.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
407 | class2type[ "[object " + name + "]" ] = name.toLowerCase()
408 | })
409 |
410 | // Define methods that will be available on all
411 | // Zepto collections
412 | $.fn = {
413 | constructor: zepto.Z,
414 | length: 0,
415 |
416 | // Because a collection acts like an array
417 | // copy over these useful array functions.
418 | forEach: emptyArray.forEach,
419 | reduce: emptyArray.reduce,
420 | push: emptyArray.push,
421 | sort: emptyArray.sort,
422 | splice: emptyArray.splice,
423 | indexOf: emptyArray.indexOf,
424 | concat: function(){
425 | var i, value, args = []
426 | for (i = 0; i < arguments.length; i++) {
427 | value = arguments[i]
428 | args[i] = zepto.isZ(value) ? value.toArray() : value
429 | }
430 | return concat.apply(zepto.isZ(this) ? this.toArray() : this, args)
431 | },
432 |
433 | // `map` and `slice` in the jQuery API work differently
434 | // from their array counterparts
435 | map: function(fn){
436 | return $($.map(this, function(el, i){ return fn.call(el, i, el) }))
437 | },
438 | slice: function(){
439 | return $(slice.apply(this, arguments))
440 | },
441 |
442 | ready: function(callback){
443 | // need to check if document.body exists for IE as that browser reports
444 | // document ready when it hasn't yet created the body element
445 | if (readyRE.test(document.readyState) && document.body) callback($)
446 | else document.addEventListener('DOMContentLoaded', function(){ callback($) }, false)
447 | return this
448 | },
449 | get: function(idx){
450 | return idx === undefined ? slice.call(this) : this[idx >= 0 ? idx : idx + this.length]
451 | },
452 | toArray: function(){ return this.get() },
453 | size: function(){
454 | return this.length
455 | },
456 | remove: function(){
457 | return this.each(function(){
458 | if (this.parentNode != null)
459 | this.parentNode.removeChild(this)
460 | })
461 | },
462 | each: function(callback){
463 | emptyArray.every.call(this, function(el, idx){
464 | return callback.call(el, idx, el) !== false
465 | })
466 | return this
467 | },
468 | filter: function(selector){
469 | if (isFunction(selector)) return this.not(this.not(selector))
470 | return $(filter.call(this, function(element){
471 | return zepto.matches(element, selector)
472 | }))
473 | },
474 | add: function(selector,context){
475 | return $(uniq(this.concat($(selector,context))))
476 | },
477 | is: function(selector){
478 | return this.length > 0 && zepto.matches(this[0], selector)
479 | },
480 | not: function(selector){
481 | var nodes=[]
482 | if (isFunction(selector) && selector.call !== undefined)
483 | this.each(function(idx){
484 | if (!selector.call(this,idx)) nodes.push(this)
485 | })
486 | else {
487 | var excludes = typeof selector == 'string' ? this.filter(selector) :
488 | (likeArray(selector) && isFunction(selector.item)) ? slice.call(selector) : $(selector)
489 | this.forEach(function(el){
490 | if (excludes.indexOf(el) < 0) nodes.push(el)
491 | })
492 | }
493 | return $(nodes)
494 | },
495 | has: function(selector){
496 | return this.filter(function(){
497 | return isObject(selector) ?
498 | $.contains(this, selector) :
499 | $(this).find(selector).size()
500 | })
501 | },
502 | eq: function(idx){
503 | return idx === -1 ? this.slice(idx) : this.slice(idx, + idx + 1)
504 | },
505 | first: function(){
506 | var el = this[0]
507 | return el && !isObject(el) ? el : $(el)
508 | },
509 | last: function(){
510 | var el = this[this.length - 1]
511 | return el && !isObject(el) ? el : $(el)
512 | },
513 | find: function(selector){
514 | var result, $this = this
515 | if (!selector) result = $()
516 | else if (typeof selector == 'object')
517 | result = $(selector).filter(function(){
518 | var node = this
519 | return emptyArray.some.call($this, function(parent){
520 | return $.contains(parent, node)
521 | })
522 | })
523 | else if (this.length == 1) result = $(zepto.qsa(this[0], selector))
524 | else result = this.map(function(){ return zepto.qsa(this, selector) })
525 | return result
526 | },
527 | closest: function(selector, context){
528 | var nodes = [], collection = typeof selector == 'object' && $(selector)
529 | this.each(function(_, node){
530 | while (node && !(collection ? collection.indexOf(node) >= 0 : zepto.matches(node, selector)))
531 | node = node !== context && !isDocument(node) && node.parentNode
532 | if (node && nodes.indexOf(node) < 0) nodes.push(node)
533 | })
534 | return $(nodes)
535 | },
536 | parents: function(selector){
537 | var ancestors = [], nodes = this
538 | while (nodes.length > 0)
539 | nodes = $.map(nodes, function(node){
540 | if ((node = node.parentNode) && !isDocument(node) && ancestors.indexOf(node) < 0) {
541 | ancestors.push(node)
542 | return node
543 | }
544 | })
545 | return filtered(ancestors, selector)
546 | },
547 | parent: function(selector){
548 | return filtered(uniq(this.pluck('parentNode')), selector)
549 | },
550 | children: function(selector){
551 | return filtered(this.map(function(){ return children(this) }), selector)
552 | },
553 | contents: function() {
554 | return this.map(function() { return this.contentDocument || slice.call(this.childNodes) })
555 | },
556 | siblings: function(selector){
557 | return filtered(this.map(function(i, el){
558 | return filter.call(children(el.parentNode), function(child){ return child!==el })
559 | }), selector)
560 | },
561 | empty: function(){
562 | return this.each(function(){ this.innerHTML = '' })
563 | },
564 | // `pluck` is borrowed from Prototype.js
565 | pluck: function(property){
566 | return $.map(this, function(el){ return el[property] })
567 | },
568 | show: function(){
569 | return this.each(function(){
570 | this.style.display == "none" && (this.style.display = '')
571 | if (getComputedStyle(this, '').getPropertyValue("display") == "none")
572 | this.style.display = defaultDisplay(this.nodeName)
573 | })
574 | },
575 | replaceWith: function(newContent){
576 | return this.before(newContent).remove()
577 | },
578 | wrap: function(structure){
579 | var func = isFunction(structure)
580 | if (this[0] && !func)
581 | var dom = $(structure).get(0),
582 | clone = dom.parentNode || this.length > 1
583 |
584 | return this.each(function(index){
585 | $(this).wrapAll(
586 | func ? structure.call(this, index) :
587 | clone ? dom.cloneNode(true) : dom
588 | )
589 | })
590 | },
591 | wrapAll: function(structure){
592 | if (this[0]) {
593 | $(this[0]).before(structure = $(structure))
594 | var children
595 | // drill down to the inmost element
596 | while ((children = structure.children()).length) structure = children.first()
597 | $(structure).append(this)
598 | }
599 | return this
600 | },
601 | wrapInner: function(structure){
602 | var func = isFunction(structure)
603 | return this.each(function(index){
604 | var self = $(this), contents = self.contents(),
605 | dom = func ? structure.call(this, index) : structure
606 | contents.length ? contents.wrapAll(dom) : self.append(dom)
607 | })
608 | },
609 | unwrap: function(){
610 | this.parent().each(function(){
611 | $(this).replaceWith($(this).children())
612 | })
613 | return this
614 | },
615 | clone: function(){
616 | return this.map(function(){ return this.cloneNode(true) })
617 | },
618 | hide: function(){
619 | return this.css("display", "none")
620 | },
621 | toggle: function(setting){
622 | return this.each(function(){
623 | var el = $(this)
624 | ;(setting === undefined ? el.css("display") == "none" : setting) ? el.show() : el.hide()
625 | })
626 | },
627 | prev: function(selector){ return $(this.pluck('previousElementSibling')).filter(selector || '*') },
628 | next: function(selector){ return $(this.pluck('nextElementSibling')).filter(selector || '*') },
629 | html: function(html){
630 | return 0 in arguments ?
631 | this.each(function(idx){
632 | var originHtml = this.innerHTML
633 | $(this).empty().append( funcArg(this, html, idx, originHtml) )
634 | }) :
635 | (0 in this ? this[0].innerHTML : null)
636 | },
637 | text: function(text){
638 | return 0 in arguments ?
639 | this.each(function(idx){
640 | var newText = funcArg(this, text, idx, this.textContent)
641 | this.textContent = newText == null ? '' : ''+newText
642 | }) :
643 | (0 in this ? this.pluck('textContent').join("") : null)
644 | },
645 | attr: function(name, value){
646 | var result
647 | return (typeof name == 'string' && !(1 in arguments)) ?
648 | (0 in this && this[0].nodeType == 1 && (result = this[0].getAttribute(name)) != null ? result : undefined) :
649 | this.each(function(idx){
650 | if (this.nodeType !== 1) return
651 | if (isObject(name)) for (key in name) setAttribute(this, key, name[key])
652 | else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name)))
653 | })
654 | },
655 | removeAttr: function(name){
656 | return this.each(function(){ this.nodeType === 1 && name.split(' ').forEach(function(attribute){
657 | setAttribute(this, attribute)
658 | }, this)})
659 | },
660 | prop: function(name, value){
661 | name = propMap[name] || name
662 | return (1 in arguments) ?
663 | this.each(function(idx){
664 | this[name] = funcArg(this, value, idx, this[name])
665 | }) :
666 | (this[0] && this[0][name])
667 | },
668 | removeProp: function(name){
669 | name = propMap[name] || name
670 | return this.each(function(){ delete this[name] })
671 | },
672 | data: function(name, value){
673 | var attrName = 'data-' + name.replace(capitalRE, '-$1').toLowerCase()
674 |
675 | var data = (1 in arguments) ?
676 | this.attr(attrName, value) :
677 | this.attr(attrName)
678 |
679 | return data !== null ? deserializeValue(data) : undefined
680 | },
681 | val: function(value){
682 | if (0 in arguments) {
683 | if (value == null) value = ""
684 | return this.each(function(idx){
685 | this.value = funcArg(this, value, idx, this.value)
686 | })
687 | } else {
688 | return this[0] && (this[0].multiple ?
689 | $(this[0]).find('option').filter(function(){ return this.selected }).pluck('value') :
690 | this[0].value)
691 | }
692 | },
693 | offset: function(coordinates){
694 | if (coordinates) return this.each(function(index){
695 | var $this = $(this),
696 | coords = funcArg(this, coordinates, index, $this.offset()),
697 | parentOffset = $this.offsetParent().offset(),
698 | props = {
699 | top: coords.top - parentOffset.top,
700 | left: coords.left - parentOffset.left
701 | }
702 |
703 | if ($this.css('position') == 'static') props['position'] = 'relative'
704 | $this.css(props)
705 | })
706 | if (!this.length) return null
707 | if (document.documentElement !== this[0] && !$.contains(document.documentElement, this[0]))
708 | return {top: 0, left: 0}
709 | var obj = this[0].getBoundingClientRect()
710 | return {
711 | left: obj.left + window.pageXOffset,
712 | top: obj.top + window.pageYOffset,
713 | width: Math.round(obj.width),
714 | height: Math.round(obj.height)
715 | }
716 | },
717 | css: function(property, value){
718 | if (arguments.length < 2) {
719 | var element = this[0]
720 | if (typeof property == 'string') {
721 | if (!element) return
722 | return element.style[camelize(property)] || getComputedStyle(element, '').getPropertyValue(property)
723 | } else if (isArray(property)) {
724 | if (!element) return
725 | var props = {}
726 | var computedStyle = getComputedStyle(element, '')
727 | $.each(property, function(_, prop){
728 | props[prop] = (element.style[camelize(prop)] || computedStyle.getPropertyValue(prop))
729 | })
730 | return props
731 | }
732 | }
733 |
734 | var css = ''
735 | if (type(property) == 'string') {
736 | if (!value && value !== 0)
737 | this.each(function(){ this.style.removeProperty(dasherize(property)) })
738 | else
739 | css = dasherize(property) + ":" + maybeAddPx(property, value)
740 | } else {
741 | for (key in property)
742 | if (!property[key] && property[key] !== 0)
743 | this.each(function(){ this.style.removeProperty(dasherize(key)) })
744 | else
745 | css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';'
746 | }
747 |
748 | return this.each(function(){ this.style.cssText += ';' + css })
749 | },
750 | index: function(element){
751 | return element ? this.indexOf($(element)[0]) : this.parent().children().indexOf(this[0])
752 | },
753 | hasClass: function(name){
754 | if (!name) return false
755 | return emptyArray.some.call(this, function(el){
756 | return this.test(className(el))
757 | }, classRE(name))
758 | },
759 | addClass: function(name){
760 | if (!name) return this
761 | return this.each(function(idx){
762 | if (!('className' in this)) return
763 | classList = []
764 | var cls = className(this), newName = funcArg(this, name, idx, cls)
765 | newName.split(/\s+/g).forEach(function(klass){
766 | if (!$(this).hasClass(klass)) classList.push(klass)
767 | }, this)
768 | classList.length && className(this, cls + (cls ? " " : "") + classList.join(" "))
769 | })
770 | },
771 | removeClass: function(name){
772 | return this.each(function(idx){
773 | if (!('className' in this)) return
774 | if (name === undefined) return className(this, '')
775 | classList = className(this)
776 | funcArg(this, name, idx, classList).split(/\s+/g).forEach(function(klass){
777 | classList = classList.replace(classRE(klass), " ")
778 | })
779 | className(this, classList.trim())
780 | })
781 | },
782 | toggleClass: function(name, when){
783 | if (!name) return this
784 | return this.each(function(idx){
785 | var $this = $(this), names = funcArg(this, name, idx, className(this))
786 | names.split(/\s+/g).forEach(function(klass){
787 | (when === undefined ? !$this.hasClass(klass) : when) ?
788 | $this.addClass(klass) : $this.removeClass(klass)
789 | })
790 | })
791 | },
792 | scrollTop: function(value){
793 | if (!this.length) return
794 | var hasScrollTop = 'scrollTop' in this[0]
795 | if (value === undefined) return hasScrollTop ? this[0].scrollTop : this[0].pageYOffset
796 | return this.each(hasScrollTop ?
797 | function(){ this.scrollTop = value } :
798 | function(){ this.scrollTo(this.scrollX, value) })
799 | },
800 | scrollLeft: function(value){
801 | if (!this.length) return
802 | var hasScrollLeft = 'scrollLeft' in this[0]
803 | if (value === undefined) return hasScrollLeft ? this[0].scrollLeft : this[0].pageXOffset
804 | return this.each(hasScrollLeft ?
805 | function(){ this.scrollLeft = value } :
806 | function(){ this.scrollTo(value, this.scrollY) })
807 | },
808 | position: function() {
809 | if (!this.length) return
810 |
811 | var elem = this[0],
812 | // Get *real* offsetParent
813 | offsetParent = this.offsetParent(),
814 | // Get correct offsets
815 | offset = this.offset(),
816 | parentOffset = rootNodeRE.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset()
817 |
818 | // Subtract element margins
819 | // note: when an element has margin: auto the offsetLeft and marginLeft
820 | // are the same in Safari causing offset.left to incorrectly be 0
821 | offset.top -= parseFloat( $(elem).css('margin-top') ) || 0
822 | offset.left -= parseFloat( $(elem).css('margin-left') ) || 0
823 |
824 | // Add offsetParent borders
825 | parentOffset.top += parseFloat( $(offsetParent[0]).css('border-top-width') ) || 0
826 | parentOffset.left += parseFloat( $(offsetParent[0]).css('border-left-width') ) || 0
827 |
828 | // Subtract the two offsets
829 | return {
830 | top: offset.top - parentOffset.top,
831 | left: offset.left - parentOffset.left
832 | }
833 | },
834 | offsetParent: function() {
835 | return this.map(function(){
836 | var parent = this.offsetParent || document.body
837 | while (parent && !rootNodeRE.test(parent.nodeName) && $(parent).css("position") == "static")
838 | parent = parent.offsetParent
839 | return parent
840 | })
841 | }
842 | }
843 |
844 | // for now
845 | $.fn.detach = $.fn.remove
846 |
847 | // Generate the `width` and `height` functions
848 | ;['width', 'height'].forEach(function(dimension){
849 | var dimensionProperty =
850 | dimension.replace(/./, function(m){ return m[0].toUpperCase() })
851 |
852 | $.fn[dimension] = function(value){
853 | var offset, el = this[0]
854 | if (value === undefined) return isWindow(el) ? el['inner' + dimensionProperty] :
855 | isDocument(el) ? el.documentElement['scroll' + dimensionProperty] :
856 | (offset = this.offset()) && offset[dimension]
857 | else return this.each(function(idx){
858 | el = $(this)
859 | el.css(dimension, funcArg(this, value, idx, el[dimension]()))
860 | })
861 | }
862 | })
863 |
864 | function traverseNode(node, fun) {
865 | fun(node)
866 | for (var i = 0, len = node.childNodes.length; i < len; i++)
867 | traverseNode(node.childNodes[i], fun)
868 | }
869 |
870 | // Generate the `after`, `prepend`, `before`, `append`,
871 | // `insertAfter`, `insertBefore`, `appendTo`, and `prependTo` methods.
872 | adjacencyOperators.forEach(function(operator, operatorIndex) {
873 | var inside = operatorIndex % 2 //=> prepend, append
874 |
875 | $.fn[operator] = function(){
876 | // arguments can be nodes, arrays of nodes, Zepto objects and HTML strings
877 | var argType, nodes = $.map(arguments, function(arg) {
878 | var arr = []
879 | argType = type(arg)
880 | if (argType == "array") {
881 | arg.forEach(function(el) {
882 | if (el.nodeType !== undefined) return arr.push(el)
883 | else if ($.zepto.isZ(el)) return arr = arr.concat(el.get())
884 | arr = arr.concat(zepto.fragment(el))
885 | })
886 | return arr
887 | }
888 | return argType == "object" || arg == null ?
889 | arg : zepto.fragment(arg)
890 | }),
891 | parent, copyByClone = this.length > 1
892 | if (nodes.length < 1) return this
893 |
894 | return this.each(function(_, target){
895 | parent = inside ? target : target.parentNode
896 |
897 | // convert all methods to a "before" operation
898 | target = operatorIndex == 0 ? target.nextSibling :
899 | operatorIndex == 1 ? target.firstChild :
900 | operatorIndex == 2 ? target :
901 | null
902 |
903 | var parentInDocument = $.contains(document.documentElement, parent)
904 |
905 | nodes.forEach(function(node){
906 | if (copyByClone) node = node.cloneNode(true)
907 | else if (!parent) return $(node).remove()
908 |
909 | parent.insertBefore(node, target)
910 | if (parentInDocument) traverseNode(node, function(el){
911 | if (el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT' &&
912 | (!el.type || el.type === 'text/javascript') && !el.src){
913 | var target = el.ownerDocument ? el.ownerDocument.defaultView : window
914 | target['eval'].call(target, el.innerHTML)
915 | }
916 | })
917 | })
918 | })
919 | }
920 |
921 | // after => insertAfter
922 | // prepend => prependTo
923 | // before => insertBefore
924 | // append => appendTo
925 | $.fn[inside ? operator+'To' : 'insert'+(operatorIndex ? 'Before' : 'After')] = function(html){
926 | $(html)[operator](this)
927 | return this
928 | }
929 | })
930 |
931 | zepto.Z.prototype = Z.prototype = $.fn
932 |
933 | // Export internal API functions in the `$.zepto` namespace
934 | zepto.uniq = uniq
935 | zepto.deserializeValue = deserializeValue
936 | $.zepto = zepto
937 |
938 | return $
939 | })()
940 |
941 | window.Zepto = Zepto
942 | window.$ === undefined && (window.$ = Zepto)
943 |
944 | ;(function($){
945 | var _zid = 1, undefined,
946 | slice = Array.prototype.slice,
947 | isFunction = $.isFunction,
948 | isString = function(obj){ return typeof obj == 'string' },
949 | handlers = {},
950 | specialEvents={},
951 | focusinSupported = 'onfocusin' in window,
952 | focus = { focus: 'focusin', blur: 'focusout' },
953 | hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' }
954 |
955 | specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents'
956 |
957 | function zid(element) {
958 | return element._zid || (element._zid = _zid++)
959 | }
960 | function findHandlers(element, event, fn, selector) {
961 | event = parse(event)
962 | if (event.ns) var matcher = matcherFor(event.ns)
963 | return (handlers[zid(element)] || []).filter(function(handler) {
964 | return handler
965 | && (!event.e || handler.e == event.e)
966 | && (!event.ns || matcher.test(handler.ns))
967 | && (!fn || zid(handler.fn) === zid(fn))
968 | && (!selector || handler.sel == selector)
969 | })
970 | }
971 | function parse(event) {
972 | var parts = ('' + event).split('.')
973 | return {e: parts[0], ns: parts.slice(1).sort().join(' ')}
974 | }
975 | function matcherFor(ns) {
976 | return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)')
977 | }
978 |
979 | function eventCapture(handler, captureSetting) {
980 | return handler.del &&
981 | (!focusinSupported && (handler.e in focus)) ||
982 | !!captureSetting
983 | }
984 |
985 | function realEvent(type) {
986 | return hover[type] || (focusinSupported && focus[type]) || type
987 | }
988 |
989 | function add(element, events, fn, data, selector, delegator, capture){
990 | var id = zid(element), set = (handlers[id] || (handlers[id] = []))
991 | events.split(/\s/).forEach(function(event){
992 | if (event == 'ready') return $(document).ready(fn)
993 | var handler = parse(event)
994 | handler.fn = fn
995 | handler.sel = selector
996 | // emulate mouseenter, mouseleave
997 | if (handler.e in hover) fn = function(e){
998 | var related = e.relatedTarget
999 | if (!related || (related !== this && !$.contains(this, related)))
1000 | return handler.fn.apply(this, arguments)
1001 | }
1002 | handler.del = delegator
1003 | var callback = delegator || fn
1004 | handler.proxy = function(e){
1005 | e = compatible(e)
1006 | if (e.isImmediatePropagationStopped()) return
1007 | e.data = data
1008 | var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args))
1009 | if (result === false) e.preventDefault(), e.stopPropagation()
1010 | return result
1011 | }
1012 | handler.i = set.length
1013 | set.push(handler)
1014 | if ('addEventListener' in element)
1015 | element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
1016 | })
1017 | }
1018 | function remove(element, events, fn, selector, capture){
1019 | var id = zid(element)
1020 | ;(events || '').split(/\s/).forEach(function(event){
1021 | findHandlers(element, event, fn, selector).forEach(function(handler){
1022 | delete handlers[id][handler.i]
1023 | if ('removeEventListener' in element)
1024 | element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
1025 | })
1026 | })
1027 | }
1028 |
1029 | $.event = { add: add, remove: remove }
1030 |
1031 | $.proxy = function(fn, context) {
1032 | var args = (2 in arguments) && slice.call(arguments, 2)
1033 | if (isFunction(fn)) {
1034 | var proxyFn = function(){ return fn.apply(context, args ? args.concat(slice.call(arguments)) : arguments) }
1035 | proxyFn._zid = zid(fn)
1036 | return proxyFn
1037 | } else if (isString(context)) {
1038 | if (args) {
1039 | args.unshift(fn[context], fn)
1040 | return $.proxy.apply(null, args)
1041 | } else {
1042 | return $.proxy(fn[context], fn)
1043 | }
1044 | } else {
1045 | throw new TypeError("expected function")
1046 | }
1047 | }
1048 |
1049 | $.fn.bind = function(event, data, callback){
1050 | return this.on(event, data, callback)
1051 | }
1052 | $.fn.unbind = function(event, callback){
1053 | return this.off(event, callback)
1054 | }
1055 | $.fn.one = function(event, selector, data, callback){
1056 | return this.on(event, selector, data, callback, 1)
1057 | }
1058 |
1059 | var returnTrue = function(){return true},
1060 | returnFalse = function(){return false},
1061 | ignoreProperties = /^([A-Z]|returnValue$|layer[XY]$|webkitMovement[XY]$)/,
1062 | eventMethods = {
1063 | preventDefault: 'isDefaultPrevented',
1064 | stopImmediatePropagation: 'isImmediatePropagationStopped',
1065 | stopPropagation: 'isPropagationStopped'
1066 | }
1067 |
1068 | function compatible(event, source) {
1069 | if (source || !event.isDefaultPrevented) {
1070 | source || (source = event)
1071 |
1072 | $.each(eventMethods, function(name, predicate) {
1073 | var sourceMethod = source[name]
1074 | event[name] = function(){
1075 | this[predicate] = returnTrue
1076 | return sourceMethod && sourceMethod.apply(source, arguments)
1077 | }
1078 | event[predicate] = returnFalse
1079 | })
1080 |
1081 | event.timeStamp || (event.timeStamp = Date.now())
1082 |
1083 | if (source.defaultPrevented !== undefined ? source.defaultPrevented :
1084 | 'returnValue' in source ? source.returnValue === false :
1085 | source.getPreventDefault && source.getPreventDefault())
1086 | event.isDefaultPrevented = returnTrue
1087 | }
1088 | return event
1089 | }
1090 |
1091 | function createProxy(event) {
1092 | var key, proxy = { originalEvent: event }
1093 | for (key in event)
1094 | if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key]
1095 |
1096 | return compatible(proxy, event)
1097 | }
1098 |
1099 | $.fn.delegate = function(selector, event, callback){
1100 | return this.on(event, selector, callback)
1101 | }
1102 | $.fn.undelegate = function(selector, event, callback){
1103 | return this.off(event, selector, callback)
1104 | }
1105 |
1106 | $.fn.live = function(event, callback){
1107 | $(document.body).delegate(this.selector, event, callback)
1108 | return this
1109 | }
1110 | $.fn.die = function(event, callback){
1111 | $(document.body).undelegate(this.selector, event, callback)
1112 | return this
1113 | }
1114 |
1115 | $.fn.on = function(event, selector, data, callback, one){
1116 | var autoRemove, delegator, $this = this
1117 | if (event && !isString(event)) {
1118 | $.each(event, function(type, fn){
1119 | $this.on(type, selector, data, fn, one)
1120 | })
1121 | return $this
1122 | }
1123 |
1124 | if (!isString(selector) && !isFunction(callback) && callback !== false)
1125 | callback = data, data = selector, selector = undefined
1126 | if (callback === undefined || data === false)
1127 | callback = data, data = undefined
1128 |
1129 | if (callback === false) callback = returnFalse
1130 |
1131 | return $this.each(function(_, element){
1132 | if (one) autoRemove = function(e){
1133 | remove(element, e.type, callback)
1134 | return callback.apply(this, arguments)
1135 | }
1136 |
1137 | if (selector) delegator = function(e){
1138 | var evt, match = $(e.target).closest(selector, element).get(0)
1139 | if (match && match !== element) {
1140 | evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element})
1141 | return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1)))
1142 | }
1143 | }
1144 |
1145 | add(element, event, callback, data, selector, delegator || autoRemove)
1146 | })
1147 | }
1148 | $.fn.off = function(event, selector, callback){
1149 | var $this = this
1150 | if (event && !isString(event)) {
1151 | $.each(event, function(type, fn){
1152 | $this.off(type, selector, fn)
1153 | })
1154 | return $this
1155 | }
1156 |
1157 | if (!isString(selector) && !isFunction(callback) && callback !== false)
1158 | callback = selector, selector = undefined
1159 |
1160 | if (callback === false) callback = returnFalse
1161 |
1162 | return $this.each(function(){
1163 | remove(this, event, callback, selector)
1164 | })
1165 | }
1166 |
1167 | $.fn.trigger = function(event, args){
1168 | event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event)
1169 | event._args = args
1170 | return this.each(function(){
1171 | // handle focus(), blur() by calling them directly
1172 | if (event.type in focus && typeof this[event.type] == "function") this[event.type]()
1173 | // items in the collection might not be DOM elements
1174 | else if ('dispatchEvent' in this) this.dispatchEvent(event)
1175 | else $(this).triggerHandler(event, args)
1176 | })
1177 | }
1178 |
1179 | // triggers event handlers on current element just as if an event occurred,
1180 | // doesn't trigger an actual event, doesn't bubble
1181 | $.fn.triggerHandler = function(event, args){
1182 | var e, result
1183 | this.each(function(i, element){
1184 | e = createProxy(isString(event) ? $.Event(event) : event)
1185 | e._args = args
1186 | e.target = element
1187 | $.each(findHandlers(element, event.type || event), function(i, handler){
1188 | result = handler.proxy(e)
1189 | if (e.isImmediatePropagationStopped()) return false
1190 | })
1191 | })
1192 | return result
1193 | }
1194 |
1195 | // shortcut methods for `.bind(event, fn)` for each event type
1196 | ;('focusin focusout focus blur load resize scroll unload click dblclick '+
1197 | 'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave '+
1198 | 'change select keydown keypress keyup error').split(' ').forEach(function(event) {
1199 | $.fn[event] = function(callback) {
1200 | return (0 in arguments) ?
1201 | this.bind(event, callback) :
1202 | this.trigger(event)
1203 | }
1204 | })
1205 |
1206 | $.Event = function(type, props) {
1207 | if (!isString(type)) props = type, type = props.type
1208 | var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true
1209 | if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name])
1210 | event.initEvent(type, bubbles, true)
1211 | return compatible(event)
1212 | }
1213 |
1214 | })(Zepto)
1215 |
1216 | ;(function($){
1217 | var jsonpID = +new Date(),
1218 | document = window.document,
1219 | key,
1220 | name,
1221 | rscript = /