├── .gitignore
├── .npmignore
├── Gruntfile.coffee
├── LICENSE.md
├── README.md
├── assets
└── scheql.svg
├── bin
└── sheql
├── coffeelint.json
├── index.js
├── package.json
├── src
├── executor.coffee
├── getDays.coffee
├── getMonths.coffee
├── getWeeks.coffee
├── getYears.coffee
├── indexFilter.coffee
├── lexer.coffee
├── propFilter.coffee
└── utils.coffee
└── test
├── mocha.opts
├── test.dateUtils.js
├── test.executor.coffee
├── test.getDays.coffee
├── test.getMonths.coffee
├── test.getWeeks.coffee
├── test.getYears.coffee
├── test.indexFilter.coffee
├── test.lexer.coffee
└── test.propFilter.coffee
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # Compiled binary addons (http://nodejs.org/api/addons.html)
20 | build/Release
21 |
22 | # Dependency directory
23 | # Deployed apps should consider commenting this line out:
24 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
25 | node_modules
26 | public/bower_components
27 | lib/
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | test/
2 | src/
3 | assets/
4 | *.coffee
5 | *.json
6 | *.md
--------------------------------------------------------------------------------
/Gruntfile.coffee:
--------------------------------------------------------------------------------
1 | matchdep = require 'matchdep'
2 | module.exports = (grunt) ->
3 | matchdep.filterDev('grunt-*').forEach grunt.loadNpmTasks
4 |
5 | grunt.initConfig
6 | pkg: grunt.file.readJSON 'package.json'
7 | release: {}
8 | coffee:compile:
9 | expand: true
10 | flatten: true
11 | src: ['./src/*']
12 | dest: './lib/'
13 | ext: '.js'
14 | browserify:
15 | compile:
16 | files: './lib/sheql.js': ['./src/executor']
17 | options: transform: ['coffeeify']
18 | grunt.registerTask 'publish', ['coffee', 'release']
19 |
20 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 SHEQL
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Schedule Query Langauge
2 | 
3 |
4 |
5 | SHEQL is a schema less solution to the problem of storing repeated events in a calendar. It is inspired by CSS selectors.
6 |
7 | ## Features
8 |
9 | 1. A Far More powerful and customizable logic for repetition can be written.
10 | 2. A Schemaless Architecture.
11 | 3. A single change is required to update repeated events.
12 | 4. Platform independent.
13 |
14 | [Learn more about the syntax](#examples).
15 |
16 |
17 | ## Elements
18 | They are analogous to tag names in HTML.
19 |
20 | 1. `y` - Representing years
21 |
22 | 2. `m` - Representing months
23 |
24 | 3. `w` - Representing weeks
25 |
26 | 4. `d` - Representing days
27 |
28 | ## Element Properties
29 | Properties are like css classes applied on tags. Each tag has its own set of classes (properties as we call them here).
30 |
31 | ### Years
32 |
33 | 1. `365d`, `366d` - Filters all the years in the given range which have 365 days or 366 days (AKA leap year)
34 |
35 | 2. `52w` - Filters all the years based on the total number of weeks that fall in that year. First day of the week is Sunday be default.
36 |
37 | 3. `2015` - Filter based on the year value.
38 |
39 | ### Months
40 |
41 | 1. `31d`, `30d`, `29d`, `28d` - Filters all the the months based on the total days that are in that month.
42 |
43 | 2. `4w`, `5w` - Filter out months based on the number of weeks in it.
44 |
45 | 3. `jan` ... `dec` - Filtering based on the month name.
46 |
47 | ### Weeks
48 | 1. `7d`, `4d` - Filter outs weeks based on the total number of days in it.
49 |
50 | ### Days
51 |
52 | 1. `21` - Filter based on the date value.
53 |
54 | 2. `sat`, `sun`, ... `fri` - Filter days on the day of the week.
55 |
56 | ## Filters
57 |
58 | Filters can be applied on any type of element. They can use the element's properties also.
59 |
60 | ### Dot notation
61 | Use it to filter out elements that have the specified property.
62 |
63 | ```css
64 | y.365d /*Selects non-leap years*/
65 | m.jan /*Select the month January*/
66 | ```
67 |
68 | ### exclamation notation
69 | Use it to filter out elements that do NOT have the specified property.
70 | ```css
71 | m!jan /*all months except january*/
72 | ```
73 |
74 | ### nth element
75 | Use it to filter out the nth element
76 | ```css
77 | d:n[x+3] /*All days except the first 2*/
78 | d:n[3] /*3 day only*/
79 | d:n[2x] /*All alternate days*/
80 | ```
81 |
82 | ### lth element
83 | Use it to filter out the nth element for the last element.
84 | ```css
85 | d:l[x+3] /*All days except the last 2*/
86 | ```
87 |
88 | # Examples
89 |
90 |
91 | **Yearly repeated on the 45th day**
92 |
93 | ```css
94 | y d:n[45]
95 | ```
96 |
97 | **Monthly 1st sat**
98 |
99 | ```css
100 | m d.sat:n[1]
101 | ```
102 |
103 | **Monthly 1st day if it's a sat**
104 |
105 | ```css
106 | m d:n[1].sat
107 | ```
108 |
109 | **monthly last sat**
110 |
111 | ```css
112 | m d.sat:l[1]
113 | ```
114 |
115 | **monthly second last sat**
116 |
117 | ```css
118 | m d.sat:l[2]
119 | ```
120 |
121 | **monthly all sat**
122 |
123 | ```css
124 | m d.sat
125 | ```
126 |
127 | **every 3rd months 2nd sat**
128 |
129 | ```css
130 | m:n[3x] d.sat:n[2]
131 | ```
132 |
133 | **every 1st of every month**
134 |
135 | ```css
136 | m d:n[1]
137 | ```
138 |
139 | **Every alternate month second week, first mondays**
140 |
141 | ```css
142 | m:n[2x] w:n[2] d.mon
143 | ```
144 |
145 | **Every alternate month second monday**
146 |
147 | ```css
148 | m:n[2x] d.mon:n[2]
149 | ```
150 |
151 | **100th day of each year**
152 |
153 | ```css
154 | y d:n[100]
155 | ```
156 |
157 | **14th Feb every yr**
158 |
159 | ```css
160 | y m:n[1] d:n[14]
161 | y m.feb d:n[14]
162 | y m.feb d.14
163 | ```
164 |
165 | **Every 20th day**
166 |
167 | ```css
168 | d:n[20x]
169 | ```
170 |
171 |
172 | **every mar-dec weekdays**
173 |
174 | ```css
175 | y m:n[-x+3] d:n[x+1]:n[-x+1]
176 | ```
177 |
178 | **every day in jan except fridays**
179 |
180 | ```css
181 | m.jan d!fri
182 | ```
183 |
184 | **every alternate month's first and third saturday**
185 |
186 | ```css
187 | m:n[2x] d.sat:l[x+2]:n[2x+1]
188 | ```
189 |
190 | ## How to use from cli
191 |
192 | 1. run `npm install sheql -g`.
193 | 2. Example - __get all the tuesdays of the year, except if they fall on the last day of the month__
194 | ```
195 | sheql 'm.sep d:l[x+2].tue'
196 | ```
197 | 2. To use it as a package dependency, install it locally and use `require 'sheql'`.
198 |
199 |
200 | ## Using as a dependency
201 |
202 | ```js
203 | var sheql = require('sheql');
204 | var startDate = new Date(2010, 1,10);
205 | var endDate = new Date(2110, 4,15);
206 | var startDayOfWeek = 1; //Monday
207 | sheql.getDates('m.sep d:l[x+2].tue', startDate, endDate, startDayOfWeek);
208 | ```
209 |
210 |
--------------------------------------------------------------------------------
/assets/scheql.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
167 |
--------------------------------------------------------------------------------
/bin/sheql:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var dateCollection, endDate, endYear, i, nomnom, opts, sh, startDate, startYear, toDateCallback, _i, _len;
4 |
5 | sh = require('../lib/executor')();
6 | nomnom = require('nomnom');
7 |
8 | startYear = new Date()
9 | .getFullYear();
10 |
11 | endYear = new Date()
12 | .getFullYear();
13 |
14 | startDate = new Date(startYear, 0, 1);
15 |
16 | endDate = new Date(endYear, 11, 31);
17 |
18 | toDateCallback = function (str) {
19 | return new Date(str);
20 | };
21 |
22 | opts = nomnom.option('startDate', {
23 | "default": startDate,
24 | transform: toDateCallback,
25 | help: "Eg: 2010-01-23 (YYYY-MM-DD).Default:"
26 | })
27 | .option('endDate', {
28 | "default": endDate,
29 | transform: toDateCallback,
30 | help: "Eg: 2010-01-23 (YYYY-MM-DD). Default:"
31 | })
32 | .option('startOfWeek', {
33 | "default": 0,
34 | help: "Eg: 0 for sun, 1 for mon etc. Default:"
35 | })
36 | .parse();
37 |
38 | if (opts._.length === 0) {
39 | return console.log("Eg: $ sheql \"y.53w m:n[2x] d.sat\" ");
40 | }
41 |
42 | dateCollection = sh.executor(opts._[0], opts.startDate, opts.endDate, opts.startOfWeek);
43 |
44 | for (_i = 0, _len = dateCollection.length; _i < _len; _i++) {
45 | i = dateCollection[_i];
46 | console.log(i.toDateString());
47 | }
48 |
49 | console.log("\nTotal:\t" + dateCollection.length + " dates");
50 |
51 | console.log("From:\t" + (opts.startDate.toDateString()));
52 |
53 | console.log("To:\t" + (opts.endDate.toDateString()));
54 |
55 | console.log("Week:\tSunday");
--------------------------------------------------------------------------------
/coffeelint.json:
--------------------------------------------------------------------------------
1 | {
2 | "arrow_spacing": {
3 | "level": "ignore"
4 | },
5 | "camel_case_classes": {
6 | "level": "error"
7 | },
8 | "coffeescript_error": {
9 | "level": "error"
10 | },
11 | "colon_assignment_spacing": {
12 | "level": "ignore",
13 | "spacing": {
14 | "left": 0,
15 | "right": 0
16 | }
17 | },
18 | "cyclomatic_complexity": {
19 | "value": 10,
20 | "level": "ignore"
21 | },
22 | "duplicate_key": {
23 | "level": "error"
24 | },
25 | "empty_constructor_needs_parens": {
26 | "level": "ignore"
27 | },
28 | "indentation": {
29 | "value": 4,
30 | "level": "error"
31 | },
32 | "line_endings": {
33 | "level": "ignore",
34 | "value": "unix"
35 | },
36 | "max_line_length": {
37 | "value": 200,
38 | "level": "error",
39 | "limitComments": true
40 | },
41 | "missing_fat_arrows": {
42 | "level": "ignore"
43 | },
44 | "newlines_after_classes": {
45 | "value": 3,
46 | "level": "ignore"
47 | },
48 | "no_backticks": {
49 | "level": "error"
50 | },
51 | "no_debugger": {
52 | "level": "warn"
53 | },
54 | "no_empty_functions": {
55 | "level": "ignore"
56 | },
57 | "no_empty_param_list": {
58 | "level": "ignore"
59 | },
60 | "no_implicit_braces": {
61 | "level": "ignore",
62 | "strict": true
63 | },
64 | "no_implicit_parens": {
65 | "strict": true,
66 | "level": "ignore"
67 | },
68 | "no_interpolation_in_single_quotes": {
69 | "level": "ignore"
70 | },
71 | "no_plusplus": {
72 | "level": "ignore"
73 | },
74 | "no_stand_alone_at": {
75 | "level": "ignore"
76 | },
77 | "no_tabs": {
78 | "level": "error"
79 | },
80 | "no_throwing_strings": {
81 | "level": "error"
82 | },
83 | "no_trailing_semicolons": {
84 | "level": "error"
85 | },
86 | "no_trailing_whitespace": {
87 | "level": "error",
88 | "allowed_in_comments": false,
89 | "allowed_in_empty_lines": false
90 | },
91 | "no_unnecessary_double_quotes": {
92 | "level": "ignore"
93 | },
94 | "no_unnecessary_fat_arrows": {
95 | "level": "warn"
96 | },
97 | "non_empty_constructor_needs_parens": {
98 | "level": "ignore"
99 | },
100 | "space_operators": {
101 | "level": "ignore"
102 | }
103 | }
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var exe = require('./lib/executor')();
2 | module.exports.getDates = function (str, startDate, endDate, startOfWeek) {
3 | return exe.executor(str, startDate, endDate, startOfWeek);
4 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sheql",
3 | "version": "0.0.29",
4 | "private": false,
5 | "devDependencies": {
6 | "chai": "^1.9.1",
7 | "coffeeify": "^0.6.0",
8 | "grunt": "^0.4.5",
9 | "grunt-browserify": "^2.1.2",
10 | "grunt-contrib-coffee": "^0.10.1",
11 | "grunt-release": "^0.7.0",
12 | "matchdep": "^0.3.0",
13 | "mocha": "^1.20.1",
14 | "nomnom": "^1.8.0",
15 | "should": "^4.0.4"
16 | },
17 | "dependencies": {
18 | "nomnom": "^1.8.0"
19 | },
20 | "description": "An elegant solution for selecting dates",
21 | "main": "index.js",
22 | "directories": {
23 | "test": "tests"
24 | },
25 | "bin": {
26 | "sheql": "./bin/sheql"
27 | },
28 | "scripts": {
29 | "test": "mocha test/test.*.js"
30 | },
31 | "repository": {
32 | "type": "git",
33 | "url": "git://github.com/tusharmath/sheql.git"
34 | },
35 | "author": "Tushar Mathur (http://tusharm.com/)",
36 | "license": "ISC",
37 | "bugs": {
38 | "url": "https://github.com/tusharmath/sheql/issues"
39 | },
40 | "preferGlobal": true,
41 | "homepage": "https://github.com/tusharmath/sheql"
42 | }
43 |
--------------------------------------------------------------------------------
/src/executor.coffee:
--------------------------------------------------------------------------------
1 | lexer = require('./lexer')()
2 | propfilter = require('./propFilter')()
3 | indexfilter = require('./indexFilter')()
4 |
5 | collectionBuilder =
6 | d : require('./getDays')()
7 | w : require('./getWeeks')()
8 | m : require('./getMonths')()
9 | y : require('./getYears')()
10 |
11 | module.exports = ->
12 | arr = []
13 | sheql = {}
14 |
15 | getCollections = (ast, itemCollection, startDate, endDate, filterName)->
16 | filteredCollection = []
17 | _cb = collectionBuilder[filterName]
18 | if ast[filterName]
19 | if itemCollection.length > 0
20 | for item in itemCollection
21 | tmpCollection = _cb.getCollection item.startDate, item.endDate
22 | arr.push.apply filteredCollection, sheql.filterCollection tmpCollection, ast[filterName]
23 | else
24 | tmpCollection = _cb.getCollection startDate, endDate
25 | filteredCollection = sheql.filterCollection tmpCollection, ast[filterName]
26 | [filteredCollection, filteredCollection.length is 0]
27 | else
28 | [itemCollection, false]
29 |
30 |
31 | sheql.filterCollection = (collection, filterProps) ->
32 | for prop in filterProps
33 |
34 | if prop.filterType is '.'
35 | collection = propfilter.hasProp collection, prop.filterOn
36 |
37 | else if prop.filterType is '!'
38 | collection = propfilter.notHaveProp collection, prop.filterOn
39 |
40 | else if prop.filterType is ':' and prop.filterOn.from is 'n'
41 | collection = indexfilter.nthElement collection, prop.filterOn.x1, prop.filterOn.x0
42 |
43 | else if prop.filterType is ':' and prop.filterOn.from is 'l'
44 | collection = indexfilter.lthElement collection, prop.filterOn.x1, prop.filterOn.x0
45 |
46 | collection
47 |
48 |
49 | sheql.executor = (str, startDate, endDate, startDay=0)->
50 | filterKeys = ['y', 'm', 'w', 'd']
51 | collectionBuilder.w.startDay = startDay
52 | ast = lexer.parser str
53 | itemCollection = []
54 | isEmptyCollection = false
55 |
56 | for i in filterKeys
57 | if isEmptyCollection is false
58 | [itemCollection, isEmptyCollection] =
59 | getCollections ast, itemCollection, startDate, endDate, i
60 | return (i.value for i in itemCollection)
61 |
62 | sheql
--------------------------------------------------------------------------------
/src/getDays.coffee:
--------------------------------------------------------------------------------
1 | module.exports = ->
2 | obj = {}
3 | dayName = [
4 | 'sun', 'mon'
5 | 'tue', 'wed'
6 | 'thr', 'fri'
7 | 'sat'
8 | ]
9 | getPropCollection = (date) -> [dayName[date.getDay()], date.getDate().toString()]
10 |
11 | obj.nextDate = (date) ->
12 | date = new Date date.getFullYear(), date.getMonth(), date.getDate()
13 | date.setDate date.getDate()+1
14 | date
15 |
16 | obj.dayCountForMonth = (year, month) ->
17 | d = new Date year, month+1, 0
18 | d.getDate()
19 |
20 | obj.getCollection = (startDate, endDate) ->
21 | dates = []
22 | date = startDate
23 | while date.valueOf() <= endDate
24 | dates.push
25 | value: date
26 | type: 'date'
27 | props: getPropCollection date
28 | date = @nextDate date
29 | dates
30 |
31 |
32 | obj.dayCount = (startDate, endDate) ->
33 | one_day = 1000*60*60*24
34 | startDate_ms = startDate.valueOf()
35 | endDate_ms = endDate.valueOf()
36 | difference_ms = endDate_ms - startDate_ms
37 | return 1+difference_ms/one_day
38 | obj
--------------------------------------------------------------------------------
/src/getMonths.coffee:
--------------------------------------------------------------------------------
1 | Days = require('./getDays')()
2 | Weeks = require('./getWeeks')()
3 | module.exports = ->
4 | monthName = [
5 | 'jan', 'feb', 'mar',
6 | 'apr', 'may', 'jun',
7 | 'jul', 'aug', 'sep',
8 | 'oct', 'nov', 'dec'
9 | ]
10 |
11 | obj = {}
12 | getStartDate = (startDate, year, month) ->
13 | tmpStartDate = new Date year, month, 1
14 |
15 | if tmpStartDate.valueOf() < startDate.valueOf()
16 | tmpStartDate = startDate
17 |
18 | tmpStartDate
19 |
20 | getEndDate = (endDate, year, month) ->
21 | tmpEndDate = new Date year, month, Days.dayCountForMonth year, month
22 | if tmpEndDate.valueOf() > endDate.valueOf()
23 | tmpEndDate = endDate
24 | tmpEndDate
25 |
26 |
27 |
28 | propCollection = (startDate, endDate) ->
29 | year = startDate.getFullYear()
30 | month = startDate.getMonth()
31 | [
32 | monthName[month]
33 | Days.dayCountForMonth(year, month)+'d'
34 | Weeks.weekCount(startDate, endDate)+'w'
35 | ]
36 |
37 |
38 | obj.monthCountForYear = (startDate, endDate, year) ->
39 | startYear = startDate.getFullYear()
40 | endYear = endDate.getFullYear()
41 | if startYear is year and endYear is year
42 | return @monthCount startDate, endDate
43 |
44 | if startYear is year
45 | return @monthCount startDate, new Date year, 11, 31
46 |
47 | if endYear is year
48 | return @monthCount new Date(year, 0, 1), endDate
49 | 12
50 |
51 |
52 | #Should return a count only
53 | obj.monthCount = (startDate, endDate) ->
54 | #Adding 12 months in a year
55 | months = (endDate.getFullYear() - startDate.getFullYear() + 1) * 12
56 |
57 | #Adding 12 months in a year
58 | months -= startDate.getMonth()
59 | months -= 12 - endDate.getMonth() - 1
60 | months
61 |
62 | obj.getCollectionForYear = (startDate, endDate, year)->
63 |
64 | startYear = startDate.getFullYear()
65 | endYear = endDate.getFullYear()
66 |
67 | if startYear is year and endYear is year
68 | return @getCollection startDate, endDate
69 |
70 | if startYear is year
71 | return @getCollection startDate, new Date year, 11, 31
72 |
73 | if endYear is year
74 | return @getCollection new Date(year, 0, 1), endDate
75 |
76 | return @getCollection new Date(year, 0, 1), new Date(year, 11, 31)
77 |
78 | obj.getCollection = (startDate, endDate) ->
79 | months =[]
80 | count = @monthCount startDate, endDate
81 | startMonth = startDate.getMonth()
82 | startYear = startDate.getFullYear()
83 | date = new Date startYear, startMonth, 1
84 |
85 | for i in [0...count]
86 |
87 | year = date.getFullYear()
88 | month = date.getMonth()
89 | mStartDate = getStartDate startDate, year, month
90 | mEndDate = getEndDate endDate, year, month
91 |
92 | months.push
93 | type: 'month'
94 | props: propCollection mStartDate, mEndDate
95 | startDate: mStartDate
96 | endDate: mEndDate
97 |
98 | date.setMonth date.getMonth() + 1
99 |
100 | months
101 |
102 | obj
--------------------------------------------------------------------------------
/src/getWeeks.coffee:
--------------------------------------------------------------------------------
1 | Days = require('./getDays')()
2 | module.exports = ->
3 | obj = startDay: 0
4 |
5 | # Start Day of the week is Sunday by default,
6 | # but can be easily changed to any other day also
7 |
8 | obj.getCollectionForMonth = (year, month) ->
9 | size = Days.dayCountForMonth year, month
10 | startDate = new Date year, month, 1
11 | endDate = new Date year, month, size
12 | @getCollection startDate, endDate
13 |
14 | obj.getCollectionForYear = (year) ->
15 | startDate = new Date year, 0, 1
16 | endDate = new Date year, 11, 31
17 | @getCollection startDate, endDate
18 |
19 |
20 | obj.getCollection = (startDate, endDate) ->
21 | endDay = (@startDay + 6)%7
22 | weekList = []
23 | date = startDate
24 | count = 0
25 |
26 | while date.valueOf() <= endDate.valueOf()
27 | #console.log date, count
28 | #Start of the week
29 | if date.getDay() is @startDay or date.valueOf() is startDate.valueOf()
30 | wStartDate = date
31 |
32 | #end of week
33 | if date.getDay() is endDay or date.valueOf() is endDate.valueOf()
34 | weekList.push
35 | value: count++
36 | startDate: wStartDate
37 | endDate: date
38 | type: 'week'
39 | props: [ Days.dayCount(wStartDate, date).toString() + 'd']
40 |
41 | #Go to next date
42 | date = Days.nextDate date
43 | weekList
44 |
45 | obj.getIncompleteStartDays = (date) ->
46 | dateDay = date.getDay()
47 | return 7 - (dateDay-@startDay) if @startDay < dateDay
48 | return @startDay - dateDay if @startDay > dateDay
49 | return 0
50 |
51 | obj.getIncompleteEndDays = (date) ->
52 | dateDay = date.getDay()
53 | return 1 + dateDay - @startDay if @startDay < dateDay
54 | return 8 - (@startDay - dateDay) if @startDay > dateDay
55 | return 1
56 |
57 | obj.weekCount = (startDate, endDate) ->
58 | count = 0
59 | date = startDate
60 | days = Days.dayCount startDate, endDate
61 | incompleteStartDays = @getIncompleteStartDays startDate
62 | incompleteEndDays = @getIncompleteEndDays endDate
63 | days = days - incompleteStartDays - incompleteEndDays
64 |
65 | count = days/7
66 |
67 | count++ if incompleteStartDays > 0
68 | count++ if incompleteEndDays > 0
69 | count
70 |
71 | obj
--------------------------------------------------------------------------------
/src/getYears.coffee:
--------------------------------------------------------------------------------
1 | Days = require('./getDays')()
2 | Weeks = require('./getWeeks')()
3 | module.exports = ->
4 | obj = {}
5 | getStartDate = (startDate, year) ->
6 | tmpStartDate = new Date year, 0, 1
7 | if tmpStartDate.valueOf() < startDate.valueOf()
8 | tmpStartDate = startDate
9 | tmpStartDate
10 |
11 | getEndDate = (endDate, year) ->
12 | tmpEndDate = new Date year, 11, 31
13 | if tmpEndDate.valueOf() > endDate.valueOf()
14 | tmpEndDate = endDate
15 | tmpEndDate
16 |
17 |
18 | propCollection = (startDate, endDate) ->
19 | year = startDate.getFullYear()
20 | weekCount = "#{Weeks.weekCount startDate, endDate}w"
21 | isLeap = obj.isLeapYear year
22 | dayCount = if isLeap is true then '366d' else '365d'
23 | [dayCount, weekCount, year.toString()]
24 |
25 |
26 | obj.isLeapYear = (year) ->
27 | d = new Date year, 2, 0
28 | if d.getDate() is 29 then true else false
29 |
30 |
31 | obj.getCollection = (startDate, endDate) ->
32 | years = []
33 | count = endDate.getFullYear() - startDate.getFullYear()
34 | currentYear = startDate.getFullYear()
35 |
36 | for i in [0..count]
37 | yStartDate = getStartDate startDate, currentYear
38 | yEndDate = getEndDate endDate, currentYear
39 |
40 | years.push
41 | type: 'year'
42 | props: propCollection yStartDate, yEndDate
43 | startDate : yStartDate
44 | endDate : yEndDate
45 | currentYear++
46 |
47 | years
48 |
49 |
50 | obj
--------------------------------------------------------------------------------
/src/indexFilter.coffee:
--------------------------------------------------------------------------------
1 | module.exports = ->
2 | obj = {}
3 |
4 | obj.lthElement = (itemCollection, x1, x0) ->
5 | itemCollection = itemCollection.reverse()
6 | @nthElement itemCollection, x1, x0
7 | .reverse()
8 |
9 | obj.nthElement = (itemCollection, x1, x0) ->
10 | #index starts from 0, implementation wise
11 | x0--
12 | filteredCollection = []
13 | totalElements = itemCollection.length
14 |
15 | #x1 is not there
16 | if x1 is 0 and x0 < totalElements and x0 >= 0
17 | filteredCollection.push itemCollection[x0]
18 | else
19 | for x in [0...totalElements]
20 | index = (x0) + x1 * x
21 | if index >= 0 and index < totalElements
22 | filteredCollection.push itemCollection[index]
23 | if x1 < 0
24 | filteredCollection = filteredCollection.reverse()
25 |
26 | filteredCollection
27 |
28 | obj
--------------------------------------------------------------------------------
/src/lexer.coffee:
--------------------------------------------------------------------------------
1 | utils = require './utils'
2 | module.exports = ->
3 | _proto = {}
4 |
5 | _proto._splitOnTokens = (str) ->
6 | utils.filterItems str.split(/( |:|\.|\!)/), (i) ->
7 | i isnt " " and i isnt ""
8 |
9 |
10 | _proto._x0Extractor = (str) ->
11 | x0 = undefined
12 | x0 = str.replace(/\]/, "").replace(/.*\[/, "").replace(/.*x/, "")
13 | x0 = (if x0 is "" then 0 else x0)
14 | parseInt x0, 10
15 |
16 | _proto._x1Extractor = (str) ->
17 | x1 = 0
18 | if str.match("x")
19 | x1 = str.replace(/x.*/, "").replace(/.*\[/, "")
20 | if x1 is ""
21 | x1 = 1
22 | else if x1 is "-"
23 | x1 = -1
24 | else
25 | x1 = parseInt(x1, 10)
26 | else
27 | x1 = 0
28 | x1
29 |
30 | _proto._parseColonFilters = (str) ->
31 | filter = {}
32 | filter.from = str[0]
33 | filter.x0 = @_x0Extractor(str)
34 | filter.x1 = @_x1Extractor(str)
35 | filter
36 |
37 | _proto._ast = (tokenCollection) ->
38 | tree = {}
39 | tokenType = undefined
40 | lastBaseToken = undefined
41 | filterOn = undefined
42 | for token in tokenCollection
43 | if token.match(/^(y|m|w|d)$/)
44 | tree[token] = []
45 | lastBaseToken = token
46 | else if token.match(/(:|\.|\!)/)
47 | tokenType = token
48 | else
49 | if tokenType is ":"
50 | filterOn = @_parseColonFilters(token)
51 | else
52 | filterOn = token
53 |
54 | tree[lastBaseToken].push
55 | filterType: tokenType
56 | filterOn: filterOn
57 |
58 | tree
59 |
60 | _proto.parser = (str) ->
61 | tokenCollection = @_splitOnTokens(str)
62 | @_ast tokenCollection
63 |
64 | _proto
--------------------------------------------------------------------------------
/src/propFilter.coffee:
--------------------------------------------------------------------------------
1 | utils = require './utils'
2 | module.exports = ->
3 | obj = {}
4 |
5 | obj.notHaveProp = (itemCollection, filterOn) ->
6 | utils.filterItems itemCollection, (item) -> item.props.indexOf(filterOn) is -1
7 |
8 | obj.hasProp = (itemCollection, filterOn) ->
9 | utils.filterItems itemCollection, (item) ->
10 | item.props.indexOf(filterOn) > -1
11 |
12 | obj
--------------------------------------------------------------------------------
/src/utils.coffee:
--------------------------------------------------------------------------------
1 | utils = {}
2 | utils.filterItems = (items, condition) ->
3 | filteredCollection = []
4 | for item in items
5 | if condition(item) is yes
6 | filteredCollection.push item
7 | filteredCollection
8 |
9 | module.exports = utils
--------------------------------------------------------------------------------
/test/mocha.opts:
--------------------------------------------------------------------------------
1 | --compilers coffee:coffee-script/register
2 | --require should
3 | --reporter min
--------------------------------------------------------------------------------
/test/test.dateUtils.js:
--------------------------------------------------------------------------------
1 | var should = require('should');
2 |
3 | describe('DateUtils', function () {
4 | var dt, DateUtils;
5 |
6 | beforeEach(function () {
7 | dt = new DateUtils();
8 | });
9 |
10 | before(function () {
11 | DateUtils = require('../src/dateUtils');
12 | });
13 |
14 | after(function () {
15 | delete require.cache[require.resolve('../src/dateUtils')];
16 | });
17 |
18 | });
--------------------------------------------------------------------------------
/test/test.executor.coffee:
--------------------------------------------------------------------------------
1 | should = require "should"
2 | describe "Sheql", ->
3 | executor = {}
4 | sh = {}
5 | beforeEach ->
6 | sh = executor()
7 |
8 |
9 | before ->
10 | executor = require("../src/executor")
11 |
12 | after ->
13 | delete require.cache[require.resolve("../src/executor")]
14 |
15 | describe "executor", ->
16 |
17 | it "should compute", ->
18 | startDate = new Date 2014, 0, 5
19 | endDate = new Date 2014, 11, 30
20 | sh.executor 'y m:n[2x] d.sat', startDate, endDate
21 | .length.should.equal 25
22 | sh.executor 'm d:n[12]', startDate, endDate
23 | .length.should.equal 12
24 | sh.executor 'm.feb d.8', startDate, endDate
25 | .length.should.equal 1
26 | sh.executor 'm.jan w d.sat', startDate, endDate
27 | .length.should.equal 3
28 | sh.executor 'm:n[-x+3] d:n[1]', startDate, endDate
29 | .length.should.equal 3
30 | sh.executor 'm.qwe d:n[1]', startDate, endDate
31 | .length.should.equal 0
32 |
33 |
34 |
35 |
36 | describe "filterCollection", ->
37 | it "should filter on dots", ->
38 | collection = [props: ['sat']]
39 | filterProps1 = [filterType: '.', filterOn: 'sat']
40 | filterProps2 = [filterType: '.', filterOn: 'mon']
41 |
42 | sh.filterCollection collection, filterProps1
43 | .should.eql collection
44 |
45 | sh.filterCollection collection, filterProps2
46 | .should.eql []
47 |
48 |
49 | it "should filter on !", ->
50 | collection = [props: ['sat']]
51 | filterProps1 = [filterType: '!', filterOn: 'sat']
52 | filterProps2 = [filterType: '!', filterOn: 'mon']
53 |
54 | sh.filterCollection collection, filterProps1
55 | .should.eql []
56 |
57 | sh.filterCollection collection, filterProps2
58 | .should.eql collection
59 |
60 |
--------------------------------------------------------------------------------
/test/test.getDays.coffee:
--------------------------------------------------------------------------------
1 | should = require "should"
2 | describe "getDays", ->
3 | getDays = {}
4 | dy = {}
5 | dStart = ''
6 | dEnd = ''
7 | beforeEach ->
8 | dy = getDays()
9 |
10 | before ->
11 | getDays = require "../src/getDays"
12 |
13 | after ->
14 | delete require.cache[require.resolve "../src/getDays" ]
15 |
16 | it "should exist", ->
17 | should.exist dy
18 |
19 | describe "getCollection()", ->
20 | it "return date array", ->
21 | startDate = new Date 2013, 11, 26
22 | endDate = new Date 2014,0,5
23 |
24 | dy.getCollection startDate, endDate
25 | .length.should.equal 11
26 |
27 | it "should attach week and date", ->
28 | startDate = new Date 2013, 11, 26
29 | endDate = new Date 2014,0,5
30 |
31 | [w1] = dy.getCollection startDate, endDate
32 | w1.props.should.eql ['thr', 26]
33 |
34 |
35 | describe "nextDate()", ->
36 | it "should return next date", ->
37 | ndy = dy.nextDate new Date 2013,11,31
38 | ndy.should.eql (new Date 2014,0,1)
39 |
40 | describe "dayCount", ->
41 | it "should return dayCount", ->
42 | startDate = new Date 2014, 0, 1
43 | endDate = new Date 2014,1,5
44 | dy.dayCount(startDate, endDate).should.equal 36
45 |
46 | startDate = new Date 2014, 5, 22
47 | endDate = new Date 2014, 5, 29
48 | dy.dayCount(startDate, endDate).should.equal 8
49 |
50 | describe "dayCountForMonth", ->
51 | it "should return dayCount", ->
52 | dy.dayCountForMonth(2016, 1).should.equal 29
53 | dy.dayCountForMonth(2011, 0).should.equal 31
54 | dy.dayCountForMonth(2011, 1).should.equal 28
55 | dy.dayCountForMonth(2012, 1).should.equal 29
56 |
--------------------------------------------------------------------------------
/test/test.getMonths.coffee:
--------------------------------------------------------------------------------
1 | should = require "should"
2 | describe "getMonths", ->
3 | getMonths = {}
4 | mn = {}
5 | dStart = ''
6 | dEnd = ''
7 | beforeEach ->
8 | mn = getMonths()
9 | dStart = new Date 2016, 1, 23
10 | dEnd = new Date 2016, 2, 23
11 |
12 | before ->
13 | getMonths = require "../src/getMonths"
14 |
15 | after ->
16 | delete require.cache[require.resolve "../src/getMonths" ]
17 |
18 | it "should exist", ->
19 | should.exist mn
20 |
21 | describe "monthCount", ->
22 | it "should exist", ->
23 | should.exist mn.monthCount
24 |
25 | it "should return count: 2", ->
26 | mn.monthCount(dStart, dEnd).should.equal 2
27 |
28 |
29 | it "should return count: 25", ->
30 | dStart = new Date 2010, 1, 1
31 | dEnd = new Date 2012, 1, 1
32 | mn.monthCount(dStart, dEnd).should.equal 25
33 |
34 |
35 | describe "monthCountForYear", ->
36 | it "should exist", ->
37 | should.exist mn.monthCountForYear
38 |
39 | it "should return monthCount for a single year only", ->
40 | dStart = new Date 2010, 5, 1
41 | dEnd = new Date 2012, 8, 1
42 | mn.monthCountForYear(dStart, dEnd, 2011).should.equal 12
43 | mn.monthCountForYear(dStart, dEnd, 2010).should.equal 7
44 | mn.monthCountForYear(dStart, dEnd, 2012).should.equal 9
45 |
46 | describe "getCollectionForYear", ->
47 | it "should exist", ->
48 | should.exist mn.getCollectionForYear
49 |
50 | it "should return a collection", ->
51 | dStart = new Date 2016, 4, 23
52 | dEnd = new Date 2018, 3, 15
53 |
54 | mn.getCollectionForYear( dStart, dEnd, 2017).length.should.equal 12
55 | mn.getCollectionForYear( dStart, dEnd, 2016).length.should.equal 8
56 |
57 |
58 | describe "getCollection", ->
59 |
60 | it "should.exist", ->
61 | should.exist mn.getCollection
62 |
63 | it "should return a collection", ->
64 | dStart = new Date 2016, 0, 23
65 | dEnd = new Date 2016, 3, 15
66 |
67 | [jan, feb, ..., apr] = mn.getCollection dStart, dEnd
68 | jan.props.should.eql ['jan', '31d', '3w']
69 | feb.props.should.eql ['feb', '29d', '5w']
70 | apr.props.should.eql ['apr', '30d', '3w']
71 |
72 | it "should attach meta", ->
73 | dStart = new Date 2016, 0, 23
74 | dEnd = new Date 2016, 3, 15
75 |
76 | [m1] = mn.getCollection dStart, dEnd
77 | m1.type.should.equal 'month'
78 |
79 |
80 | it "should attach start and end dates", ->
81 | dStart = new Date 2016, 0, 23
82 | dEnd = new Date 2016, 3, 15
83 |
84 | [m1, ..., ml] = mn.getCollection dStart, dEnd
85 | m1.startDate.toString().should.equal dStart.toString()
86 | m1.endDate.toString().should.equal (new Date 2016, 0, 31).toString()
87 |
88 | ml.startDate.toString().should.equal (new Date 2016, 3, 1).toString()
89 | ml.endDate.toString().should.equal (new Date 2016, 3, 15).toString()
90 |
--------------------------------------------------------------------------------
/test/test.getWeeks.coffee:
--------------------------------------------------------------------------------
1 | should = require "should"
2 | describe "getWeeks", ->
3 | getWeeks = {}
4 | wk = {}
5 | dStart = ''
6 | dEnd = ''
7 | beforeEach ->
8 | wk = getWeeks()
9 |
10 | before ->
11 | getWeeks = require "../src/getWeeks"
12 |
13 | after ->
14 | delete require.cache[require.resolve "../src/getWeeks" ]
15 |
16 | it "should exist", ->
17 | should.exist wk
18 |
19 | describe "getCollection", ->
20 | it "should exist", -> should.exist wk.getCollection
21 |
22 | it "should return an array", ->
23 | dStart = new Date 2014, 1, 3
24 | dEnd = new Date 2014, 1, 20
25 | wk.getCollection( dStart, dEnd).length.should.equal 3
26 |
27 | it "should attach start and end dates", ->
28 | dStart = new Date 2013, 11, 27
29 | dEnd = new Date 2014, 1, 20
30 | [w1] = wk.getCollection(dStart, dEnd)
31 | w1.startDate.valueOf().should.equal dStart.valueOf()
32 | w1.endDate.valueOf().should.equal (new Date 2013, 11, 28).valueOf()
33 |
34 | it "should attach weeks size in days", ->
35 | dStart = new Date 2014, 5, 22
36 | dEnd = new Date 2014, 6, 1
37 | [w1, w2, w3] = wk.getCollection(dStart, dEnd)
38 | w1.props.should.eql ['7d']
39 | w2.props.should.eql ['3d']
40 |
41 | describe "getCollectionForMonth", ->
42 | it "should exist", -> should.exist wk.getCollectionForMonth
43 |
44 | it "should return an array", ->
45 | wk.getCollectionForMonth(2014, 0).length.should.equal 5
46 |
47 |
48 | describe "getCollectionForYear", ->
49 | it "should exist", -> should.exist wk.getCollectionForYear
50 |
51 | it "should return an array", ->
52 | wk.getCollectionForYear(2014).length.should.equal 53
53 |
54 | describe "getIncompleteEndDays", ->
55 | it "should return number of days", ->
56 | wk.getIncompleteEndDays new Date 2014, 0, 28
57 | .should.equal 3
58 |
59 | wk.getIncompleteEndDays new Date 2014, 1, 28
60 | .should.equal 6
61 |
62 | wk.getIncompleteEndDays new Date 2014, 1, 2
63 | .should.equal 1
64 |
65 | wk.getIncompleteEndDays new Date 2014, 1, 27
66 | .should.equal 5
67 |
68 |
69 | it "should handle variable startDay", ->
70 | wk.startDay = 4
71 | wk.getIncompleteEndDays new Date 2014, 1, 28
72 | .should.equal 2
73 |
74 | wk.startDay = 5
75 | wk.getIncompleteEndDays new Date 2014, 1, 26
76 | .should.equal 6
77 |
78 |
79 | describe "getIncompleteStartDays", ->
80 | it "should return number of days", ->
81 | wk.getIncompleteStartDays new Date 2014, 3, 2
82 | .should.equal 4
83 |
84 | wk.getIncompleteStartDays new Date 2014, 0, 2
85 | .should.equal 3
86 |
87 | wk.getIncompleteStartDays new Date 2014, 0, 5
88 | .should.equal 0
89 |
90 | wk.getIncompleteStartDays new Date 2014, 1, 3
91 | .should.equal 6
92 |
93 | wk.getIncompleteStartDays new Date 2000, 0, 1
94 | .should.equal 1
95 |
96 | wk.getIncompleteStartDays new Date 2014, 5, 6
97 | .should.equal 2
98 |
99 | wk.getIncompleteStartDays new Date 2014, 5, 3
100 | .should.equal 5
101 |
102 | it "should handle variable startDay", ->
103 | wk.startDay = 4
104 | wk.getIncompleteStartDays new Date 2014, 0, 2
105 | .should.equal 0
106 |
107 | wk.startDay = 3
108 | wk.getIncompleteStartDays new Date 2014, 5, 3
109 | .should.equal 1
110 |
111 |
112 |
113 | describe "weekCount", ->
114 | it "should exist", ->
115 | should.exist wk.weekCount
116 |
117 | it "should use the set first day of the week", ->
118 | dStart = new Date 2000, 0, 1
119 | dEnd = new Date 2000, 11, 31
120 | wk.startDay = 1
121 | wk.weekCount(dStart, dEnd).should.equal 53
122 |
123 | it "should return week count", ->
124 | dStart = new Date 2014, 1, 3
125 | dEnd = new Date 2014, 1, 20
126 | wk.weekCount(dStart, dEnd).should.equal 3
127 |
128 | dStart = new Date 2016, 3, 15
129 | dEnd = new Date 2016, 3, 23
130 | wk.weekCount(dStart, dEnd).should.equal 2
131 |
132 | dStart = new Date 2000, 0, 1
133 | dEnd = new Date 2000, 0, 31
134 | wk.weekCount(dStart, dEnd).should.equal 6
135 |
136 | dStart = new Date 2016, 0, 23
137 | dEnd = new Date 2016, 0, 31
138 | wk.weekCount(dStart, dEnd).should.equal 3
139 |
140 |
141 | it "should return week count for year", ->
142 | dStart = new Date 2014, 0, 10
143 | dEnd = new Date 2014, 1, 14
144 | wk.weekCount(dStart, dEnd).should.equal 6
145 |
146 | it "should return week count for same week", ->
147 | dStart = new Date 2013, 11, 30
148 | dEnd = new Date 2014, 0, 2
149 | wk.weekCount(dStart, dEnd).should.equal 1
150 |
151 |
--------------------------------------------------------------------------------
/test/test.getYears.coffee:
--------------------------------------------------------------------------------
1 | should = require "should"
2 | describe "getYears", ->
3 | getYears = {}
4 | yr = {}
5 | dStart = 0
6 | dEnd = 0
7 | beforeEach ->
8 | yr = getYears()
9 | dStart = new Date 2016, 1, 23
10 | dEnd = new Date 2020, 4, 11
11 |
12 | before ->
13 | getYears = require "../src/getYears"
14 |
15 | after ->
16 | delete require.cache[require.resolve "../src/getYears" ]
17 |
18 | it "should exist", ->
19 | should.exist yr
20 |
21 | describe "getCollection", ->
22 | it "should exist", ->
23 | should.exist yr.getCollection
24 |
25 | it "should return a collection of 5 items", ->
26 | yr.getCollection(dStart, dEnd).length .should.equal 5
27 |
28 | it "should attach type as year", ->
29 | [year] = yr.getCollection(dStart, dEnd)
30 | year.type.should.equal 'year'
31 |
32 | it "should attach props", ->
33 | [year1,year2,..., year20] = yr.getCollection(dStart, dEnd)
34 | year1.props.should.eql ['366d', '45w', '2016']
35 | year2.props.should.eql ['365d', '53w', '2017']
36 | year20.props.should.eql ['366d', '20w', '2020']
37 |
38 | it "should attach startDate and endDate", ->
39 | [year1, year2,..., yearlast] = yr.getCollection(dStart, dEnd)
40 |
41 | year1.startDate.toString().should.equal dStart.toString()
42 | year1.endDate.toString().should.equal (new Date 2016, 11, 31).toString()
43 |
44 | year2.startDate.toString().should.equal (new Date 2017, 0, 1).toString()
45 | year2.endDate.toString().should.equal (new Date 2017, 11, 31).toString()
46 |
47 | yearlast.startDate.toString().should.equal (new Date 2020, 0 ,1).toString()
48 | yearlast.endDate.toString().should.equal (new Date 2020, 4, 11).toString()
49 |
50 |
51 | describe "isLeapYear", ->
52 | it "should work", ->
53 | yr.isLeapYear(2012).should.be.true
54 | yr.isLeapYear(2010).should.be.false
55 |
--------------------------------------------------------------------------------
/test/test.indexFilter.coffee:
--------------------------------------------------------------------------------
1 | should = require "should"
2 | describe "indexFilter", ->
3 | indexFilter = {}
4 | dxf = {}
5 | dStart = ''
6 | dEnd = ''
7 | itemCollection = []
8 | beforeEach ->
9 | dxf = indexFilter()
10 | itemCollection = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
11 |
12 | before ->
13 | indexFilter = require "../src/indexFilter"
14 |
15 | after ->
16 | delete require.cache[require.resolve "../src/indexFilter" ]
17 |
18 | it "should exist", ->
19 | should.exist dxf
20 |
21 | describe "lthElement()", ->
22 | it "should return alternate elements n[2x] ", ->
23 | dxf.lthElement itemCollection, 2, 0
24 | .should.eql ['b', 'd', 'f']
25 |
26 | it "should return third element [3]", ->
27 | dxf.lthElement itemCollection, 0, 3
28 | .should.eql ['e']
29 |
30 | it "should return all but first 3 [x+3]", ->
31 | dxf.lthElement itemCollection, 1, 3
32 | .should.eql ['a', 'b', 'c', 'd', 'e']
33 |
34 | it "should return first child [-x+4]", ->
35 | dxf.lthElement itemCollection, -1, 4
36 | .should.eql ['d', 'e', 'f', 'g']
37 |
38 | it "should return [0]", ->
39 | dxf.lthElement itemCollection, 0, 0
40 | .should.eql [ ]
41 |
42 | it "should return [7]", ->
43 | dxf.lthElement itemCollection, 0, 7
44 | .should.eql ['a']
45 |
46 |
47 |
48 | describe "nthElement", ->
49 |
50 | it "should return alternate elements n[2x] ", ->
51 | dxf.nthElement itemCollection, 2, 0
52 | .should.eql ['b', 'd', 'f']
53 |
54 | it "should return 3rd element [3]", ->
55 | dxf.nthElement itemCollection, 0, 3
56 | .should.eql ['c']
57 |
58 | it "should return all but first 3 [x+3]", ->
59 | dxf.nthElement itemCollection, 1, 3
60 | .should.eql ['c', 'd', 'e', 'f', 'g']
61 |
62 | it "should return first child [-x+4]", ->
63 | dxf.nthElement itemCollection, -1, 4
64 | .should.eql [ 'a', 'b', 'c', 'd' ]
65 |
66 | it "should return [0]", ->
67 | dxf.nthElement itemCollection, 0, 0
68 | .should.eql [ ]
69 |
70 | it "should return [7]", ->
71 | dxf.nthElement itemCollection, 0, 7
72 | .should.eql ['g']
73 |
74 | it "should return 3 elements for [-x+3]", ->
75 | dxf.nthElement itemCollection, -1, 3
76 | .should.eql ['a', 'b', 'c']
77 |
--------------------------------------------------------------------------------
/test/test.lexer.coffee:
--------------------------------------------------------------------------------
1 | should = require("should")
2 | describe "Lexer", ->
3 | lxr = undefined
4 | Lexer = undefined
5 | beforeEach ->
6 | lxr = Lexer()
7 |
8 |
9 | before ->
10 | Lexer = require("../src/lexer")
11 |
12 |
13 | after ->
14 | delete require.cache[require.resolve("../src/lexer")]
15 |
16 |
17 |
18 | describe "_ast()", ->
19 | it "should exist", ->
20 | should.exist lxr._ast
21 |
22 | it "should build ast", ->
23 | lxr._ast ['y', '.', 'leap', 'm', '.', 'jan', 'd', '.', 'wed']
24 | .should.eql
25 | y: [filterType: '.', filterOn: 'leap']
26 | m: [filterType: '.', filterOn: 'jan' ]
27 | d: [filterType: '.', filterOn: 'wed' ]
28 |
29 | it "should attach core type", ->
30 | lxr._ast([
31 | "y"
32 | "m"
33 | "d"
34 | ]).should.eql
35 | y: []
36 | m: []
37 | d: []
38 |
39 |
40 |
41 | it "should attach filters", ->
42 | lxr._ast([
43 | "y"
44 | "!"
45 | "leap"
46 | "m"
47 | ":"
48 | "n[x+1]"
49 | "d"
50 | "."
51 | "sat"
52 | "."
53 | "21"
54 | ]).should.eql
55 | y: [
56 | filterType: "!"
57 | filterOn: "leap"
58 | ]
59 | m: [
60 | filterType: ":"
61 | filterOn: # 'n[x+1]'
62 | from: "n"
63 | x0: 1
64 | x1: 1
65 | ]
66 | d: [
67 | {
68 | filterType: "."
69 | filterOn: "sat"
70 | }
71 | {
72 | filterType: "."
73 | filterOn: "21"
74 | }
75 | ]
76 |
77 |
78 |
79 |
80 |
81 | describe "_splitOnTokens()", ->
82 | it "should exist", ->
83 | should.exist lxr._splitOnTokens
84 |
85 |
86 | it "should split on string", ->
87 | lxr._splitOnTokens("y m d").should.eql [
88 | "y"
89 | "m"
90 | "d"
91 | ]
92 | lxr._splitOnTokens "y.leap m.jan d.wed"
93 | .should.eql ['y', '.', 'leap', 'm', '.', 'jan', 'd', '.', 'wed']
94 |
95 |
96 | it "should ignore multi spaces", ->
97 | lxr._splitOnTokens("y m d").should.eql [
98 | "y"
99 | "m"
100 | "d"
101 | ]
102 |
103 |
104 | it "should split on colons", ->
105 | lxr._splitOnTokens("y m:n[2x+4] d").should.eql [
106 | "y"
107 | "m"
108 | ":"
109 | "n[2x+4]"
110 | "d"
111 | ]
112 |
113 |
114 | it "should split on dots", ->
115 | lxr._splitOnTokens("y m.sat d").should.eql [
116 | "y"
117 | "m"
118 | "."
119 | "sat"
120 | "d"
121 | ]
122 |
123 |
124 | it "should split on exclamation", ->
125 | lxr._splitOnTokens("y m!sat d").should.eql [
126 | "y"
127 | "m"
128 | "!"
129 | "sat"
130 | "d"
131 | ]
132 |
133 |
134 | it "should split on all", ->
135 | lxr._splitOnTokens("y.sat:n[x+3]:l[2] m!sat d").should.eql [
136 | "y"
137 | "."
138 | "sat"
139 | ":"
140 | "n[x+3]"
141 | ":"
142 | "l[2]"
143 | "m"
144 | "!"
145 | "sat"
146 | "d"
147 | ]
148 |
149 |
150 |
151 |
152 | describe "_x1Extractor()", ->
153 | it "should exist", ->
154 | should.exist lxr._x1Extractor
155 |
156 |
157 | it "should determine the type for n[x+1]", ->
158 | lxr._x1Extractor("n[x+1]").should.equal 1
159 |
160 |
161 | it "should determine the type for n[2x+1]", ->
162 | lxr._x1Extractor("n[2x+1]").should.equal 2
163 |
164 |
165 | it "should determine the type for n[2x]", ->
166 | lxr._x1Extractor("n[2x]").should.equal 2
167 |
168 |
169 | it "should determine the type for n[2]", ->
170 | lxr._x1Extractor("n[2]").should.equal 0
171 |
172 |
173 | it "should determine the type for n[-2]", ->
174 | lxr._x1Extractor("n[-2]").should.equal 0
175 |
176 |
177 | it "should determine the type for n[-2x]", ->
178 | lxr._x1Extractor("n[-2x]").should.equal -2
179 |
180 | it "experiment", ->
181 | lxr._x1Extractor("n[-x+3]").should.equal -1
182 |
183 |
184 |
185 | describe "_x0Extractor()", ->
186 | it "should exist", ->
187 | should.exist lxr._x0Extractor
188 |
189 |
190 | it "should determine the type for n[x+1]", ->
191 | lxr._x0Extractor("n[x+1]").should.equal 1
192 |
193 |
194 | it "should determine the type for n[2x+1]", ->
195 | lxr._x0Extractor("n[2x+1]").should.equal 1
196 |
197 |
198 | it "should determine the type for n[2x]", ->
199 | lxr._x0Extractor("n[2x]").should.equal 0
200 |
201 |
202 | it "should determine the type for n[2]", ->
203 | lxr._x0Extractor("n[2]").should.equal 2
204 |
205 |
206 | it "should determine the type for n[-2]", ->
207 | lxr._x0Extractor("n[-2]").should.equal -2
208 |
209 |
210 | it "should determine the type for n[-2x]", ->
211 | lxr._x0Extractor("l[-2x]").should.equal 0
212 |
213 |
214 |
215 |
216 | describe "_parseColonFilters()", ->
217 | it "should exist", ->
218 | should.exist lxr._parseColonFilters
219 |
220 |
221 | it "should create sub tree for colon tokens", ->
222 | lxr._parseColonFilters("n[-4x-23]").should.eql
223 | from: "n"
224 | x0: -23
225 | x1: -4
226 |
227 | lxr._parseColonFilters("n[-x+3]").should.eql
228 | from: "n"
229 | x0: 3
230 | x1: -1
231 |
232 |
233 |
234 |
235 |
236 | describe "parser()", ->
237 | it "should have parse", ->
238 | should.exist lxr.parser
239 |
240 | it "should return an ast", ->
241 | lxr.parser("y m d.sat").should.eql
242 | y: []
243 | m: []
244 | d: [
245 | filterType: "."
246 | filterOn: "sat"
247 | ]
--------------------------------------------------------------------------------
/test/test.propFilter.coffee:
--------------------------------------------------------------------------------
1 | should = require "should"
2 | describe "propFilter", ->
3 | propFilter = {}
4 | pf = {}
5 | dStart = ''
6 | dEnd = ''
7 | beforeEach ->
8 | pf = propFilter()
9 |
10 | before ->
11 | propFilter = require "../src/propFilter"
12 |
13 | after ->
14 | delete require.cache[require.resolve "../src/propFilter" ]
15 |
16 | it "should exist", ->
17 | should.exist pf
18 |
19 | describe "notHaveProp", ->
20 | it "should return true", ->
21 | itemCollection = [props: ['aaa']]
22 | pf.notHaveProp itemCollection, 'aaa'
23 | .should.eql []
24 |
25 |
26 | it "should return empty", ->
27 | itemCollection = [props: ['aaaa']]
28 | pf.notHaveProp itemCollection, 'aaa'
29 | .should.eql [itemCollection[0]]
30 |
31 | describe "hasProp", ->
32 | it "should return true", ->
33 | itemCollection = [props: ['aaa']]
34 | pf.hasProp itemCollection, 'aaa'
35 | .should.eql [itemCollection[0]]
36 |
37 |
38 | it "should return empty", ->
39 | pf.hasProp [props: ['aaaa']], 'aaa'
40 | .should.eql []
--------------------------------------------------------------------------------