├── .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 | ![alt text](https://raw.githubusercontent.com/practo/sheql/master/public/images/scheql.png) 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 | 18 | 20 | 40 | 44 | 48 | 52 | 53 | 55 | 56 | 58 | image/svg+xml 59 | 61 | 62 | 63 | 64 | 65 | 69 | 74 | 76 | SCHE L 88 | 94 | 104 | 105 | 106 | SCHE 121 | 127 | 136 | 149 | 150 | L 165 | 166 | 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 [] --------------------------------------------------------------------------------