├── .eslintrc ├── .gitignore ├── README-CN.md ├── README.md ├── asset ├── codemirror.css ├── codemirror.js ├── qone.ico ├── qone.png ├── show-hint.js ├── sql-hint.js └── sql-qone.js ├── index.html ├── package.json ├── qone.js ├── qone.min.js └── test ├── clone.html ├── index.html ├── index.ie.html ├── lib ├── qunit.css └── qunit.js ├── test.ie.js └── test.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "standard", 3 | "rules": { 4 | "indent": [ 5 | "error", 6 | 4 7 | ], 8 | "space-before-function-paren": [ 9 | "error", 10 | "never" 11 | ], 12 | "one-var": 0, 13 | "no-undef": 0, 14 | "eqeqeq": 0, 15 | "no-mixed-operators": 0, 16 | "camelcase": 0, 17 | "no-return-assign": 0 18 | } 19 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules -------------------------------------------------------------------------------- /README-CN.md: -------------------------------------------------------------------------------- 1 | [English](./README.md) | 简体中文 2 | 3 | qone 4 | ============================== 5 | [![npm version](https://badge.fury.io/js/qone.svg)](https://www.npmjs.com/package/qone) 6 | 7 | .NET LINQ 在 javascript 中的实现 8 | 9 | ## 缘由 10 | 11 | 最近刚好修改了腾讯文档 Excel 表格公式的一些 bug,主要是修改公式的 parser 。比如下面的脚本怎么转成 javascript 运行? 12 | 13 | ``` js 14 | = IF(SUM(J6:J7) + SUM(J6:J7) > 10, "A2 是 foo", "A2 不是 foo") 15 | ``` 16 | 17 | 公式或一些脚本语言的实现包含几个主要步骤: 18 | 19 | scanner > lexer > parser > ast > code string 20 | 21 | 得到 code string 之后可以动态运行,比如 js 里使用 eval ,eval 能保留上下文信息,缺点是执行代码包含编译器代码,eval 的安全性等。 22 | 得到 code string 之后也可直接使用生成的 code string 运行,缺点是依赖构建工具或者编辑器插件去动态替换源代码。 23 | 24 | 比如 wind 同时支持 JIT 和 AOT, qone 的思路和上面类似,但不完全相同, qone 的如下: 25 | 26 | scanner > lexer > parser > ast > method(ast) 27 | 28 | 这个后面写原理时候再细说。 29 | 30 | 总的来说,因为腾讯文档公式相关工作、早年的 kmdjs 开发 (uglify2) 和 .NET 开发,所以有了 qone 。 31 | 32 | - [LINQ](#linq) 33 | - [qone 安装](#qone-安装) 34 | - [qone 关键字与运算符](#qone-关键字与运算符) 35 | - [qone 方法注入](#qone-方法注入) 36 | - [qone select 输出](#qone-select-输出) 37 | - [qone orderby](#qone-orderby) 38 | - [qone groupby](#qone-groupby) 39 | - [qone 多数据源](#qone-多数据源) 40 | - [qone 嵌套子数据源](#qone-嵌套子数据源) 41 | - [qone limit 与分页查询](#qone-limit-与分页查询) 42 | - [links](#star--fork--pr--repl--follow-me) 43 | --- 44 | 45 | ## LINQ 46 | 47 | LINQ (语言集成查询) 是 .NET Framework 3.5 版中引入的一项创新功能。在 Visual Studio 中,可以用 Visual Basic 或 C# 为以下数据源编写 LINQ 查询:SQL Server 数据库、XML 文档、ADO.NET 数据集,以及可枚举的 Objects(即 LINQ to Objects)。 48 | 49 | qone 是一款让 Web 前端工程师在 javascript 使用 .NET 平台下类似 LINQ 语法的前端库。qone 让 Web 前端工程师通过字符串的形式实现了 LINQ to Objects 的调用(下面统一叫做 qone to objects),Objects即 JSON 组成的 Array。举个简单的例子(qone 远比下面的例子强大): 50 | 51 | ``` js 52 | var list = [ 53 | { name: 'qone', age: 1 }, 54 | { name: 'linq', age: 18 }, 55 | { name: 'dntzhang', age: 28 } 56 | ] 57 | 58 | var result = qone({ list }).query(` 59 | from n in list 60 | where n.age > 18 61 | select n 62 | `) 63 | 64 | assert.deepEqual(result, [{ "name": "dntzhang", "age": 28 }]) 65 | ``` 66 | 67 | 与 LINQ 一样,和 SQL 不同,qone 的 from 也在前面,为后面语句能够有智能提示,qone 是基于 string 的实时编译,不是 javasript 的原生语法,所以虽然 from 写在前面但不支持智能提示,但可以专门为 qone 写个编辑器插件去实现智能提示,所以 qone 语法设计上依然把 from 放在前面。 68 | 69 | 从根本上说,qone to objects 表示一种新的处理集合的方法。 采用旧方法,您必须编写指定如何从集合检索数据的复杂的 foreach 循环。 而采用 qone 方法,您只需编写描述要检索的内容的声明性代码。 70 | 另外,与传统的 foreach 循环相比,qone 查询具有三大优势: 71 | 72 | * 它们更简明、更易读,尤其在筛选多个条件时 73 | * 它们使用最少的应用程序代码提供强大的筛选、排序和分组功能 74 | * 无需修改或只需做很小的修改即可将它们移植到其他数据源 75 | 76 | 通常,您要对数据执行的操作越复杂,就越能体会到 qone 相较于传统迭代技术的优势。 77 | 78 | ## qone 安装 79 | 80 | ``` bash 81 | npm install qone 82 | ``` 83 | 84 | CDN: 85 | 86 | * [https://unpkg.com/qone@1.0.0/qone.js](https://unpkg.com/qone@1.0.0/qone.js) 87 | * [https://unpkg.com/qone@1.0.0/qone.min.js](https://unpkg.com/qone@1.0.0/qone.min.js) 88 | 89 | ## qone 关键字与运算符 90 | 91 | * from 92 | * in 93 | * where 94 | * select 95 | * orderby 96 | * desc 97 | * asc 98 | * groupby 99 | * limit 100 | 101 | 其中 from 和 in 一起使用,orderby 和 desc 或者 asc 一起使用。 102 | 103 | from 也可以把子属性作为 dataSource: 104 | 105 | ``` js 106 | from n in data.list 107 | ``` 108 | 109 | qone 支持下面三类运算符: 110 | 111 | * 括号:( ) 112 | * 比较运算符: = , > , < , >= , <= , != 113 | * 与或非: && , || , ! 114 | 115 | 条件判断语句也支持 null, undefined, true, false。 116 | 117 | 通过上面各种组合,你可以写出很复杂的查询条件。比如: 118 | 119 | ``` js 120 | qone({ list }).query(` 121 | from n in list 122 | where !(n.age > 17 || n.age < 2) && n.name != 'dntzhang' 123 | select n 124 | `) 125 | ``` 126 | 127 | 也支持 bool 类型的查询: 128 | 129 | ``` js 130 | var list = [ 131 | { name: 'qone', age: 1, isBaby: true }, 132 | { name: 'linq', age: 18 }, 133 | { name: 'dntzhang', age: 28 }] 134 | 135 | var result = qone({ list }).query(` 136 | from a in list 137 | where a.isBaby && n.name = 'qone' 138 | select a 139 | `) 140 | 141 | assert.deepEqual(result, [{ name: 'qone', age: 1, isBaby: true }]) 142 | ``` 143 | 144 | 其中 isBaby 是 bool 类型,同样的写法: 145 | a.isBaby = true (等同于: a.isBaby) 146 | a.isBaby = false (等同于: !a.isBaby) 147 | 148 | ## qone 方法注入 149 | 150 | 通过上面介绍发现 qone 不支持加减乘除位求模运算?怎么才能图灵完备?方法注入搞定一切!如下所示: 151 | 152 | ``` js 153 | QUnit.test("Method test 8", function (assert) { 154 | var arr = [1, 2, 3, 4, 5] 155 | 156 | qone('square', function (num) { 157 | return num * num 158 | }) 159 | 160 | qone('sqrt', function (num) { 161 | return Math.sqrt(num) 162 | }) 163 | 164 | var result = qone({ arr }).query(` 165 | from n in arr 166 | where sqrt(n) >= 2 167 | select { squareValue : square(n) } 168 | `) 169 | 170 | assert.deepEqual(result, [{ "squareValue": 16 }, { "squareValue": 25 }]) 171 | }) 172 | ``` 173 | 174 | 方法也是支持多参数传入,所以可以写出任意的查询条件。其中select, where, orderby, groupby 语句都支持方法注入。 175 | 176 | ## qone select 输出 177 | 178 | 通过 select 可以输出各种格式和字段的数据: 179 | 180 | ``` js 181 | QUnit.test("Select JSON test", function (assert) { 182 | var list = [ 183 | { name: 'qone', age: 1 }, 184 | { name: 'linq', age: 18 }, 185 | { name: 'dntzhang', age: 28 } 186 | ] 187 | 188 | var result = qone({ list }).query(` 189 | from n in list 190 | where n.age < 20 191 | select {n.age, n.name} 192 | `) 193 | 194 | assert.deepEqual(result, [ 195 | { "age": 1 , "name": "qone" }, 196 | { "age": 18, "name": "linq" } 197 | ]) 198 | 199 | }) 200 | ``` 201 | 202 | 把所有场景列举一下: 203 | 204 | * `select n` 输出源 item 205 | * `select n.name` 输出一维表 206 | * `select n.name, n.age` 输出二维表 207 | * `select { n.age, n.name }` 缺省方式输出 JSON Array(key自动使用 age 和 name) 208 | * `select { a: n.age, b: n.name }` 指定 key 输出 JSON Array 209 | * `select { a: methodName(n.age), b: n.name }` 注入方法 210 | * `select methodName(n.age), n.name` 注入方法 211 | * `select methodName(n.age, n.name, 1, true, 'abc')` 注入方法并传递参数 212 | 213 | 214 | ## qone orderby 215 | 216 | 217 | ``` js 218 | var result = qone({ list }).query(` 219 | from n in list 220 | where n.age > 0 221 | orderby n.age asc, n.name desc 222 | select n 223 | `) 224 | 225 | ``` 226 | 227 | 如果没有标记 asc 或者 desc,使用默认排序 asc。 228 | 229 | ## qone groupby 230 | 231 | ``` js 232 | QUnit.test("Simple groupby test 1", function (assert) { 233 | var list = [ 234 | { name: 'qone', age: 1 }, 235 | { name: 'linq', age: 18 }, 236 | { name: 'dntzhang1', age: 28 }, 237 | { name: 'dntzhang2', age: 28 }, 238 | { name: 'dntzhang3', age: 29 } 239 | ] 240 | 241 | var result = qone({ list }).query(` 242 | from n in list 243 | where n.age > 18 244 | groupby n.age 245 | `) 246 | 247 | assert.deepEqual(result, [ 248 | [{ "name": "dntzhang1", "age": 28 }, { "name": "dntzhang2", "age": 28 }], 249 | [{ "name": "dntzhang3", "age": 29 }]]) 250 | 251 | }) 252 | ``` 253 | 254 | groupby 可以作为结束语句,不用跟着也不能跟着 select 语句,groupby 也可以支持方法注入。 255 | 256 | ## qone 多数据源 257 | 258 | ``` js 259 | QUnit.test("Multi datasource with props condition", function (assert) { 260 | 261 | var listA = [ 262 | { name: 'qone', age: 1 }, 263 | { name: 'linq', age: 18 }, 264 | { name: 'dntzhang', age: 28 }] 265 | 266 | 267 | var listB = [ 268 | { name: 'x', age: 11 }, 269 | { name: 'xx', age: 18 }, 270 | { name: 'xxx', age: 13 } 271 | ] 272 | 273 | var result = qone({ listA, listB }).query(` 274 | from a in listA 275 | from b in listB 276 | where a.age = b.age 277 | select a, b 278 | `) 279 | 280 | assert.deepEqual(result, [[{ "name": "linq", "age": 18 }, { "name": "xx", "age": 18 }]]) 281 | }) 282 | ``` 283 | 284 | 多数据源会产生笛卡儿积。 285 | 286 | ## qone 嵌套子数据源 287 | 288 | ``` js 289 | QUnit.test("Multi deep from test ", function (assert) { 290 | 291 | var list = [ 292 | { name: 'qone', age: 1, isBaby: true, colors: [{ xx: [1, 2, 3] }, { xx: [11, 2, 3] }] }, 293 | { name: 'linq', age: 18, colors: [{ xx: [100, 2, 3] }, { xx: [11, 2, 3] }] }, 294 | { name: 'dntzhang', age: 28, colors: [{ xx: [1, 2, 3] }, { xx: [11, 2, 3] }] }] 295 | 296 | var result = qone({ list }).query(` 297 | from a in list 298 | from c in a.colors 299 | from d in c.xx 300 | where d === 100 301 | select a.name, c,d 302 | `) 303 | 304 | assert.deepEqual(result, [["linq", { "xx": [100, 2, 3] }, 100]]) 305 | }) 306 | ``` 307 | 308 | 也可以和自身的数据源会产生笛卡儿积。 309 | 310 | ## qone limit 与分页查询 311 | 312 | 通过 limit 可以应付最常见的两种查询场景 - top N 和 分页。 313 | 314 | 查询top3: 315 | 316 | ``` js 317 | QUnit.test("Limit top 3", function (assert) { 318 | var list = [ 319 | { name: 'dntzhang1', age: 1 }, 320 | { name: 'dntzhang2', age: 2 }, 321 | { name: 'dntzhang3', age: 3 }, 322 | { name: 'dntzhang4', age: 4 }, 323 | { name: 'dntzhang5', age: 5 }, 324 | { name: 'dntzhang6', age: 6 }, 325 | { name: 'dntzhang7', age: 7 }, 326 | { name: 'dntzhang8', age: 8 }, 327 | { name: 'dntzhang9', age: 9 }, 328 | { name: 'dntzhang10', age: 10 } 329 | ] 330 | 331 | var pageIndex = 1, 332 | pageSize = 4 333 | var result = qone({ list }).query(` 334 | from n in list 335 | select n 336 | limit 0, 3 337 | `) 338 | 339 | 340 | assert.deepEqual(result, [ 341 | 342 | { name: 'dntzhang1', age: 1 }, 343 | { name: 'dntzhang2', age: 2 }, 344 | { name: 'dntzhang3', age: 3 } 345 | ]) 346 | 347 | }) 348 | ``` 349 | 350 | 分页查询: 351 | 352 | ``` js 353 | QUnit.test("Limit one page test", function (assert) { 354 | var list = [ 355 | { name: 'dntzhang1', age: 1 }, 356 | { name: 'dntzhang2', age: 2 }, 357 | { name: 'dntzhang3', age: 3 }, 358 | { name: 'dntzhang4', age: 4 }, 359 | { name: 'dntzhang5', age: 5 }, 360 | { name: 'dntzhang6', age: 6 }, 361 | { name: 'dntzhang7', age: 7 }, 362 | { name: 'dntzhang8', age: 8 }, 363 | { name: 'dntzhang9', age: 9 }, 364 | { name: 'dntzhang10', age: 10 } 365 | ] 366 | 367 | var pageIndex = 1, 368 | pageSize = 4 369 | var result = qone({ list }).query(` 370 | from n in list 371 | where n.age > 0 372 | select n 373 | limit ${pageIndex * pageSize}, ${pageSize} 374 | `) 375 | 376 | 377 | assert.deepEqual(result, [ 378 | { name: 'dntzhang5', age: 5 }, 379 | { name: 'dntzhang6', age: 6 }, 380 | { name: 'dntzhang7', age: 7 }, 381 | { name: 'dntzhang8', age: 8 }]) 382 | 383 | }) 384 | ``` 385 | 386 | ## star & fork & pr & repl & follow me 387 | 388 | * [https://github.com/dntzhang/qone](https://github.com/dntzhang/qone) 389 | * [https://dntzhang.github.io/qone](https://dntzhang.github.io/qone) 390 | * [https://github.com/dntzhang](https://github.com/dntzhang) 391 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | English | [简体中文](./README-CN.md) 3 | 4 | 5 | qone 6 | ============================== 7 | [![npm version](https://badge.fury.io/js/qone.svg)](https://www.npmjs.com/package/qone) 8 | 9 | .NET LINQ in javascript 10 | 11 | 12 | ## The reason 13 | 14 | Recently, it has just changed some bug of the Excel formula of the Tencent document, mainly modifying the parser of the formula. 15 | 16 | For example, how can the following script be transformed to JavaScript? 17 | 18 | ``` js 19 | = IF(SUM(J6:J7) + SUM(J6:J7) > 10, "A2 is foo", "A2 is not foo") 20 | ``` 21 | 22 | The implementation of formulas or scripting languages includes several main steps: 23 | 24 | Scanner > lexer > parser > ast > code string 25 | 26 | After getting code string, you can run (JIT) dynamically, such as using Eval in JS, Eval can retain context information, the disadvantage is that the execution code contains compiler code, and it is unsafe, and so on. 27 | After getting code string, you can also use the generated code string to run (AOT) directly, and the disadvantage is to rely on the build tool or editor plug-in to dynamically replace the source code. 28 | 29 | For example, wind supports JIT and AOT at the same time. Qone's thinking is similar to that above, but not exactly the same. Qone is as follows: 30 | 31 | Scanner > lexer > parser > ast > method(ast) 32 | 33 | The detailed principles will be written later. In general, the reasons are the work of the Tencent document formula, early kmdjs development (uglify2) and.NET development, so there is Qone. 34 | 35 | - [LINQ](#linq) 36 | - [qone install](#qone-install) 37 | - [qone keyword and operator](#qone-keyword-and-operator) 38 | - [qone method](#qone-method) 39 | - [qone select](#qone-select) 40 | - [qone orderby](#qone-orderby) 41 | - [qone groupby](#qone-groupby) 42 | - [qone multiple data source](#qone-multiple-data-source) 43 | - [qone subdata source](#qone-subdata-source) 44 | - [qone limit and pagination](#qone-limit-and-pagination) 45 | - [links](#star--fork--pr--repl--follow-me) 46 | --- 47 | 48 | ## LINQ 49 | 50 | LINQ (language integrated query) is an innovative function introduced in.NET Framework 3.5. In Visual Studio, you can write LINQ queries with Visual Basic or C# for the following data sources: the SQL Server database, XML document, ADO.NET data set, and enumerable Objects. 51 | 52 | Qone allows Web front-end engineers to use LINQ to Objects through js string or js template string, Objects is an array of JSON. Let me give you a simple example (Qone is much stronger than the following example): 53 | 54 | 55 | ``` js 56 | var list = [ 57 | { name: 'qone', age: 1 }, 58 | { name: 'linq', age: 18 }, 59 | { name: 'dntzhang', age: 28 } 60 | ] 61 | 62 | var result = qone({ list }).query(` 63 | from n in list 64 | where n.age > 18 65 | select n 66 | `) 67 | 68 | assert.deepEqual(result, [{ "name": "dntzhang", "age": 28 }]) 69 | ``` 70 | 71 | Qone's `from` keyword is also in front like LINQ, with intelligent prompts for the later statements,but qone is real-time compilation based on string, not the native syntax of javasript, so although from is written in the front but does not support intelligent hints. 72 | 73 | ## qone install 74 | 75 | ``` bash 76 | npm install qone 77 | ``` 78 | 79 | or get it by CDN: 80 | 81 | * [https://unpkg.com/qone@1.0.0/qone.js](https://unpkg.com/qone@1.0.0/qone.js) 82 | * [https://unpkg.com/qone@1.0.0/qone.min.js](https://unpkg.com/qone@1.0.0/qone.min.js) 83 | 84 | ## qone keyword and operator 85 | 86 | * from 87 | * in 88 | * where 89 | * select 90 | * orderby 91 | * desc 92 | * asc 93 | * groupby 94 | * limit 95 | 96 | from and in are used together, orderby and desc or asc are used together. 97 | 98 | from can also take the sub attributes as dataSource: 99 | 100 | ``` js 101 | from n in data.list 102 | ``` 103 | 104 | Qone supports the following three class operators: 105 | 106 | * brackets:( ) 107 | * comparison operator: = , > , < , >= , <= , != 108 | * and or not: && , || , ! 109 | 110 | Conditional judgement also supports null, undefined, true, false. 111 | 112 | Through the above combinations, you can write complex query conditions. For example: 113 | 114 | ``` js 115 | qone({ list }).query(` 116 | from n in list 117 | where !(n.age > 17 || n.age < 2) && n.name != 'dntzhang' 118 | select n 119 | `) 120 | ``` 121 | 122 | The bool type of query is also supported: 123 | 124 | ``` js 125 | var list = [ 126 | { name: 'qone', age: 1, isBaby: true }, 127 | { name: 'linq', age: 18 }, 128 | { name: 'dntzhang', age: 28 }] 129 | 130 | var result = qone({ list }).query(` 131 | from a in list 132 | where a.isBaby && n.name = 'qone' 133 | select a 134 | `) 135 | 136 | assert.deepEqual(result, [{ name: 'qone', age: 1, isBaby: true }]) 137 | ``` 138 | 139 | isBaby is the bool type, and the same way: 140 | a.isBaby = true (equivalent to: a.isBaby) 141 | a.isBaby = false (equivalent to: !a.isBaby) 142 | 143 | ## qone method 144 | 145 | From the above, it is found that QOne does not support arithmetic operations of addition, subtraction, multiplication and division. How can be turing-complete ? Method injection to do everything! As shown below: 146 | 147 | ``` js 148 | QUnit.test("Method test 8", function (assert) { 149 | var arr = [1, 2, 3, 4, 5] 150 | 151 | qone('square', function (num) { 152 | return num * num 153 | }) 154 | 155 | qone('sqrt', function (num) { 156 | return Math.sqrt(num) 157 | }) 158 | 159 | var result = qone({ arr }).query(` 160 | from n in arr 161 | where sqrt(n) >= 2 162 | select { squareValue : square(n) } 163 | `) 164 | 165 | assert.deepEqual(result, [{ "squareValue": 16 }, { "squareValue": 25 }]) 166 | }) 167 | ``` 168 | 169 | The method also supports the introduction of multiple parameters, so it can write arbitrary query conditions. select, where, orderby, groupby statements support method injection. 170 | 171 | ## qone select 172 | 173 | select can output data of various formats and fields: 174 | 175 | ``` js 176 | QUnit.test("Select JSON test", function (assert) { 177 | var list = [ 178 | { name: 'qone', age: 1 }, 179 | { name: 'linq', age: 18 }, 180 | { name: 'dntzhang', age: 28 } 181 | ] 182 | 183 | var result = qone({ list }).query(` 184 | from n in list 185 | where n.age < 20 186 | select {n.age, n.name} 187 | `) 188 | 189 | assert.deepEqual(result, [ 190 | { "age": 1 , "name": "qone" }, 191 | { "age": 18, "name": "linq" } 192 | ]) 193 | 194 | }) 195 | ``` 196 | 197 | List all the scenes: 198 | 199 | * `select n` Output source item 200 | * `select n.name` Output one-dimensional table 201 | * `select n.name, n.age` Output two-dimensional table 202 | * `select { n.age, n.name }` Output JSON Array by default (key automatically uses age and name). 203 | * `select { a: n.age, b: n.name }` Specify the key output JSON Array 204 | * `select { a: methodName(n.age), b: n.name }` Injection method 205 | * `select methodName(n.age), n.name` Injection method 206 | * `select methodName(n.age, n.name, 1, true, 'abc')` Injection method and transfer parameters 207 | 208 | 209 | 210 | ## qone orderby 211 | 212 | 213 | ``` js 214 | var result = qone({ list }).query(` 215 | from n in list 216 | where n.age > 0 217 | orderby n.age asc, n.name desc 218 | select n 219 | `) 220 | 221 | ``` 222 | 223 | If no asc or desc is marked, use the default sort asc. 224 | 225 | ## qone groupby 226 | 227 | ``` js 228 | QUnit.test("Simple groupby test 1", function (assert) { 229 | var list = [ 230 | { name: 'qone', age: 1 }, 231 | { name: 'linq', age: 18 }, 232 | { name: 'dntzhang1', age: 28 }, 233 | { name: 'dntzhang2', age: 28 }, 234 | { name: 'dntzhang3', age: 29 } 235 | ] 236 | 237 | var result = qone({ list }).query(` 238 | from n in list 239 | where n.age > 18 240 | groupby n.age 241 | `) 242 | 243 | assert.deepEqual(result, [ 244 | [{ "name": "dntzhang1", "age": 28 }, { "name": "dntzhang2", "age": 28 }], 245 | [{ "name": "dntzhang3", "age": 29 }]]) 246 | 247 | }) 248 | ``` 249 | 250 | groupby can be used as an ending statement, without following or following select statements. groupby can also support method injection. 251 | 252 | ## qone multiple data source 253 | 254 | ``` js 255 | QUnit.test("Multi datasource with props condition", function (assert) { 256 | 257 | var listA = [ 258 | { name: 'qone', age: 1 }, 259 | { name: 'linq', age: 18 }, 260 | { name: 'dntzhang', age: 28 }] 261 | 262 | 263 | var listB = [ 264 | { name: 'x', age: 11 }, 265 | { name: 'xx', age: 18 }, 266 | { name: 'xxx', age: 13 } 267 | ] 268 | 269 | var result = qone({ listA, listB }).query(` 270 | from a in listA 271 | from b in listB 272 | where a.age = b.age 273 | select a, b 274 | `) 275 | 276 | assert.deepEqual(result, [[{ "name": "linq", "age": 18 }, { "name": "xx", "age": 18 }]]) 277 | }) 278 | ``` 279 | 280 | The multi data source produces Cartesian product. 281 | 282 | ## qone subdata source 283 | 284 | ``` js 285 | QUnit.test("Multi deep from test ", function (assert) { 286 | 287 | var list = [ 288 | { name: 'qone', age: 1, isBaby: true, colors: [{ xx: [1, 2, 3] }, { xx: [11, 2, 3] }] }, 289 | { name: 'linq', age: 18, colors: [{ xx: [100, 2, 3] }, { xx: [11, 2, 3] }] }, 290 | { name: 'dntzhang', age: 28, colors: [{ xx: [1, 2, 3] }, { xx: [11, 2, 3] }] }] 291 | 292 | var result = qone({ list }).query(` 293 | from a in list 294 | from c in a.colors 295 | from d in c.xx 296 | where d === 100 297 | select a.name, c,d 298 | `) 299 | 300 | assert.deepEqual(result, [["linq", { "xx": [100, 2, 3] }, 100]]) 301 | }) 302 | ``` 303 | 304 | It can also produce Cartesian product with its own data source. 305 | 306 | ## qone limit and pagination 307 | 308 | Limit can cope with the two most common query scenarios - top N and paging. 309 | 310 | Query top3: 311 | 312 | ``` js 313 | QUnit.test("Limit top 3", function (assert) { 314 | var list = [ 315 | { name: 'dntzhang1', age: 1 }, 316 | { name: 'dntzhang2', age: 2 }, 317 | { name: 'dntzhang3', age: 3 }, 318 | { name: 'dntzhang4', age: 4 }, 319 | { name: 'dntzhang5', age: 5 }, 320 | { name: 'dntzhang6', age: 6 }, 321 | { name: 'dntzhang7', age: 7 }, 322 | { name: 'dntzhang8', age: 8 }, 323 | { name: 'dntzhang9', age: 9 }, 324 | { name: 'dntzhang10', age: 10 } 325 | ] 326 | 327 | var pageIndex = 1, 328 | pageSize = 4 329 | var result = qone({ list }).query(` 330 | from n in list 331 | select n 332 | limit 0, 3 333 | `) 334 | 335 | 336 | assert.deepEqual(result, [ 337 | 338 | { name: 'dntzhang1', age: 1 }, 339 | { name: 'dntzhang2', age: 2 }, 340 | { name: 'dntzhang3', age: 3 } 341 | ]) 342 | 343 | }) 344 | ``` 345 | 346 | Paging query: 347 | 348 | ``` js 349 | QUnit.test("Limit one page test", function (assert) { 350 | var list = [ 351 | { name: 'dntzhang1', age: 1 }, 352 | { name: 'dntzhang2', age: 2 }, 353 | { name: 'dntzhang3', age: 3 }, 354 | { name: 'dntzhang4', age: 4 }, 355 | { name: 'dntzhang5', age: 5 }, 356 | { name: 'dntzhang6', age: 6 }, 357 | { name: 'dntzhang7', age: 7 }, 358 | { name: 'dntzhang8', age: 8 }, 359 | { name: 'dntzhang9', age: 9 }, 360 | { name: 'dntzhang10', age: 10 } 361 | ] 362 | 363 | var pageIndex = 1, 364 | pageSize = 4 365 | var result = qone({ list }).query(` 366 | from n in list 367 | where n.age > 0 368 | select n 369 | limit ${pageIndex * pageSize}, ${pageSize} 370 | `) 371 | 372 | 373 | assert.deepEqual(result, [ 374 | { name: 'dntzhang5', age: 5 }, 375 | { name: 'dntzhang6', age: 6 }, 376 | { name: 'dntzhang7', age: 7 }, 377 | { name: 'dntzhang8', age: 8 }]) 378 | 379 | }) 380 | ``` 381 | 382 | ## star & fork & pr & repl & follow me 383 | 384 | * [https://github.com/dntzhang/qone](https://github.com/dntzhang/qone) 385 | * [https://dntzhang.github.io/qone](https://dntzhang.github.io/qone) 386 | * [https://github.com/dntzhang](https://github.com/dntzhang) 387 | -------------------------------------------------------------------------------- /asset/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | height: 300px; 7 | color: black; 8 | direction: ltr; 9 | } 10 | 11 | /* PADDING */ 12 | 13 | .CodeMirror-lines { 14 | padding: 4px 0; /* Vertical padding around content */ 15 | } 16 | .CodeMirror pre { 17 | padding: 0 4px; /* Horizontal padding of content */ 18 | } 19 | 20 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 21 | background-color: white; /* The little square between H and V scrollbars */ 22 | } 23 | 24 | /* GUTTER */ 25 | 26 | .CodeMirror-gutters { 27 | border-right: 1px solid #ddd; 28 | background-color: #f7f7f7; 29 | white-space: nowrap; 30 | } 31 | .CodeMirror-linenumbers {} 32 | .CodeMirror-linenumber { 33 | padding: 0 3px 0 5px; 34 | min-width: 20px; 35 | text-align: right; 36 | color: #999; 37 | white-space: nowrap; 38 | } 39 | 40 | .CodeMirror-guttermarker { color: black; } 41 | .CodeMirror-guttermarker-subtle { color: #999; } 42 | 43 | /* CURSOR */ 44 | 45 | .CodeMirror-cursor { 46 | border-left: 1px solid black; 47 | border-right: none; 48 | width: 0; 49 | } 50 | /* Shown when moving in bi-directional text */ 51 | .CodeMirror div.CodeMirror-secondarycursor { 52 | border-left: 1px solid silver; 53 | } 54 | .cm-fat-cursor .CodeMirror-cursor { 55 | width: auto; 56 | border: 0 !important; 57 | background: #7e7; 58 | } 59 | .cm-fat-cursor div.CodeMirror-cursors { 60 | z-index: 1; 61 | } 62 | .cm-fat-cursor-mark { 63 | background-color: rgba(20, 255, 20, 0.5); 64 | -webkit-animation: blink 1.06s steps(1) infinite; 65 | -moz-animation: blink 1.06s steps(1) infinite; 66 | animation: blink 1.06s steps(1) infinite; 67 | } 68 | .cm-animate-fat-cursor { 69 | width: auto; 70 | border: 0; 71 | -webkit-animation: blink 1.06s steps(1) infinite; 72 | -moz-animation: blink 1.06s steps(1) infinite; 73 | animation: blink 1.06s steps(1) infinite; 74 | background-color: #7e7; 75 | } 76 | @-moz-keyframes blink { 77 | 0% {} 78 | 50% { background-color: transparent; } 79 | 100% {} 80 | } 81 | @-webkit-keyframes blink { 82 | 0% {} 83 | 50% { background-color: transparent; } 84 | 100% {} 85 | } 86 | @keyframes blink { 87 | 0% {} 88 | 50% { background-color: transparent; } 89 | 100% {} 90 | } 91 | 92 | /* Can style cursor different in overwrite (non-insert) mode */ 93 | .CodeMirror-overwrite .CodeMirror-cursor {} 94 | 95 | .cm-tab { display: inline-block; text-decoration: inherit; } 96 | 97 | .CodeMirror-rulers { 98 | position: absolute; 99 | left: 0; right: 0; top: -50px; bottom: -20px; 100 | overflow: hidden; 101 | } 102 | .CodeMirror-ruler { 103 | border-left: 1px solid #ccc; 104 | top: 0; bottom: 0; 105 | position: absolute; 106 | } 107 | 108 | /* DEFAULT THEME */ 109 | 110 | .cm-s-default .cm-header {color: blue;} 111 | .cm-s-default .cm-quote {color: #090;} 112 | .cm-negative {color: #d44;} 113 | .cm-positive {color: #292;} 114 | .cm-header, .cm-strong {font-weight: bold;} 115 | .cm-em {font-style: italic;} 116 | .cm-link {text-decoration: underline;} 117 | .cm-strikethrough {text-decoration: line-through;} 118 | 119 | .cm-s-default .cm-keyword {color: #708;} 120 | .cm-s-default .cm-atom {color: #219;} 121 | .cm-s-default .cm-number {color: #164;} 122 | .cm-s-default .cm-def {color: #00f;} 123 | .cm-s-default .cm-variable, 124 | .cm-s-default .cm-punctuation, 125 | .cm-s-default .cm-property, 126 | .cm-s-default .cm-operator {} 127 | .cm-s-default .cm-variable-2 {color: #05a;} 128 | .cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} 129 | .cm-s-default .cm-comment {color: #a50;} 130 | .cm-s-default .cm-string {color: #a11;} 131 | .cm-s-default .cm-string-2 {color: #f50;} 132 | .cm-s-default .cm-meta {color: #555;} 133 | .cm-s-default .cm-qualifier {color: #555;} 134 | .cm-s-default .cm-builtin {color: #30a;} 135 | .cm-s-default .cm-bracket {color: #997;} 136 | .cm-s-default .cm-tag {color: #170;} 137 | .cm-s-default .cm-attribute {color: #00c;} 138 | .cm-s-default .cm-hr {color: #999;} 139 | .cm-s-default .cm-link {color: #00c;} 140 | 141 | .cm-s-default .cm-error {color: #f00;} 142 | .cm-invalidchar {color: #f00;} 143 | 144 | .CodeMirror-composing { border-bottom: 2px solid; } 145 | 146 | /* Default styles for common addons */ 147 | 148 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;} 149 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} 150 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 151 | .CodeMirror-activeline-background {background: #e8f2ff;} 152 | 153 | /* STOP */ 154 | 155 | /* The rest of this file contains styles related to the mechanics of 156 | the editor. You probably shouldn't touch them. */ 157 | 158 | .CodeMirror { 159 | position: relative; 160 | overflow: hidden; 161 | background: white; 162 | } 163 | 164 | .CodeMirror-scroll { 165 | overflow: scroll !important; /* Things will break if this is overridden */ 166 | /* 30px is the magic margin used to hide the element's real scrollbars */ 167 | /* See overflow: hidden in .CodeMirror */ 168 | margin-bottom: -30px; margin-right: -30px; 169 | padding-bottom: 30px; 170 | height: 100%; 171 | outline: none; /* Prevent dragging from highlighting the element */ 172 | position: relative; 173 | } 174 | .CodeMirror-sizer { 175 | position: relative; 176 | border-right: 30px solid transparent; 177 | } 178 | 179 | /* The fake, visible scrollbars. Used to force redraw during scrolling 180 | before actual scrolling happens, thus preventing shaking and 181 | flickering artifacts. */ 182 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 183 | position: absolute; 184 | z-index: 6; 185 | display: none; 186 | } 187 | .CodeMirror-vscrollbar { 188 | right: 0; top: 0; 189 | overflow-x: hidden; 190 | overflow-y: scroll; 191 | } 192 | .CodeMirror-hscrollbar { 193 | bottom: 0; left: 0; 194 | overflow-y: hidden; 195 | overflow-x: scroll; 196 | } 197 | .CodeMirror-scrollbar-filler { 198 | right: 0; bottom: 0; 199 | } 200 | .CodeMirror-gutter-filler { 201 | left: 0; bottom: 0; 202 | } 203 | 204 | .CodeMirror-gutters { 205 | position: absolute; left: 0; top: 0; 206 | min-height: 100%; 207 | z-index: 3; 208 | } 209 | .CodeMirror-gutter { 210 | white-space: normal; 211 | height: 100%; 212 | display: inline-block; 213 | vertical-align: top; 214 | margin-bottom: -30px; 215 | } 216 | .CodeMirror-gutter-wrapper { 217 | position: absolute; 218 | z-index: 4; 219 | background: none !important; 220 | border: none !important; 221 | } 222 | .CodeMirror-gutter-background { 223 | position: absolute; 224 | top: 0; bottom: 0; 225 | z-index: 4; 226 | } 227 | .CodeMirror-gutter-elt { 228 | position: absolute; 229 | cursor: default; 230 | z-index: 4; 231 | } 232 | .CodeMirror-gutter-wrapper ::selection { background-color: transparent } 233 | .CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } 234 | 235 | .CodeMirror-lines { 236 | cursor: text; 237 | min-height: 1px; /* prevents collapsing before first draw */ 238 | } 239 | .CodeMirror pre { 240 | /* Reset some styles that the rest of the page might have set */ 241 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 242 | border-width: 0; 243 | background: transparent; 244 | font-family: inherit; 245 | font-size: inherit; 246 | margin: 0; 247 | white-space: pre; 248 | word-wrap: normal; 249 | line-height: inherit; 250 | color: inherit; 251 | z-index: 2; 252 | position: relative; 253 | overflow: visible; 254 | -webkit-tap-highlight-color: transparent; 255 | -webkit-font-variant-ligatures: contextual; 256 | font-variant-ligatures: contextual; 257 | } 258 | .CodeMirror-wrap pre { 259 | word-wrap: break-word; 260 | white-space: pre-wrap; 261 | word-break: normal; 262 | } 263 | 264 | .CodeMirror-linebackground { 265 | position: absolute; 266 | left: 0; right: 0; top: 0; bottom: 0; 267 | z-index: 0; 268 | } 269 | 270 | .CodeMirror-linewidget { 271 | position: relative; 272 | z-index: 2; 273 | padding: 0.1px; /* Force widget margins to stay inside of the container */ 274 | } 275 | 276 | .CodeMirror-widget {} 277 | 278 | .CodeMirror-rtl pre { direction: rtl; } 279 | 280 | .CodeMirror-code { 281 | outline: none; 282 | } 283 | 284 | /* Force content-box sizing for the elements where we expect it */ 285 | .CodeMirror-scroll, 286 | .CodeMirror-sizer, 287 | .CodeMirror-gutter, 288 | .CodeMirror-gutters, 289 | .CodeMirror-linenumber { 290 | -moz-box-sizing: content-box; 291 | box-sizing: content-box; 292 | } 293 | 294 | .CodeMirror-measure { 295 | position: absolute; 296 | width: 100%; 297 | height: 0; 298 | overflow: hidden; 299 | visibility: hidden; 300 | } 301 | 302 | .CodeMirror-cursor { 303 | position: absolute; 304 | pointer-events: none; 305 | } 306 | .CodeMirror-measure pre { position: static; } 307 | 308 | div.CodeMirror-cursors { 309 | visibility: hidden; 310 | position: relative; 311 | z-index: 3; 312 | } 313 | div.CodeMirror-dragcursors { 314 | visibility: visible; 315 | } 316 | 317 | .CodeMirror-focused div.CodeMirror-cursors { 318 | visibility: visible; 319 | } 320 | 321 | .CodeMirror-selected { background: #d9d9d9; } 322 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 323 | .CodeMirror-crosshair { cursor: crosshair; } 324 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } 325 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } 326 | 327 | .cm-searching { 328 | background-color: #ffa; 329 | background-color: rgba(255, 255, 0, .4); 330 | } 331 | 332 | /* Used to force a border model for a node */ 333 | .cm-force-border { padding-right: .1px; } 334 | 335 | @media print { 336 | /* Hide the cursor when printing */ 337 | .CodeMirror div.CodeMirror-cursors { 338 | visibility: hidden; 339 | } 340 | } 341 | 342 | /* See issue #2901 */ 343 | .cm-tab-wrap-hack:after { content: ''; } 344 | 345 | /* Help users use markselection to safely style text background */ 346 | span.CodeMirror-selectedtext { background: none; } 347 | -------------------------------------------------------------------------------- /asset/qone.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dntzhang/qone/5c6dab86edb09a15d163796b43c0ea1f293f9dd6/asset/qone.ico -------------------------------------------------------------------------------- /asset/qone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dntzhang/qone/5c6dab86edb09a15d163796b43c0ea1f293f9dd6/asset/qone.png -------------------------------------------------------------------------------- /asset/show-hint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | var HINT_ELEMENT_CLASS = "CodeMirror-hint"; 15 | var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active"; 16 | 17 | // This is the old interface, kept around for now to stay 18 | // backwards-compatible. 19 | CodeMirror.showHint = function(cm, getHints, options) { 20 | if (!getHints) return cm.showHint(options); 21 | if (options && options.async) getHints.async = true; 22 | var newOpts = {hint: getHints}; 23 | if (options) for (var prop in options) newOpts[prop] = options[prop]; 24 | return cm.showHint(newOpts); 25 | }; 26 | 27 | CodeMirror.defineExtension("showHint", function(options) { 28 | options = parseOptions(this, this.getCursor("start"), options); 29 | var selections = this.listSelections() 30 | if (selections.length > 1) return; 31 | // By default, don't allow completion when something is selected. 32 | // A hint function can have a `supportsSelection` property to 33 | // indicate that it can handle selections. 34 | if (this.somethingSelected()) { 35 | if (!options.hint.supportsSelection) return; 36 | // Don't try with cross-line selections 37 | for (var i = 0; i < selections.length; i++) 38 | if (selections[i].head.line != selections[i].anchor.line) return; 39 | } 40 | 41 | if (this.state.completionActive) this.state.completionActive.close(); 42 | var completion = this.state.completionActive = new Completion(this, options); 43 | if (!completion.options.hint) return; 44 | 45 | CodeMirror.signal(this, "startCompletion", this); 46 | completion.update(true); 47 | }); 48 | 49 | function Completion(cm, options) { 50 | this.cm = cm; 51 | this.options = options; 52 | this.widget = null; 53 | this.debounce = 0; 54 | this.tick = 0; 55 | this.startPos = this.cm.getCursor("start"); 56 | this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length; 57 | 58 | var self = this; 59 | cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); }); 60 | } 61 | 62 | var requestAnimationFrame = window.requestAnimationFrame || function(fn) { 63 | return setTimeout(fn, 1000/60); 64 | }; 65 | var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout; 66 | 67 | Completion.prototype = { 68 | close: function() { 69 | if (!this.active()) return; 70 | this.cm.state.completionActive = null; 71 | this.tick = null; 72 | this.cm.off("cursorActivity", this.activityFunc); 73 | 74 | if (this.widget && this.data) CodeMirror.signal(this.data, "close"); 75 | if (this.widget) this.widget.close(); 76 | CodeMirror.signal(this.cm, "endCompletion", this.cm); 77 | }, 78 | 79 | active: function() { 80 | return this.cm.state.completionActive == this; 81 | }, 82 | 83 | pick: function(data, i) { 84 | var completion = data.list[i]; 85 | if (completion.hint) completion.hint(this.cm, data, completion); 86 | else this.cm.replaceRange(getText(completion), completion.from || data.from, 87 | completion.to || data.to, "complete"); 88 | CodeMirror.signal(data, "pick", completion); 89 | this.close(); 90 | }, 91 | 92 | cursorActivity: function() { 93 | if (this.debounce) { 94 | cancelAnimationFrame(this.debounce); 95 | this.debounce = 0; 96 | } 97 | 98 | var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line); 99 | if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch || 100 | pos.ch < this.startPos.ch || this.cm.somethingSelected() || 101 | (pos.ch && this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) { 102 | this.close(); 103 | } else { 104 | var self = this; 105 | this.debounce = requestAnimationFrame(function() {self.update();}); 106 | if (this.widget) this.widget.disable(); 107 | } 108 | }, 109 | 110 | update: function(first) { 111 | if (this.tick == null) return 112 | var self = this, myTick = ++this.tick 113 | fetchHints(this.options.hint, this.cm, this.options, function(data) { 114 | if (self.tick == myTick) self.finishUpdate(data, first) 115 | }) 116 | }, 117 | 118 | finishUpdate: function(data, first) { 119 | if (this.data) CodeMirror.signal(this.data, "update"); 120 | 121 | var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle); 122 | if (this.widget) this.widget.close(); 123 | 124 | this.data = data; 125 | 126 | if (data && data.list.length) { 127 | if (picked && data.list.length == 1) { 128 | this.pick(data, 0); 129 | } else { 130 | this.widget = new Widget(this, data); 131 | CodeMirror.signal(data, "shown"); 132 | } 133 | } 134 | } 135 | }; 136 | 137 | function parseOptions(cm, pos, options) { 138 | var editor = cm.options.hintOptions; 139 | var out = {}; 140 | for (var prop in defaultOptions) out[prop] = defaultOptions[prop]; 141 | if (editor) for (var prop in editor) 142 | if (editor[prop] !== undefined) out[prop] = editor[prop]; 143 | if (options) for (var prop in options) 144 | if (options[prop] !== undefined) out[prop] = options[prop]; 145 | if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos) 146 | return out; 147 | } 148 | 149 | function getText(completion) { 150 | if (typeof completion == "string") return completion; 151 | else return completion.text; 152 | } 153 | 154 | function buildKeyMap(completion, handle) { 155 | var baseMap = { 156 | Up: function() {handle.moveFocus(-1);}, 157 | Down: function() {handle.moveFocus(1);}, 158 | PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);}, 159 | PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);}, 160 | Home: function() {handle.setFocus(0);}, 161 | End: function() {handle.setFocus(handle.length - 1);}, 162 | Enter: handle.pick, 163 | Tab: handle.pick, 164 | Esc: handle.close 165 | }; 166 | var custom = completion.options.customKeys; 167 | var ourMap = custom ? {} : baseMap; 168 | function addBinding(key, val) { 169 | var bound; 170 | if (typeof val != "string") 171 | bound = function(cm) { return val(cm, handle); }; 172 | // This mechanism is deprecated 173 | else if (baseMap.hasOwnProperty(val)) 174 | bound = baseMap[val]; 175 | else 176 | bound = val; 177 | ourMap[key] = bound; 178 | } 179 | if (custom) 180 | for (var key in custom) if (custom.hasOwnProperty(key)) 181 | addBinding(key, custom[key]); 182 | var extra = completion.options.extraKeys; 183 | if (extra) 184 | for (var key in extra) if (extra.hasOwnProperty(key)) 185 | addBinding(key, extra[key]); 186 | return ourMap; 187 | } 188 | 189 | function getHintElement(hintsElement, el) { 190 | while (el && el != hintsElement) { 191 | if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el; 192 | el = el.parentNode; 193 | } 194 | } 195 | 196 | function Widget(completion, data) { 197 | this.completion = completion; 198 | this.data = data; 199 | this.picked = false; 200 | var widget = this, cm = completion.cm; 201 | 202 | var hints = this.hints = document.createElement("ul"); 203 | hints.className = "CodeMirror-hints"; 204 | this.selectedHint = data.selectedHint || 0; 205 | 206 | var completions = data.list; 207 | for (var i = 0; i < completions.length; ++i) { 208 | var elt = hints.appendChild(document.createElement("li")), cur = completions[i]; 209 | var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS); 210 | if (cur.className != null) className = cur.className + " " + className; 211 | elt.className = className; 212 | if (cur.render) cur.render(elt, data, cur); 213 | else elt.appendChild(document.createTextNode(cur.displayText || getText(cur))); 214 | elt.hintId = i; 215 | } 216 | 217 | var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null); 218 | var left = pos.left, top = pos.bottom, below = true; 219 | hints.style.left = left + "px"; 220 | hints.style.top = top + "px"; 221 | // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. 222 | var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth); 223 | var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight); 224 | (completion.options.container || document.body).appendChild(hints); 225 | var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH; 226 | var scrolls = hints.scrollHeight > hints.clientHeight + 1 227 | var startScroll = cm.getScrollInfo(); 228 | 229 | if (overlapY > 0) { 230 | var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top); 231 | if (curTop - height > 0) { // Fits above cursor 232 | hints.style.top = (top = pos.top - height) + "px"; 233 | below = false; 234 | } else if (height > winH) { 235 | hints.style.height = (winH - 5) + "px"; 236 | hints.style.top = (top = pos.bottom - box.top) + "px"; 237 | var cursor = cm.getCursor(); 238 | if (data.from.ch != cursor.ch) { 239 | pos = cm.cursorCoords(cursor); 240 | hints.style.left = (left = pos.left) + "px"; 241 | box = hints.getBoundingClientRect(); 242 | } 243 | } 244 | } 245 | var overlapX = box.right - winW; 246 | if (overlapX > 0) { 247 | if (box.right - box.left > winW) { 248 | hints.style.width = (winW - 5) + "px"; 249 | overlapX -= (box.right - box.left) - winW; 250 | } 251 | hints.style.left = (left = pos.left - overlapX) + "px"; 252 | } 253 | if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling) 254 | node.style.paddingRight = cm.display.nativeBarWidth + "px" 255 | 256 | cm.addKeyMap(this.keyMap = buildKeyMap(completion, { 257 | moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); }, 258 | setFocus: function(n) { widget.changeActive(n); }, 259 | menuSize: function() { return widget.screenAmount(); }, 260 | length: completions.length, 261 | close: function() { completion.close(); }, 262 | pick: function() { widget.pick(); }, 263 | data: data 264 | })); 265 | 266 | if (completion.options.closeOnUnfocus) { 267 | var closingOnBlur; 268 | cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); }); 269 | cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); }); 270 | } 271 | 272 | cm.on("scroll", this.onScroll = function() { 273 | var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect(); 274 | var newTop = top + startScroll.top - curScroll.top; 275 | var point = newTop - (window.pageYOffset || (document.documentElement || document.body).scrollTop); 276 | if (!below) point += hints.offsetHeight; 277 | if (point <= editor.top || point >= editor.bottom) return completion.close(); 278 | hints.style.top = newTop + "px"; 279 | hints.style.left = (left + startScroll.left - curScroll.left) + "px"; 280 | }); 281 | 282 | CodeMirror.on(hints, "dblclick", function(e) { 283 | var t = getHintElement(hints, e.target || e.srcElement); 284 | if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();} 285 | }); 286 | 287 | CodeMirror.on(hints, "click", function(e) { 288 | var t = getHintElement(hints, e.target || e.srcElement); 289 | if (t && t.hintId != null) { 290 | widget.changeActive(t.hintId); 291 | if (completion.options.completeOnSingleClick) widget.pick(); 292 | } 293 | }); 294 | 295 | CodeMirror.on(hints, "mousedown", function() { 296 | setTimeout(function(){cm.focus();}, 20); 297 | }); 298 | 299 | CodeMirror.signal(data, "select", completions[this.selectedHint], hints.childNodes[this.selectedHint]); 300 | return true; 301 | } 302 | 303 | Widget.prototype = { 304 | close: function() { 305 | if (this.completion.widget != this) return; 306 | this.completion.widget = null; 307 | this.hints.parentNode.removeChild(this.hints); 308 | this.completion.cm.removeKeyMap(this.keyMap); 309 | 310 | var cm = this.completion.cm; 311 | if (this.completion.options.closeOnUnfocus) { 312 | cm.off("blur", this.onBlur); 313 | cm.off("focus", this.onFocus); 314 | } 315 | cm.off("scroll", this.onScroll); 316 | }, 317 | 318 | disable: function() { 319 | this.completion.cm.removeKeyMap(this.keyMap); 320 | var widget = this; 321 | this.keyMap = {Enter: function() { widget.picked = true; }}; 322 | this.completion.cm.addKeyMap(this.keyMap); 323 | }, 324 | 325 | pick: function() { 326 | this.completion.pick(this.data, this.selectedHint); 327 | }, 328 | 329 | changeActive: function(i, avoidWrap) { 330 | if (i >= this.data.list.length) 331 | i = avoidWrap ? this.data.list.length - 1 : 0; 332 | else if (i < 0) 333 | i = avoidWrap ? 0 : this.data.list.length - 1; 334 | if (this.selectedHint == i) return; 335 | var node = this.hints.childNodes[this.selectedHint]; 336 | node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, ""); 337 | node = this.hints.childNodes[this.selectedHint = i]; 338 | node.className += " " + ACTIVE_HINT_ELEMENT_CLASS; 339 | if (node.offsetTop < this.hints.scrollTop) 340 | this.hints.scrollTop = node.offsetTop - 3; 341 | else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight) 342 | this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3; 343 | CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node); 344 | }, 345 | 346 | screenAmount: function() { 347 | return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1; 348 | } 349 | }; 350 | 351 | function applicableHelpers(cm, helpers) { 352 | if (!cm.somethingSelected()) return helpers 353 | var result = [] 354 | for (var i = 0; i < helpers.length; i++) 355 | if (helpers[i].supportsSelection) result.push(helpers[i]) 356 | return result 357 | } 358 | 359 | function fetchHints(hint, cm, options, callback) { 360 | if (hint.async) { 361 | hint(cm, callback, options) 362 | } else { 363 | var result = hint(cm, options) 364 | if (result && result.then) result.then(callback) 365 | else callback(result) 366 | } 367 | } 368 | 369 | function resolveAutoHints(cm, pos) { 370 | var helpers = cm.getHelpers(pos, "hint"), words 371 | if (helpers.length) { 372 | var resolved = function(cm, callback, options) { 373 | var app = applicableHelpers(cm, helpers); 374 | function run(i) { 375 | if (i == app.length) return callback(null) 376 | fetchHints(app[i], cm, options, function(result) { 377 | if (result && result.list.length > 0) callback(result) 378 | else run(i + 1) 379 | }) 380 | } 381 | run(0) 382 | } 383 | resolved.async = true 384 | resolved.supportsSelection = true 385 | return resolved 386 | } else if (words = cm.getHelper(cm.getCursor(), "hintWords")) { 387 | return function(cm) { return CodeMirror.hint.fromList(cm, {words: words}) } 388 | } else if (CodeMirror.hint.anyword) { 389 | return function(cm, options) { return CodeMirror.hint.anyword(cm, options) } 390 | } else { 391 | return function() {} 392 | } 393 | } 394 | 395 | CodeMirror.registerHelper("hint", "auto", { 396 | resolve: resolveAutoHints 397 | }); 398 | 399 | CodeMirror.registerHelper("hint", "fromList", function(cm, options) { 400 | var cur = cm.getCursor(), token = cm.getTokenAt(cur) 401 | var term, from = CodeMirror.Pos(cur.line, token.start), to = cur 402 | if (token.start < cur.ch && /\w/.test(token.string.charAt(cur.ch - token.start - 1))) { 403 | term = token.string.substr(0, cur.ch - token.start) 404 | } else { 405 | term = "" 406 | from = cur 407 | } 408 | var found = []; 409 | for (var i = 0; i < options.words.length; i++) { 410 | var word = options.words[i]; 411 | if (word.slice(0, term.length) == term) 412 | found.push(word); 413 | } 414 | 415 | if (found.length) return {list: found, from: from, to: to}; 416 | }); 417 | 418 | CodeMirror.commands.autocomplete = CodeMirror.showHint; 419 | 420 | var defaultOptions = { 421 | hint: CodeMirror.hint.auto, 422 | completeSingle: true, 423 | alignWithWord: true, 424 | closeCharacters: /[\s()\[\]{};:>,]/, 425 | closeOnUnfocus: true, 426 | completeOnSingleClick: true, 427 | container: null, 428 | customKeys: null, 429 | extraKeys: null 430 | }; 431 | 432 | CodeMirror.defineOption("hintOptions", null); 433 | }); 434 | -------------------------------------------------------------------------------- /asset/sql-hint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror"), require("../../mode/sql/sql")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror", "../../mode/sql/sql"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | var tables; 15 | var defaultTable; 16 | var keywords; 17 | var identifierQuote; 18 | var CONS = { 19 | QUERY_DIV: ";", 20 | ALIAS_KEYWORD: "AS" 21 | }; 22 | var Pos = CodeMirror.Pos, cmpPos = CodeMirror.cmpPos; 23 | 24 | function isArray(val) { return Object.prototype.toString.call(val) == "[object Array]" } 25 | 26 | function getKeywords(editor) { 27 | var mode = editor.doc.modeOption; 28 | if (mode === "sql") mode = "text/x-sql"; 29 | return CodeMirror.resolveMode(mode).keywords; 30 | } 31 | 32 | function getIdentifierQuote(editor) { 33 | var mode = editor.doc.modeOption; 34 | if (mode === "sql") mode = "text/x-sql"; 35 | return CodeMirror.resolveMode(mode).identifierQuote || "`"; 36 | } 37 | 38 | function getText(item) { 39 | return typeof item == "string" ? item : item.text; 40 | } 41 | 42 | function wrapTable(name, value) { 43 | if (isArray(value)) value = {columns: value} 44 | if (!value.text) value.text = name 45 | return value 46 | } 47 | 48 | function parseTables(input) { 49 | var result = {} 50 | if (isArray(input)) { 51 | for (var i = input.length - 1; i >= 0; i--) { 52 | var item = input[i] 53 | result[getText(item).toUpperCase()] = wrapTable(getText(item), item) 54 | } 55 | } else if (input) { 56 | for (var name in input) 57 | result[name.toUpperCase()] = wrapTable(name, input[name]) 58 | } 59 | return result 60 | } 61 | 62 | function getTable(name) { 63 | return tables[name.toUpperCase()] 64 | } 65 | 66 | function shallowClone(object) { 67 | var result = {}; 68 | for (var key in object) if (object.hasOwnProperty(key)) 69 | result[key] = object[key]; 70 | return result; 71 | } 72 | 73 | function match(string, word) { 74 | var len = string.length; 75 | var sub = getText(word).substr(0, len); 76 | return string.toUpperCase() === sub.toUpperCase(); 77 | } 78 | 79 | function addMatches(result, search, wordlist, formatter) { 80 | if (isArray(wordlist)) { 81 | for (var i = 0; i < wordlist.length; i++) 82 | if (match(search, wordlist[i])) result.push(formatter(wordlist[i])) 83 | } else { 84 | for (var word in wordlist) if (wordlist.hasOwnProperty(word)) { 85 | var val = wordlist[word] 86 | if (!val || val === true) 87 | val = word 88 | else 89 | val = val.displayText ? {text: val.text, displayText: val.displayText} : val.text 90 | if (match(search, val)) result.push(formatter(val)) 91 | } 92 | } 93 | } 94 | 95 | function cleanName(name) { 96 | // Get rid name from identifierQuote and preceding dot(.) 97 | if (name.charAt(0) == ".") { 98 | name = name.substr(1); 99 | } 100 | // replace doublicated identifierQuotes with single identifierQuotes 101 | // and remove single identifierQuotes 102 | var nameParts = name.split(identifierQuote+identifierQuote); 103 | for (var i = 0; i < nameParts.length; i++) 104 | nameParts[i] = nameParts[i].replace(new RegExp(identifierQuote,"g"), ""); 105 | return nameParts.join(identifierQuote); 106 | } 107 | 108 | function insertIdentifierQuotes(name) { 109 | var nameParts = getText(name).split("."); 110 | for (var i = 0; i < nameParts.length; i++) 111 | nameParts[i] = identifierQuote + 112 | // doublicate identifierQuotes 113 | nameParts[i].replace(new RegExp(identifierQuote,"g"), identifierQuote+identifierQuote) + 114 | identifierQuote; 115 | var escaped = nameParts.join("."); 116 | if (typeof name == "string") return escaped; 117 | name = shallowClone(name); 118 | name.text = escaped; 119 | return name; 120 | } 121 | 122 | function nameCompletion(cur, token, result, editor) { 123 | // Try to complete table, column names and return start position of completion 124 | var useIdentifierQuotes = false; 125 | var nameParts = []; 126 | var start = token.start; 127 | var cont = true; 128 | while (cont) { 129 | cont = (token.string.charAt(0) == "."); 130 | useIdentifierQuotes = useIdentifierQuotes || (token.string.charAt(0) == identifierQuote); 131 | 132 | start = token.start; 133 | nameParts.unshift(cleanName(token.string)); 134 | 135 | token = editor.getTokenAt(Pos(cur.line, token.start)); 136 | if (token.string == ".") { 137 | cont = true; 138 | token = editor.getTokenAt(Pos(cur.line, token.start)); 139 | } 140 | } 141 | 142 | // Try to complete table names 143 | var string = nameParts.join("."); 144 | addMatches(result, string, tables, function(w) { 145 | return useIdentifierQuotes ? insertIdentifierQuotes(w) : w; 146 | }); 147 | 148 | // Try to complete columns from defaultTable 149 | addMatches(result, string, defaultTable, function(w) { 150 | return useIdentifierQuotes ? insertIdentifierQuotes(w) : w; 151 | }); 152 | 153 | // Try to complete columns 154 | string = nameParts.pop(); 155 | var table = nameParts.join("."); 156 | 157 | var alias = false; 158 | var aliasTable = table; 159 | // Check if table is available. If not, find table by Alias 160 | if (!getTable(table)) { 161 | var oldTable = table; 162 | table = findTableByAlias(table, editor); 163 | if (table !== oldTable) alias = true; 164 | } 165 | 166 | var columns = getTable(table); 167 | if (columns && columns.columns) 168 | columns = columns.columns; 169 | 170 | if (columns) { 171 | addMatches(result, string, columns, function(w) { 172 | var tableInsert = table; 173 | if (alias == true) tableInsert = aliasTable; 174 | if (typeof w == "string") { 175 | w = tableInsert + "." + w; 176 | } else { 177 | w = shallowClone(w); 178 | w.text = tableInsert + "." + w.text; 179 | } 180 | return useIdentifierQuotes ? insertIdentifierQuotes(w) : w; 181 | }); 182 | } 183 | 184 | return start; 185 | } 186 | 187 | function eachWord(lineText, f) { 188 | var words = lineText.split(/\s+/) 189 | for (var i = 0; i < words.length; i++) 190 | if (words[i]) f(words[i].replace(/[,;]/g, '')) 191 | } 192 | 193 | function findTableByAlias(alias, editor) { 194 | var doc = editor.doc; 195 | var fullQuery = doc.getValue(); 196 | var aliasUpperCase = alias.toUpperCase(); 197 | var previousWord = ""; 198 | var table = ""; 199 | var separator = []; 200 | var validRange = { 201 | start: Pos(0, 0), 202 | end: Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).length) 203 | }; 204 | 205 | //add separator 206 | var indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV); 207 | while(indexOfSeparator != -1) { 208 | separator.push(doc.posFromIndex(indexOfSeparator)); 209 | indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV, indexOfSeparator+1); 210 | } 211 | separator.unshift(Pos(0, 0)); 212 | separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length)); 213 | 214 | //find valid range 215 | var prevItem = null; 216 | var current = editor.getCursor() 217 | for (var i = 0; i < separator.length; i++) { 218 | if ((prevItem == null || cmpPos(current, prevItem) > 0) && cmpPos(current, separator[i]) <= 0) { 219 | validRange = {start: prevItem, end: separator[i]}; 220 | break; 221 | } 222 | prevItem = separator[i]; 223 | } 224 | 225 | if (validRange.start) { 226 | var query = doc.getRange(validRange.start, validRange.end, false); 227 | 228 | for (var i = 0; i < query.length; i++) { 229 | var lineText = query[i]; 230 | eachWord(lineText, function(word) { 231 | var wordUpperCase = word.toUpperCase(); 232 | if (wordUpperCase === aliasUpperCase && getTable(previousWord)) 233 | table = previousWord; 234 | if (wordUpperCase !== CONS.ALIAS_KEYWORD) 235 | previousWord = word; 236 | }); 237 | if (table) break; 238 | } 239 | } 240 | return table; 241 | } 242 | 243 | CodeMirror.registerHelper("hint", "sql", function(editor, options) { 244 | tables = parseTables(options && options.tables) 245 | var defaultTableName = options && options.defaultTable; 246 | var disableKeywords = options && options.disableKeywords; 247 | defaultTable = defaultTableName && getTable(defaultTableName); 248 | keywords = getKeywords(editor); 249 | identifierQuote = getIdentifierQuote(editor); 250 | 251 | if (defaultTableName && !defaultTable) 252 | defaultTable = findTableByAlias(defaultTableName, editor); 253 | 254 | defaultTable = defaultTable || []; 255 | 256 | if (defaultTable.columns) 257 | defaultTable = defaultTable.columns; 258 | 259 | var cur = editor.getCursor(); 260 | var result = []; 261 | var token = editor.getTokenAt(cur), start, end, search; 262 | if (token.end > cur.ch) { 263 | token.end = cur.ch; 264 | token.string = token.string.slice(0, cur.ch - token.start); 265 | } 266 | 267 | if (token.string.match(/^[.`"\w@]\w*$/)) { 268 | search = token.string; 269 | start = token.start; 270 | end = token.end; 271 | } else { 272 | start = end = cur.ch; 273 | search = ""; 274 | } 275 | if (search.charAt(0) == "." || search.charAt(0) == identifierQuote) { 276 | start = nameCompletion(cur, token, result, editor); 277 | } else { 278 | addMatches(result, search, defaultTable, function(w) {return {text:w, className: "CodeMirror-hint-table CodeMirror-hint-default-table"};}); 279 | addMatches( 280 | result, 281 | search, 282 | tables, 283 | function(w) { 284 | if (typeof w === 'object') { 285 | w.className = "CodeMirror-hint-table"; 286 | } else { 287 | w = {text: w, className: "CodeMirror-hint-table"}; 288 | } 289 | 290 | return w; 291 | } 292 | ); 293 | if (!disableKeywords) 294 | addMatches(result, search, keywords, function(w) {return {text: w.toUpperCase(), className: "CodeMirror-hint-keyword"};}); 295 | } 296 | 297 | return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)}; 298 | }); 299 | }); 300 | -------------------------------------------------------------------------------- /asset/sql-qone.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.defineMode("sql", function(config, parserConfig) { 15 | "use strict"; 16 | 17 | var client = parserConfig.client || {}, 18 | atoms = parserConfig.atoms || {"false": true, "true": true, "null": true}, 19 | builtin = parserConfig.builtin || {}, 20 | keywords = parserConfig.keywords || {}, 21 | operatorChars = parserConfig.operatorChars || /^[*+\-%<>!=&|~^]/, 22 | support = parserConfig.support || {}, 23 | hooks = parserConfig.hooks || {}, 24 | dateSQL = parserConfig.dateSQL || {"date" : true, "time" : true, "timestamp" : true}, 25 | backslashStringEscapes = parserConfig.backslashStringEscapes !== false, 26 | brackets = parserConfig.brackets || /^[\{}\(\)\[\]]/, 27 | punctuation = parserConfig.punctuation || /^[;.,:]/ 28 | 29 | function tokenBase(stream, state) { 30 | var ch = stream.next(); 31 | 32 | // call hooks from the mime type 33 | if (hooks[ch]) { 34 | var result = hooks[ch](stream, state); 35 | if (result !== false) return result; 36 | } 37 | 38 | if (support.hexNumber && 39 | ((ch == "0" && stream.match(/^[xX][0-9a-fA-F]+/)) 40 | || (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]+'/))) { 41 | // hex 42 | // ref: http://dev.mysql.com/doc/refman/5.5/en/hexadecimal-literals.html 43 | return "number"; 44 | } else if (support.binaryNumber && 45 | (((ch == "b" || ch == "B") && stream.match(/^'[01]+'/)) 46 | || (ch == "0" && stream.match(/^b[01]+/)))) { 47 | // bitstring 48 | // ref: http://dev.mysql.com/doc/refman/5.5/en/bit-field-literals.html 49 | return "number"; 50 | } else if (ch.charCodeAt(0) > 47 && ch.charCodeAt(0) < 58) { 51 | // numbers 52 | // ref: http://dev.mysql.com/doc/refman/5.5/en/number-literals.html 53 | stream.match(/^[0-9]*(\.[0-9]+)?([eE][-+]?[0-9]+)?/); 54 | support.decimallessFloat && stream.match(/^\.(?!\.)/); 55 | return "number"; 56 | } else if (ch == "?" && (stream.eatSpace() || stream.eol() || stream.eat(";"))) { 57 | // placeholders 58 | return "variable-3"; 59 | } else if (ch == "'" || (ch == '"' && support.doubleQuote)) { 60 | // strings 61 | // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html 62 | state.tokenize = tokenLiteral(ch); 63 | return state.tokenize(stream, state); 64 | } else if ((((support.nCharCast && (ch == "n" || ch == "N")) 65 | || (support.charsetCast && ch == "_" && stream.match(/[a-z][a-z0-9]*/i))) 66 | && (stream.peek() == "'" || stream.peek() == '"'))) { 67 | // charset casting: _utf8'str', N'str', n'str' 68 | // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html 69 | return "keyword"; 70 | } else if (support.commentSlashSlash && ch == "/" && stream.eat("/")) { 71 | // 1-line comment 72 | stream.skipToEnd(); 73 | return "comment"; 74 | } else if ((support.commentHash && ch == "#") 75 | || (ch == "-" && stream.eat("-") && (!support.commentSpaceRequired || stream.eat(" ")))) { 76 | // 1-line comments 77 | // ref: https://kb.askmonty.org/en/comment-syntax/ 78 | stream.skipToEnd(); 79 | return "comment"; 80 | } else if (ch == "/" && stream.eat("*")) { 81 | // multi-line comments 82 | // ref: https://kb.askmonty.org/en/comment-syntax/ 83 | state.tokenize = tokenComment(1); 84 | return state.tokenize(stream, state); 85 | } else if (ch == ".") { 86 | // .1 for 0.1 87 | if (support.zerolessFloat && stream.match(/^(?:\d+(?:e[+-]?\d+)?)/i)) 88 | return "number"; 89 | if (stream.match(/^\.+/)) 90 | return null 91 | // .table_name (ODBC) 92 | // // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html 93 | if (support.ODBCdotTable && stream.match(/^[\w\d_]+/)) 94 | return "variable-2"; 95 | } else if (operatorChars.test(ch)) { 96 | // operators 97 | stream.eatWhile(operatorChars); 98 | return "operator"; 99 | } else if (brackets.test(ch)) { 100 | // brackets 101 | stream.eatWhile(brackets); 102 | return "bracket"; 103 | } else if (punctuation.test(ch)) { 104 | // punctuation 105 | stream.eatWhile(punctuation); 106 | return "punctuation"; 107 | } else if (ch == '{' && 108 | (stream.match(/^( )*(d|D|t|T|ts|TS)( )*'[^']*'( )*}/) || stream.match(/^( )*(d|D|t|T|ts|TS)( )*"[^"]*"( )*}/))) { 109 | // dates (weird ODBC syntax) 110 | // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html 111 | return "number"; 112 | } else { 113 | stream.eatWhile(/^[_\w\d]/); 114 | var word = stream.current().toLowerCase(); 115 | // dates (standard SQL syntax) 116 | // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html 117 | if (dateSQL.hasOwnProperty(word) && (stream.match(/^( )+'[^']*'/) || stream.match(/^( )+"[^"]*"/))) 118 | return "number"; 119 | if (atoms.hasOwnProperty(word)) return "atom"; 120 | if (builtin.hasOwnProperty(word)) return "builtin"; 121 | if (keywords.hasOwnProperty(word)) return "keyword"; 122 | if (client.hasOwnProperty(word)) return "string-2"; 123 | return null; 124 | } 125 | } 126 | 127 | // 'string', with char specified in quote escaped by '\' 128 | function tokenLiteral(quote) { 129 | return function(stream, state) { 130 | var escaped = false, ch; 131 | while ((ch = stream.next()) != null) { 132 | if (ch == quote && !escaped) { 133 | state.tokenize = tokenBase; 134 | break; 135 | } 136 | escaped = backslashStringEscapes && !escaped && ch == "\\"; 137 | } 138 | return "string"; 139 | }; 140 | } 141 | function tokenComment(depth) { 142 | return function(stream, state) { 143 | var m = stream.match(/^.*?(\/\*|\*\/)/) 144 | if (!m) stream.skipToEnd() 145 | else if (m[1] == "/*") state.tokenize = tokenComment(depth + 1) 146 | else if (depth > 1) state.tokenize = tokenComment(depth - 1) 147 | else state.tokenize = tokenBase 148 | return "comment" 149 | } 150 | } 151 | 152 | function pushContext(stream, state, type) { 153 | state.context = { 154 | prev: state.context, 155 | indent: stream.indentation(), 156 | col: stream.column(), 157 | type: type 158 | }; 159 | } 160 | 161 | function popContext(state) { 162 | state.indent = state.context.indent; 163 | state.context = state.context.prev; 164 | } 165 | 166 | return { 167 | startState: function() { 168 | return {tokenize: tokenBase, context: null}; 169 | }, 170 | 171 | token: function(stream, state) { 172 | if (stream.sol()) { 173 | if (state.context && state.context.align == null) 174 | state.context.align = false; 175 | } 176 | if (state.tokenize == tokenBase && stream.eatSpace()) return null; 177 | 178 | var style = state.tokenize(stream, state); 179 | if (style == "comment") return style; 180 | 181 | if (state.context && state.context.align == null) 182 | state.context.align = true; 183 | 184 | var tok = stream.current(); 185 | if (tok == "(") 186 | pushContext(stream, state, ")"); 187 | else if (tok == "[") 188 | pushContext(stream, state, "]"); 189 | else if (state.context && state.context.type == tok) 190 | popContext(state); 191 | return style; 192 | }, 193 | 194 | indent: function(state, textAfter) { 195 | var cx = state.context; 196 | if (!cx) return CodeMirror.Pass; 197 | var closing = textAfter.charAt(0) == cx.type; 198 | if (cx.align) return cx.col + (closing ? 0 : 1); 199 | else return cx.indent + (closing ? 0 : config.indentUnit); 200 | }, 201 | 202 | blockCommentStart: "/*", 203 | blockCommentEnd: "*/", 204 | lineComment: support.commentSlashSlash ? "//" : support.commentHash ? "#" : "--", 205 | closeBrackets: "()[]{}''\"\"``" 206 | }; 207 | }); 208 | 209 | (function() { 210 | "use strict"; 211 | 212 | // `identifier` 213 | function hookIdentifier(stream) { 214 | // MySQL/MariaDB identifiers 215 | // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html 216 | var ch; 217 | while ((ch = stream.next()) != null) { 218 | if (ch == "`" && !stream.eat("`")) return "variable-2"; 219 | } 220 | stream.backUp(stream.current().length - 1); 221 | return stream.eatWhile(/\w/) ? "variable-2" : null; 222 | } 223 | 224 | // "identifier" 225 | function hookIdentifierDoublequote(stream) { 226 | // Standard SQL /SQLite identifiers 227 | // ref: http://web.archive.org/web/20160813185132/http://savage.net.au/SQL/sql-99.bnf.html#delimited%20identifier 228 | // ref: http://sqlite.org/lang_keywords.html 229 | var ch; 230 | while ((ch = stream.next()) != null) { 231 | if (ch == "\"" && !stream.eat("\"")) return "variable-2"; 232 | } 233 | stream.backUp(stream.current().length - 1); 234 | return stream.eatWhile(/\w/) ? "variable-2" : null; 235 | } 236 | 237 | // variable token 238 | function hookVar(stream) { 239 | // variables 240 | // @@prefix.varName @varName 241 | // varName can be quoted with ` or ' or " 242 | // ref: http://dev.mysql.com/doc/refman/5.5/en/user-variables.html 243 | if (stream.eat("@")) { 244 | stream.match(/^session\./); 245 | stream.match(/^local\./); 246 | stream.match(/^global\./); 247 | } 248 | 249 | if (stream.eat("'")) { 250 | stream.match(/^.*'/); 251 | return "variable-2"; 252 | } else if (stream.eat('"')) { 253 | stream.match(/^.*"/); 254 | return "variable-2"; 255 | } else if (stream.eat("`")) { 256 | stream.match(/^.*`/); 257 | return "variable-2"; 258 | } else if (stream.match(/^[0-9a-zA-Z$\.\_]+/)) { 259 | return "variable-2"; 260 | } 261 | return null; 262 | }; 263 | 264 | // short client keyword token 265 | function hookClient(stream) { 266 | // \N means NULL 267 | // ref: http://dev.mysql.com/doc/refman/5.5/en/null-values.html 268 | if (stream.eat("N")) { 269 | return "atom"; 270 | } 271 | // \g, etc 272 | // ref: http://dev.mysql.com/doc/refman/5.5/en/mysql-commands.html 273 | return stream.match(/^[a-zA-Z.#!?]/) ? "variable-2" : null; 274 | } 275 | 276 | // these keywords are used by all SQL dialects (however, a mode can still overwrite it) 277 | var sqlKeywords = "asc desc from in select where limit "; 278 | 279 | // turn a space-separated list into an array 280 | function set(str) { 281 | var obj = {}, words = str.split(" "); 282 | for (var i = 0; i < words.length; ++i) obj[words[i]] = true; 283 | return obj; 284 | } 285 | 286 | 287 | CodeMirror.defineMIME("text/qone", { 288 | name: "sql", 289 | keywords: set(sqlKeywords + "groupby orderby "), 290 | builtin: set("bool boolean bit blob enum long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision real date datetime year unsigned signed decimal numeric"), 291 | atoms: set("false true null undefined"), 292 | operatorChars: /^[*+\-%<>!=]/, 293 | dateSQL: set("date time timestamp"), 294 | support: set("ODBCdotTable doubleQuote binaryNumber hexNumber") 295 | }); 296 | }()); 297 | 298 | }); 299 | 300 | /* 301 | How Properties of Mime Types are used by SQL Mode 302 | ================================================= 303 | 304 | keywords: 305 | A list of keywords you want to be highlighted. 306 | builtin: 307 | A list of builtin types you want to be highlighted (if you want types to be of class "builtin" instead of "keyword"). 308 | operatorChars: 309 | All characters that must be handled as operators. 310 | client: 311 | Commands parsed and executed by the client (not the server). 312 | support: 313 | A list of supported syntaxes which are not common, but are supported by more than 1 DBMS. 314 | * ODBCdotTable: .tableName 315 | * zerolessFloat: .1 316 | * doubleQuote 317 | * nCharCast: N'string' 318 | * charsetCast: _utf8'string' 319 | * commentHash: use # char for comments 320 | * commentSlashSlash: use // for comments 321 | * commentSpaceRequired: require a space after -- for comments 322 | atoms: 323 | Keywords that must be highlighted as atoms,. Some DBMS's support more atoms than others: 324 | UNKNOWN, INFINITY, UNDERFLOW, NaN... 325 | dateSQL: 326 | Used for date/time SQL standard syntax, because not all DBMS's support same temporal types. 327 | */ -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Qone - .NET LINQ in javascript. 6 | 7 | 8 | 87 | 88 | 101 | 102 | 103 | 104 | 105 | 106 | 107 |
108 |
109 | Qone 110 | 111 |
112 | 113 |
.NET LINQ in javascript.
114 | 115 | 116 |
var list = [
117 |     { name: 'qone', age: 1 },
118 |     { name: 'linq', age: 18 },
119 |     { name: 'tencent', age: 20 },
120 |     { name: 'dntzhang', age: 28 }
121 | ]
122 | 
123 | var result = qone({ list }).query(`
124 | 
125 |     
126 |
127 | 132 |
133 |
`)
134 | 
135 | console.log(result)
136 |     
137 |         
138 |

139 | 140 | 143 | 144 |
  
145 |
146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 192 | 193 | 194 | 195 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "qone", 3 | "version": "2.0.0", 4 | "main": "qone.js", 5 | "description": "Next-generation web query language, extend .NET LINQ for javascript.", 6 | "scripts": { 7 | "lint": "eslint qone.js --fix", 8 | "build": "uglifyjs -o qone.min.js --ie8 --compress --mangle -- qone.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/dntzhang/qone" 13 | }, 14 | "keywords": [ 15 | "qone", 16 | "linq", 17 | "query" 18 | ], 19 | "dependencies": {}, 20 | "devDependencies": { 21 | "eslint": "^4.3.0", 22 | "eslint-config-standard": "^10.2.1", 23 | "eslint-plugin-import": "^2.7.0", 24 | "eslint-plugin-node": "^5.1.1", 25 | "eslint-plugin-promise": "^3.5.0", 26 | "eslint-plugin-standard": "^3.0.1", 27 | "uglifyjs": "^2.4.11" 28 | }, 29 | "author": "dntzhang", 30 | "license": "MIT", 31 | "bugs": { 32 | "url": "https://github.com/dntzhang/qone/issues/new" 33 | }, 34 | "homepage": "https://dntzhang.github.io/qone/" 35 | } 36 | -------------------------------------------------------------------------------- /qone.min.js: -------------------------------------------------------------------------------- 1 | /* qone v2.0.0 - Next-generation web query language, extend .NET LINQ for javascript. 2 | * By dntzhang https://github.com/dntzhang 3 | * Github: https://github.com/dntzhang/qone 4 | * MIT Licensed. 5 | */ 6 | !function(u,t){"object"==typeof module&&module.exports?module.exports=t():u.qone=t()}(this,function(){function u(u,t){return Object.prototype.hasOwnProperty.call(u,t)}function t(u){return"[object Array]"===Object.prototype.toString.call(u)}function e(u){return null!==u&&"object"==typeof u&&!Array.isArray(u)}function i(u){var t,e=[] 7 | for(t in u)e.push(t) 8 | return e}function s(u){var t,e={} 9 | for(t=0;t=48&&57>=u}function r(u){return n(u)||a(u)}function o(u){return u.split("")}function h(u){return x.test(u)?parseInt(u.substr(2),16):_.test(u)?parseInt(u.substr(1),8):g.test(u)?parseFloat(u):void 0}function a(u){return m.letter.test(u)||"_"===u}function A(u){return null===u||void 0===u}function c(u,t,e){var s,n,r,o 11 | if(A(u)||A(t))return!1 12 | if(u.prototype!==t.prototype)return!1 13 | try{r=i(u),o=i(t)}catch(h){return!1}if(r.length!=o.length)return!1 14 | for(r.sort(),o.sort(),s=r.length-1;s>=0;s--)if(r[s]!=o[s])return!1 15 | for(s=r.length-1;s>=0;s--)if(n=r[s],!b(u[n],t[n],e))return!1 16 | return typeof u==typeof t}function p(u){if(null===u||void 0===u)throw new TypeError("Object.assign cannot be called with null or undefined") 17 | return Object(u)}function B(){var u,t,e,i,s 18 | try{if(!Object.assign)return!1 19 | if(u=new String("abc"),u[5]="de","5"===Object.getOwnPropertyNames(u)[0])return!1 20 | for(t={},e=0;10>e;e++)t["_"+String.fromCharCode(e)]=e 21 | return i=Object.getOwnPropertyNames(t).map(function(u){return t[u]}),"0123456789"!==i.join("")?!1:(s={},"abcdefghijklmnopqrst".split("").forEach(function(u){s[u]=u}),"abcdefghijklmnopqrst"!==Object.keys(Object.assign({},s)).join("")?!1:!0)}catch(n){return!1}}var C,l,F,D=s(o(" \r ​")),f=s(o("+-*&%=<>!?|~^")),d=s(o("[]{}(),;:")),E=s(o("[{}(,.;:")),y=s(["false","null","true","undefined"]),v={"null":null,undefined:void 0,"true":!0,"false":!1},x=/^0x[0-9a-f]+$/i,_=/^0[0-7]+$/,g=/^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i,k=s(["from","in","where","select","orderby","desc","asc","groupby","limit"]),m={letter:RegExp("[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u0523\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0621-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971\\u0972\\u097B-\\u097F\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C33\\u0C35-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D28\\u0D2A-\\u0D39\\u0D3D\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC\\u0EDD\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8B\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10D0-\\u10FA\\u10FC\\u1100-\\u1159\\u115F-\\u11A2\\u11A8-\\u11F9\\u1200-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u1676\\u1681-\\u169A\\u16A0-\\u16EA\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u1900-\\u191C\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19A9\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u2094\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183\\u2184\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2C6F\\u2C71-\\u2C7D\\u2C80-\\u2CE4\\u2D00-\\u2D25\\u2D30-\\u2D65\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005\\u3006\\u3031-\\u3035\\u303B\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31B7\\u31F0-\\u31FF\\u3400\\u4DB5\\u4E00\\u9FC3\\uA000-\\uA48C\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA65F\\uA662-\\uA66E\\uA67F-\\uA697\\uA717-\\uA71F\\uA722-\\uA788\\uA78B\\uA78C\\uA7FB-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA90A-\\uA925\\uA930-\\uA946\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAC00\\uD7A3\\uF900-\\uFA2D\\uFA30-\\uFA6A\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]"),non_spacing_mark:RegExp("[\\u0300-\\u036F\\u0483-\\u0487\\u0591-\\u05BD\\u05BF\\u05C1\\u05C2\\u05C4\\u05C5\\u05C7\\u0610-\\u061A\\u064B-\\u065E\\u0670\\u06D6-\\u06DC\\u06DF-\\u06E4\\u06E7\\u06E8\\u06EA-\\u06ED\\u0711\\u0730-\\u074A\\u07A6-\\u07B0\\u07EB-\\u07F3\\u0816-\\u0819\\u081B-\\u0823\\u0825-\\u0827\\u0829-\\u082D\\u0900-\\u0902\\u093C\\u0941-\\u0948\\u094D\\u0951-\\u0955\\u0962\\u0963\\u0981\\u09BC\\u09C1-\\u09C4\\u09CD\\u09E2\\u09E3\\u0A01\\u0A02\\u0A3C\\u0A41\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A51\\u0A70\\u0A71\\u0A75\\u0A81\\u0A82\\u0ABC\\u0AC1-\\u0AC5\\u0AC7\\u0AC8\\u0ACD\\u0AE2\\u0AE3\\u0B01\\u0B3C\\u0B3F\\u0B41-\\u0B44\\u0B4D\\u0B56\\u0B62\\u0B63\\u0B82\\u0BC0\\u0BCD\\u0C3E-\\u0C40\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56\\u0C62\\u0C63\\u0CBC\\u0CBF\\u0CC6\\u0CCC\\u0CCD\\u0CE2\\u0CE3\\u0D41-\\u0D44\\u0D4D\\u0D62\\u0D63\\u0DCA\\u0DD2-\\u0DD4\\u0DD6\\u0E31\\u0E34-\\u0E3A\\u0E47-\\u0E4E\\u0EB1\\u0EB4-\\u0EB9\\u0EBB\\u0EBC\\u0EC8-\\u0ECD\\u0F18\\u0F19\\u0F35\\u0F37\\u0F39\\u0F71-\\u0F7E\\u0F80-\\u0F84\\u0F86\\u0F87\\u0F90-\\u0F97\\u0F99-\\u0FBC\\u0FC6\\u102D-\\u1030\\u1032-\\u1037\\u1039\\u103A\\u103D\\u103E\\u1058\\u1059\\u105E-\\u1060\\u1071-\\u1074\\u1082\\u1085\\u1086\\u108D\\u109D\\u135F\\u1712-\\u1714\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17B7-\\u17BD\\u17C6\\u17C9-\\u17D3\\u17DD\\u180B-\\u180D\\u18A9\\u1920-\\u1922\\u1927\\u1928\\u1932\\u1939-\\u193B\\u1A17\\u1A18\\u1A56\\u1A58-\\u1A5E\\u1A60\\u1A62\\u1A65-\\u1A6C\\u1A73-\\u1A7C\\u1A7F\\u1B00-\\u1B03\\u1B34\\u1B36-\\u1B3A\\u1B3C\\u1B42\\u1B6B-\\u1B73\\u1B80\\u1B81\\u1BA2-\\u1BA5\\u1BA8\\u1BA9\\u1C2C-\\u1C33\\u1C36\\u1C37\\u1CD0-\\u1CD2\\u1CD4-\\u1CE0\\u1CE2-\\u1CE8\\u1CED\\u1DC0-\\u1DE6\\u1DFD-\\u1DFF\\u20D0-\\u20DC\\u20E1\\u20E5-\\u20F0\\u2CEF-\\u2CF1\\u2DE0-\\u2DFF\\u302A-\\u302F\\u3099\\u309A\\uA66F\\uA67C\\uA67D\\uA6F0\\uA6F1\\uA802\\uA806\\uA80B\\uA825\\uA826\\uA8C4\\uA8E0-\\uA8F1\\uA926-\\uA92D\\uA947-\\uA951\\uA980-\\uA982\\uA9B3\\uA9B6-\\uA9B9\\uA9BC\\uAA29-\\uAA2E\\uAA31\\uAA32\\uAA35\\uAA36\\uAA43\\uAA4C\\uAAB0\\uAAB2-\\uAAB4\\uAAB7\\uAAB8\\uAABE\\uAABF\\uAAC1\\uABE5\\uABE8\\uABED\\uFB1E\\uFE00-\\uFE0F\\uFE20-\\uFE26]"),space_combining_mark:RegExp("[\\u0903\\u093E-\\u0940\\u0949-\\u094C\\u094E\\u0982\\u0983\\u09BE-\\u09C0\\u09C7\\u09C8\\u09CB\\u09CC\\u09D7\\u0A03\\u0A3E-\\u0A40\\u0A83\\u0ABE-\\u0AC0\\u0AC9\\u0ACB\\u0ACC\\u0B02\\u0B03\\u0B3E\\u0B40\\u0B47\\u0B48\\u0B4B\\u0B4C\\u0B57\\u0BBE\\u0BBF\\u0BC1\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCC\\u0BD7\\u0C01-\\u0C03\\u0C41-\\u0C44\\u0C82\\u0C83\\u0CBE\\u0CC0-\\u0CC4\\u0CC7\\u0CC8\\u0CCA\\u0CCB\\u0CD5\\u0CD6\\u0D02\\u0D03\\u0D3E-\\u0D40\\u0D46-\\u0D48\\u0D4A-\\u0D4C\\u0D57\\u0D82\\u0D83\\u0DCF-\\u0DD1\\u0DD8-\\u0DDF\\u0DF2\\u0DF3\\u0F3E\\u0F3F\\u0F7F\\u102B\\u102C\\u1031\\u1038\\u103B\\u103C\\u1056\\u1057\\u1062-\\u1064\\u1067-\\u106D\\u1083\\u1084\\u1087-\\u108C\\u108F\\u109A-\\u109C\\u17B6\\u17BE-\\u17C5\\u17C7\\u17C8\\u1923-\\u1926\\u1929-\\u192B\\u1930\\u1931\\u1933-\\u1938\\u19B0-\\u19C0\\u19C8\\u19C9\\u1A19-\\u1A1B\\u1A55\\u1A57\\u1A61\\u1A63\\u1A64\\u1A6D-\\u1A72\\u1B04\\u1B35\\u1B3B\\u1B3D-\\u1B41\\u1B43\\u1B44\\u1B82\\u1BA1\\u1BA6\\u1BA7\\u1BAA\\u1C24-\\u1C2B\\u1C34\\u1C35\\u1CE1\\u1CF2\\uA823\\uA824\\uA827\\uA880\\uA881\\uA8B4-\\uA8C3\\uA952\\uA953\\uA983\\uA9B4\\uA9B5\\uA9BA\\uA9BB\\uA9BD-\\uA9C0\\uAA2F\\uAA30\\uAA33\\uAA34\\uAA4D\\uAA7B\\uABE3\\uABE4\\uABE6\\uABE7\\uABE9\\uABEA\\uABEC]"),connector_punctuation:RegExp("[\\u005F\\u203F\\u2040\\u2054\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFF3F]")},b=function(u,t,e){return e||(e={}),u===t?!0:u instanceof Date&&t instanceof Date?u.getTime()===t.getTime():!u||!t||"object"!=typeof u&&"object"!=typeof t?e.strict?u===t:u==t:c(u,t,e)},w=Object.getOwnPropertySymbols,j=Object.prototype.hasOwnProperty,O=Object.prototype.propertyIsEnumerable,M=B()?Object.assign:function(u,t){var e,i,s,n,r,o=p(u) 22 | for(s=1;s"===this.text.charAt(this.pos+1)?(this.col+=2,this.pos+=2,{type:"operator",value:"!="}):"="===t&&"="===this.text.charAt(this.pos+1)?("="===this.text.charAt(this.pos+2)?(this.col+=3,this.pos+=3):(this.col+=2,this.pos+=2),{type:"operator",value:"="}):">"===t&&"="===this.text.charAt(this.pos+1)?(this.col+=2,this.pos+=2,{type:"operator",value:">="}):"<"===t&&"="===this.text.charAt(this.pos+1)?(this.col+=2,this.pos+=2,{type:"operator",value:"<="}):{type:"operator",value:this.next()}:n(t)?this.readNum():"\n"==t?{type:"br",value:this.next()}:'"'==t||"'"==t?this.readString():void 0},start:function(){var u=this.scan() 34 | void 0!==u&&(this.list.push(u),this.start())}},C=function(u){this.tokens=u,this.index=0,this.ast=[]},C.prototype={start:function(){this.parse()},_isKeyword:function(u){return"keyword"===u.type&&("from"===u.value||"where"===u.value||"select"===u.value||"orderby"===u.value||"groupby"===u.value||"limit"===u.value)},_conditions:function(){for(var u,t,e=[],i=this.tokens[this.index];!this._isKeyword(i);)this._is("punc","(")?(this.index++,e.push([this._conditionBlock()])):"&&"===i.value||"||"===i.value?(this.index++,e.push(i.value),e.push(this._condition())):this._is(i,"operator","!")?(u=this.tokens[this.index+1],this._is(u,"punc","(")?(this.index+=2,e.push(["!",this._conditionBlock()])):"name"===u.type&&(this.index++,e.push(["!",this._prop()]))):(t=this._condition(),t.length>0&&e.push(t)),this.index++,i=this.tokens[this.index] 35 | return e},_conditionBlock:function(){for(var u,t=[],e=this.tokens[this.index];!this._is(e,"punc",")")&&!this._isKeyword(e);)this._is(e,"punc","(")?(this.index++,t.push(this._conditionBlock())):"&&"===e.value||"||"===e.value?(this.index++,t.push(e.value),t.push(this._condition())):this._is(e,"operator","!")?(u=this.tokens[this.index+1],this._is(u,"punc","(")?(this.index+=2,t.push(["!",this._conditionBlock()])):"name"===u.type&&(this.index++,t.push(["!",this._prop()]))):t.push(this._condition()),this.index++,e=this.tokens[this.index] 36 | return t},_condition:function(){for(var u,t=[],e=this.tokens[this.index];!this._is(e,"punc",")")&&("&&"!==e.value&&"||"!==e.value||"operator"!==e.type)&&!this._isKeyword(e);)this._is(e,"punc","(")?(this.index++,t.push(this._conditionBlock())):"name"===e.type?t.push(this._prop()):this._is(e,"operator","!")?(u=this.tokens[this.index+1],this._is(u,"punc","(")?(this.index+=2,t.push(["!",this._conditionBlock()])):"name"===u.type&&(this.index++,t.push(["!",this._prop()]))):"atom"===e.type?t.push(v[e.value]):"br"!==e.type&&t.push(e.value),this.index++,e=this.tokens[this.index] 37 | return this.index--,t},_prop:function(){var u=[],t=this.tokens[this.index] 38 | if("name"===t.type&&this.tokens[this.index+1]&&this._is(this.tokens[this.index+1],"punc","("))this.index+=2,u={name:t.value,args:this._args()} 39 | else{for(;t&&("string"===t.type||"number"===t.type||"name"===t.type||"atom"===t.type||"punc"===t.type&&("."===t.value||"["===t.value||"]"===t.value));)("punc"!==t.type||"."!==t.value&&"["!==t.value&&"]"!==t.value)&&(u.length>0||"name"===t.type?u.push(t.value):u=t),this.index++,t=this.tokens[this.index] 40 | this.index--}return u},_parseCondition:function(u){var e=this._splitArray(u,"||"),i=this 41 | return e.forEach(function(u,s){t(u)&&(e[s]=i._parseCondition(u))}),e},_splitArray:function(u,t){if(u.indexOf(t)===-1)return u 42 | var e=[],i=[] 43 | return u.forEach(function(u){u!==t?i.push(u):(e.push(i),e.push(t),i=[])}),e.push(i),e},_select:function(){var u=this.tokens[this.index] 44 | return"{"===u.value?(this.index++,":"===this.tokens[this.index+1].value?this._json():this._simpleJson()):this._propList()},_json:function(){for(var u=[],t=this.tokens[this.index],e={};"}"!==t.value;)":"===this.tokens[this.index+1].value&&(e.key=this._prop()[0]),":"===t.value?(this.index++,e.value=this._prop()):","===t.value&&(this.index++,u.push(e),e={},e.key=this._prop()[0]),this.index++,t=this.tokens[this.index] 45 | return u.push(e),this.index--,{json:u}},_simpleJson:function(){for(var u,t=[],e=this.tokens[this.index];"}"!==e.value;)t=this._propList(),e=this.tokens[this.index] 46 | return this.index--,u=[],t.forEach(function(t){u.push({key:t[t.length-1],value:t})}),{json:u}},_propList:function(){for(var u=[],t=this.tokens[this.index];t&&"}"!==t.value&&!this._isKeyword(t);)","===t.value||"br"===t.type?this.index++:(u.push(this._prop()),this.index++),t=this.tokens[this.index] 47 | return u},_is:function(u,t,e){return u.type===t&&u.value===e},_args:function(){for(var u,t=[],e=this.tokens[this.index];!this._is(e,"punc",")");)this._is(this.tokens[this.index],"punc",",")&&this.index++,u=this._prop(),t.push(u),this.index++,e=this.tokens[this.index] 48 | return t},_orderby:function(){for(var u=[],t=this.tokens[this.index],e={desc:!1};"keyword"!==t.type||"desc"===t.value||"asc"===t.value;)"name"===t.type?e.prop=this._prop():this._is(t,"keyword","desc")?(e.desc=!0,u.push(e),e={desc:!1}):this._is(t,"punc",",")&&(e.prop&&u.push(e),e={desc:!1}),this.index++,t=this.tokens[this.index] 49 | return e.prop&&u.push(e),u},_limit:function(){for(var u=[],t=this.tokens[this.index];t&&!this._isKeyword(t);)"number"===t.type&&u.push(t.value),this.index++,t=this.tokens[this.index] 50 | return u},parse:function(){var u,t=this.tokens[this.index] 51 | if(t)switch(t.type){case"keyword":switch(t.value){case"from":u=this.tokens[this.index+1].value,this.index+=3,this.ast.push(["from",[u,this._prop()]]),this.index+=1,this.parse() 52 | break 53 | case"where":this.index++,this.ast.push(["where",this._parseCondition(this._conditions())]),this.parse() 54 | break 55 | case"select":this.index++,this.ast.push(["select",this._select()]),this.parse() 56 | break 57 | case"orderby":this.index++,this.ast.push(["orderby",this._orderby()]),this.parse() 58 | break 59 | case"groupby":this.index++,this.ast.push(["groupby",this._propList()]),this.parse() 60 | break 61 | case"limit":this.index++,this.ast.push(["limit",this._limit()]),this.parse()}break 62 | case"br":this.index++,this.parse()}}},l=function(u){this.data=this.extend(u),this.ast=null,this.keyMap={}},l.methodMap={},l.prototype={extend:function(u,t){if(null==u||"object"!=typeof u)return u 63 | if(u.constructor!=Object&&u.constructor!=Array)return u 64 | if(u.constructor==Date||u.constructor==RegExp||u.constructor==Function||u.constructor==String||u.constructor==Number||u.constructor==Boolean)return new u.constructor(u) 65 | t=t||new u.constructor 66 | for(var e in u)t[e]=void 0===t[e]?this.extend(u[e],null):t[e] 67 | return t},query:function(u){var t,e=new P(u) 68 | return e.start(),t=new C(e.list),t.start(),this.ast=t.ast,this.exce(),this.result},preprocessData:function(u,t){t.forEach(function(t,e,i){i[e]={},i[e][u]=t})},productSelf:function(u,t){var e=this,i=[] 69 | this.cp.forEach(function(s){var n=JSON.parse(JSON.stringify(e._getDataByPath(s,t))) 70 | e.preprocessData(u,n),e.productOut([s],n,i)}),this.cp=i},exce:function(){for(var u,t,e,s,n,r,o=this.ast,h=0,a=o.length;a>h;h++)switch(t=o[h],t[0]){case"from":e=t[1][1][0],this.keyMap[e]?(this.keyMap[t[1][0]]=t[1][1],this.productSelf(t[1][0],t[1][1],this.keyMap,this.cp)):(u=t[1][0],s=this._getDataByPath(this.data,t[1][1]),this.preprocessData(u,s),this.cp?this.cp=this.product(this.cp,s):this.cp=s,this.keyMap[t[1][0]]=t[1][1]),"where"===o[h+1][0]&&(this.filter(o[h+1][1]),h++) 71 | break 72 | case"where":this.filter(o[h][1]) 73 | break 74 | case"select":this.select(t[1]) 75 | break 76 | case"orderby":n=[],r=[],t[1].forEach(function(u){n.push(u.prop),r.push(u.desc?"desc":"asc")}),this._orderby(this.cp,n,r) 77 | break 78 | case"groupby":this.groupby(this.cp,t[1]),u=i(this.keyMap)[0],this.result.forEach(function(t){t.forEach(function(t,e,i){i[e]=t[u]})}) 79 | break 80 | case"limit":this.limitData(t[1])}},limitData:function(u){this.result=this.result.splice(u[0],u[1])},groupby:function(u,i,s){var n,r,o,h,a,A,c,p,B=this 81 | for("function"==typeof i?n=i:(e(i)&&(s=i,i=[]),s=s||{},void 0===s.strict&&(s.strict=!0),i=[].concat(i).filter(Boolean),r=i.length,n=0===r?function(u,t){return b(u,t,s)}:function(u,e){for(var n,o,h,a=-1;++ae;e++)if("object"==typeof u[e]){t=!1 90 | break}return t},_check:function(u,e){var i,s,n,r=this,o=e.length 91 | if(1===o&&t(e))return this._check(u,e[0]) 92 | if(2===o&&"!"===e[0])return!this._check(u,e[1]) 93 | if(this._isBool(e))return t(e)?this._getDataByPath(u,e):this.callMethod(u,e.name,e.args) 94 | if(3===o&&"||"!==e[1]&&"&&"!==e[1])return this._checkCond(u,e) 95 | for(i=0,s=!0;o>i;){if(n=e[i],s=r._check(u,n),i++,s){if("||"===e[i])return!0}else if("&&"===e[i])return!1 96 | i++}return s},_checkCond:function(u,i){var s=!0,n=i[0],r=i[2] 97 | return t(n)?n=this._getDataByPath(u,n):e(n)&&(n=this.callMethod(u,n.name,n.args)),t(r)?r=this._getDataByPath(u,r):e(r)&&(r=this.callMethod(u,r.name,r.args)),this._cond(n,i[1],r)||(s=!1),s},_cond:function(u,t,e){switch(t){case">":return u>e 98 | case"<":return e>u 99 | case">=":return u>=e 100 | case"<=":return e>=u 101 | case"=":return u===e 102 | case"!=":return u!==e}},_getDataByPath:function(u,t){var e=u 103 | return t.forEach(function(u,t){e=e[u]}),e},_orderby:function(u,e,i){var s,n,r,o="asc",h=this 104 | return"length"in u&&"object"==typeof u?(i instanceof Array||(s=i||o),e=e instanceof Array?e:[e],u.sort(function(u,a){var A,c,p,B=e.length 105 | if(u&&a)for(A=0;B>A;A++){if(p=e[A],c="asc"==(s||i[A]||o)?-1:1,t(p)?(n=h._getDataByPath(u,p),r=h._getDataByPath(a,p)):(n=h.callMethod(u,p.name,p.args),r=h.callMethod(a,p.name,p.args)),n>r)return-c 106 | if(r>n)return c}return 0})):[]}},F=function(u){if(2!==arguments.length)return new l(u) 107 | var t=arguments[0] 108 | l.methodMap[t]&&console.warn("["+t+"] method has been defined. you will rewrite it."),l.methodMap[t]=arguments[1]}}) 109 | 110 | -------------------------------------------------------------------------------- /test/clone.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
aa
10 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Qone Unit Testing 9 | 10 | 11 | 12 | 13 |
14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /test/index.ie.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Qone Unit Testing 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /test/lib/qunit.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * QUnit 2.6.1-pre 3 | * https://qunitjs.com/ 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license 7 | * https://jquery.org/license 8 | * 9 | * Date: 2018-03-27T02:25Z 10 | */ 11 | 12 | /** Font Family and Sizes */ 13 | 14 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult { 15 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; 16 | } 17 | 18 | #qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } 19 | #qunit-tests { font-size: smaller; } 20 | 21 | 22 | /** Resets */ 23 | 24 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { 25 | margin: 0; 26 | padding: 0; 27 | } 28 | 29 | 30 | /** Header (excluding toolbar) */ 31 | 32 | #qunit-header { 33 | padding: 0.5em 0 0.5em 1em; 34 | 35 | color: #8699A4; 36 | background-color: #0D3349; 37 | 38 | font-size: 1.5em; 39 | line-height: 1em; 40 | font-weight: 400; 41 | 42 | border-radius: 5px 5px 0 0; 43 | } 44 | 45 | #qunit-header a { 46 | text-decoration: none; 47 | color: #C2CCD1; 48 | } 49 | 50 | #qunit-header a:hover, 51 | #qunit-header a:focus { 52 | color: #FFF; 53 | } 54 | 55 | #qunit-banner { 56 | height: 5px; 57 | } 58 | 59 | #qunit-filteredTest { 60 | padding: 0.5em 1em 0.5em 1em; 61 | color: #366097; 62 | background-color: #F4FF77; 63 | } 64 | 65 | #qunit-userAgent { 66 | padding: 0.5em 1em 0.5em 1em; 67 | color: #FFF; 68 | background-color: #2B81AF; 69 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; 70 | } 71 | 72 | 73 | /** Toolbar */ 74 | 75 | #qunit-testrunner-toolbar { 76 | padding: 0.5em 1em 0.5em 1em; 77 | color: #5E740B; 78 | background-color: #EEE; 79 | } 80 | 81 | #qunit-testrunner-toolbar .clearfix { 82 | height: 0; 83 | clear: both; 84 | } 85 | 86 | #qunit-testrunner-toolbar label { 87 | display: inline-block; 88 | } 89 | 90 | #qunit-testrunner-toolbar input[type=checkbox], 91 | #qunit-testrunner-toolbar input[type=radio] { 92 | margin: 3px; 93 | vertical-align: -2px; 94 | } 95 | 96 | #qunit-testrunner-toolbar input[type=text] { 97 | box-sizing: border-box; 98 | height: 1.6em; 99 | } 100 | 101 | .qunit-url-config, 102 | .qunit-filter, 103 | #qunit-modulefilter { 104 | display: inline-block; 105 | line-height: 2.1em; 106 | } 107 | 108 | .qunit-filter, 109 | #qunit-modulefilter { 110 | float: right; 111 | position: relative; 112 | margin-left: 1em; 113 | } 114 | 115 | .qunit-url-config label { 116 | margin-right: 0.5em; 117 | } 118 | 119 | #qunit-modulefilter-search { 120 | box-sizing: border-box; 121 | width: 400px; 122 | } 123 | 124 | #qunit-modulefilter-search-container:after { 125 | position: absolute; 126 | right: 0.3em; 127 | content: "\25bc"; 128 | color: black; 129 | } 130 | 131 | #qunit-modulefilter-dropdown { 132 | /* align with #qunit-modulefilter-search */ 133 | box-sizing: border-box; 134 | width: 400px; 135 | position: absolute; 136 | right: 0; 137 | top: 50%; 138 | margin-top: 0.8em; 139 | 140 | border: 1px solid #D3D3D3; 141 | border-top: none; 142 | border-radius: 0 0 .25em .25em; 143 | color: #000; 144 | background-color: #F5F5F5; 145 | z-index: 99; 146 | } 147 | 148 | #qunit-modulefilter-dropdown a { 149 | color: inherit; 150 | text-decoration: none; 151 | } 152 | 153 | #qunit-modulefilter-dropdown .clickable.checked { 154 | font-weight: bold; 155 | color: #000; 156 | background-color: #D2E0E6; 157 | } 158 | 159 | #qunit-modulefilter-dropdown .clickable:hover { 160 | color: #FFF; 161 | background-color: #0D3349; 162 | } 163 | 164 | #qunit-modulefilter-actions { 165 | display: block; 166 | overflow: auto; 167 | 168 | /* align with #qunit-modulefilter-dropdown-list */ 169 | font: smaller/1.5em sans-serif; 170 | } 171 | 172 | #qunit-modulefilter-dropdown #qunit-modulefilter-actions > * { 173 | box-sizing: border-box; 174 | max-height: 2.8em; 175 | display: block; 176 | padding: 0.4em; 177 | } 178 | 179 | #qunit-modulefilter-dropdown #qunit-modulefilter-actions > button { 180 | float: right; 181 | font: inherit; 182 | } 183 | 184 | #qunit-modulefilter-dropdown #qunit-modulefilter-actions > :last-child { 185 | /* insert padding to align with checkbox margins */ 186 | padding-left: 3px; 187 | } 188 | 189 | #qunit-modulefilter-dropdown-list { 190 | max-height: 200px; 191 | overflow-y: auto; 192 | margin: 0; 193 | border-top: 2px groove threedhighlight; 194 | padding: 0.4em 0 0; 195 | font: smaller/1.5em sans-serif; 196 | } 197 | 198 | #qunit-modulefilter-dropdown-list li { 199 | white-space: nowrap; 200 | overflow: hidden; 201 | text-overflow: ellipsis; 202 | } 203 | 204 | #qunit-modulefilter-dropdown-list .clickable { 205 | display: block; 206 | padding-left: 0.15em; 207 | } 208 | 209 | 210 | /** Tests: Pass/Fail */ 211 | 212 | #qunit-tests { 213 | list-style-position: inside; 214 | } 215 | 216 | #qunit-tests li { 217 | padding: 0.4em 1em 0.4em 1em; 218 | border-bottom: 1px solid #FFF; 219 | list-style-position: inside; 220 | } 221 | 222 | #qunit-tests > li { 223 | display: none; 224 | } 225 | 226 | #qunit-tests li.running, 227 | #qunit-tests li.pass, 228 | #qunit-tests li.fail, 229 | #qunit-tests li.skipped, 230 | #qunit-tests li.aborted { 231 | display: list-item; 232 | } 233 | 234 | #qunit-tests.hidepass { 235 | position: relative; 236 | } 237 | 238 | #qunit-tests.hidepass li.running, 239 | #qunit-tests.hidepass li.pass:not(.todo) { 240 | visibility: hidden; 241 | position: absolute; 242 | width: 0; 243 | height: 0; 244 | padding: 0; 245 | border: 0; 246 | margin: 0; 247 | } 248 | 249 | #qunit-tests li strong { 250 | cursor: pointer; 251 | } 252 | 253 | #qunit-tests li.skipped strong { 254 | cursor: default; 255 | } 256 | 257 | #qunit-tests li a { 258 | padding: 0.5em; 259 | color: #C2CCD1; 260 | text-decoration: none; 261 | } 262 | 263 | #qunit-tests li p a { 264 | padding: 0.25em; 265 | color: #6B6464; 266 | } 267 | #qunit-tests li a:hover, 268 | #qunit-tests li a:focus { 269 | color: #000; 270 | } 271 | 272 | #qunit-tests li .runtime { 273 | float: right; 274 | font-size: smaller; 275 | } 276 | 277 | .qunit-assert-list { 278 | margin-top: 0.5em; 279 | padding: 0.5em; 280 | 281 | background-color: #FFF; 282 | 283 | border-radius: 5px; 284 | } 285 | 286 | .qunit-source { 287 | margin: 0.6em 0 0.3em; 288 | } 289 | 290 | .qunit-collapsed { 291 | display: none; 292 | } 293 | 294 | #qunit-tests table { 295 | border-collapse: collapse; 296 | margin-top: 0.2em; 297 | } 298 | 299 | #qunit-tests th { 300 | text-align: right; 301 | vertical-align: top; 302 | padding: 0 0.5em 0 0; 303 | } 304 | 305 | #qunit-tests td { 306 | vertical-align: top; 307 | } 308 | 309 | #qunit-tests pre { 310 | margin: 0; 311 | white-space: pre-wrap; 312 | word-wrap: break-word; 313 | } 314 | 315 | #qunit-tests del { 316 | color: #374E0C; 317 | background-color: #E0F2BE; 318 | text-decoration: none; 319 | } 320 | 321 | #qunit-tests ins { 322 | color: #500; 323 | background-color: #FFCACA; 324 | text-decoration: none; 325 | } 326 | 327 | /*** Test Counts */ 328 | 329 | #qunit-tests b.counts { color: #000; } 330 | #qunit-tests b.passed { color: #5E740B; } 331 | #qunit-tests b.failed { color: #710909; } 332 | 333 | #qunit-tests li li { 334 | padding: 5px; 335 | background-color: #FFF; 336 | border-bottom: none; 337 | list-style-position: inside; 338 | } 339 | 340 | /*** Passing Styles */ 341 | 342 | #qunit-tests li li.pass { 343 | color: #3C510C; 344 | background-color: #FFF; 345 | border-left: 10px solid #C6E746; 346 | } 347 | 348 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } 349 | #qunit-tests .pass .test-name { color: #366097; } 350 | 351 | #qunit-tests .pass .test-actual, 352 | #qunit-tests .pass .test-expected { color: #999; } 353 | 354 | #qunit-banner.qunit-pass { background-color: #C6E746; } 355 | 356 | /*** Failing Styles */ 357 | 358 | #qunit-tests li li.fail { 359 | color: #710909; 360 | background-color: #FFF; 361 | border-left: 10px solid #EE5757; 362 | white-space: pre; 363 | } 364 | 365 | #qunit-tests > li:last-child { 366 | border-radius: 0 0 5px 5px; 367 | } 368 | 369 | #qunit-tests .fail { color: #000; background-color: #EE5757; } 370 | #qunit-tests .fail .test-name, 371 | #qunit-tests .fail .module-name { color: #000; } 372 | 373 | #qunit-tests .fail .test-actual { color: #EE5757; } 374 | #qunit-tests .fail .test-expected { color: #008000; } 375 | 376 | #qunit-banner.qunit-fail { background-color: #EE5757; } 377 | 378 | 379 | /*** Aborted tests */ 380 | #qunit-tests .aborted { color: #000; background-color: orange; } 381 | /*** Skipped tests */ 382 | 383 | #qunit-tests .skipped { 384 | background-color: #EBECE9; 385 | } 386 | 387 | #qunit-tests .qunit-todo-label, 388 | #qunit-tests .qunit-skipped-label { 389 | background-color: #F4FF77; 390 | display: inline-block; 391 | font-style: normal; 392 | color: #366097; 393 | line-height: 1.8em; 394 | padding: 0 0.5em; 395 | margin: -0.4em 0.4em -0.4em 0; 396 | } 397 | 398 | #qunit-tests .qunit-todo-label { 399 | background-color: #EEE; 400 | } 401 | 402 | /** Result */ 403 | 404 | #qunit-testresult { 405 | color: #2B81AF; 406 | background-color: #D2E0E6; 407 | 408 | border-bottom: 1px solid #FFF; 409 | } 410 | #qunit-testresult .clearfix { 411 | height: 0; 412 | clear: both; 413 | } 414 | #qunit-testresult .module-name { 415 | font-weight: 700; 416 | } 417 | #qunit-testresult-display { 418 | padding: 0.5em 1em 0.5em 1em; 419 | width: 85%; 420 | float:left; 421 | } 422 | #qunit-testresult-controls { 423 | padding: 0.5em 1em 0.5em 1em; 424 | width: 10%; 425 | float:left; 426 | } 427 | 428 | /** Fixture */ 429 | 430 | #qunit-fixture { 431 | position: absolute; 432 | top: -10000px; 433 | left: -10000px; 434 | width: 1000px; 435 | height: 1000px; 436 | } 437 | -------------------------------------------------------------------------------- /test/test.ie.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | QUnit.test("Basic test", function (assert) { 4 | var arr = [1, 2, 3, 4, 5]; 5 | 6 | var result = qone({ arr: arr }).query("\n from n in arr \n where n > 3\n select n\n "); 7 | 8 | assert.deepEqual(result, [4, 5]); 9 | }); 10 | 11 | QUnit.test("Query json array", function (assert) { 12 | var list = [{ name: 'qone', age: 1 }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]; 13 | 14 | var result = qone({ list: list }).query("\n from n in list \n where n.age > 18\n select n\n "); 15 | 16 | assert.deepEqual(result, [{ "name": "dntzhang", "age": 28 }]); 17 | }); 18 | 19 | QUnit.test("Multi conditional query", function (assert) { 20 | var list = [{ name: 'qone', age: 1 }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]; 21 | 22 | var result = qone({ list: list }).query("\n from n in list \n where n.age > 10 && n.name = 'linq'\n select n\n "); 23 | 24 | assert.deepEqual(result, [{ "name": "linq", "age": 18 }]); 25 | }); 26 | 27 | QUnit.test("Select test", function (assert) { 28 | var list = [{ name: 'qone', age: 1 }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]; 29 | 30 | var result = qone({ list: list }).query("\n from n in list \n where n.age < 20\n select n.age, n.name\n "); 31 | 32 | assert.deepEqual(result, [[1, "qone"], [18, "linq"]]); 33 | }); 34 | 35 | QUnit.test("Select JSON test", function (assert) { 36 | var list = [{ name: 'qone', age: 1 }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]; 37 | 38 | var result = qone({ list: list }).query("\n from n in list \n where n.age < 20\n select {n.age, n.name}\n "); 39 | 40 | assert.deepEqual(result, [{ "age": 1, "name": "qone" }, { "age": 18, "name": "linq" }]); 41 | }); 42 | 43 | QUnit.test("Select full JSON test", function (assert) { 44 | var list = [{ name: 'qone', age: 1 }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]; 45 | 46 | var result = qone({ list: list }).query("\n from n in list \n where n.age < 20\n select {a : n.age, b : n.name}\n "); 47 | 48 | assert.deepEqual(result, [{ "a": 1, "b": "qone" }, { "a": 18, "b": "linq" }]); 49 | }); 50 | 51 | QUnit.test("|| conditional test", function (assert) { 52 | var list = [{ name: 'qone', age: 1 }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]; 53 | 54 | var result = qone({ list: list }).query("\n from n in list \n where n.age > 19 || n.age < 2\n select n\n "); 55 | 56 | assert.deepEqual(result, [{ "name": "qone", "age": 1 }, { "name": "dntzhang", "age": 28 }]); 57 | }); 58 | 59 | QUnit.test("|| and && conditional test", function (assert) { 60 | var list = [{ name: 'qone', age: 0 }, { name: 'linq', age: 1 }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]; 61 | 62 | var result = qone({ list: list }).query("\n from n in list \n where n.age > 17 || n.age < 2 && n.name != 'dntzhang'\n select n\n "); 63 | 64 | assert.deepEqual(result, [{ "name": "qone", "age": 0 }, { "name": "linq", "age": 1 }, { "name": "linq", "age": 18 }, { "name": "dntzhang", "age": 28 }]); 65 | }); 66 | 67 | QUnit.test("(), || and && conditional test", function (assert) { 68 | var list = [{ name: 'qone', age: 0 }, { name: 'linq', age: 1 }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]; 69 | 70 | var result = qone({ list: list }).query("\n from n in list \n where (n.age > 17 || n.age < 2) && n.name != 'dntzhang'\n select n\n "); 71 | 72 | assert.deepEqual(result, [{ "name": "qone", "age": 0 }, { "name": "linq", "age": 1 }, { "name": "linq", "age": 18 }]); 73 | }); 74 | 75 | QUnit.test("Query from prop", function (assert) { 76 | var data = { 77 | users: [{ name: 'qone', age: 1 }, { name: 'dntzhang', age: 17 }, { name: 'dntzhang2', age: 27 }] 78 | }; 79 | 80 | var result = qone({ data: data }).query("\n from n in data.users \n where n.age < 10\n select n\n "); 81 | 82 | assert.deepEqual(result, [{ "name": "qone", "age": 1 }]); 83 | }); 84 | 85 | QUnit.test("Multi datasource ", function (assert) { 86 | 87 | var listA = [{ name: 'qone', age: 1 }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]; 88 | 89 | var listB = [{ name: 'x', age: 11 }, { name: 'xx', age: 12 }, { name: 'xxx', age: 13 }]; 90 | 91 | var result = qone({ listA: listA, listB: listB }).query("\n from a in listA \n from b in listB \n where a.age < 20 && b.age > 11\n select a, b\n "); 92 | 93 | assert.deepEqual(result, [[{ "name": "qone", "age": 1 }, { "name": "xx", "age": 12 }], [{ "name": "qone", "age": 1 }, { "name": "xxx", "age": 13 }], [{ "name": "linq", "age": 18 }, { "name": "xx", "age": 12 }], [{ "name": "linq", "age": 18 }, { "name": "xxx", "age": 13 }]]); 94 | }); 95 | 96 | QUnit.test("Multi datasource with props condition", function (assert) { 97 | 98 | var listA = [{ name: 'qone', age: 1 }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]; 99 | 100 | var listB = [{ name: 'x', age: 11 }, { name: 'xx', age: 18 }, { name: 'xxx', age: 13 }]; 101 | 102 | var result = qone({ listA: listA, listB: listB }).query("\n from a in listA \n from b in listB \n where a.age = b.age\n select a, b\n "); 103 | 104 | assert.deepEqual(result, [[{ "name": "linq", "age": 18 }, { "name": "xx", "age": 18 }]]); 105 | }); 106 | 107 | QUnit.test("Bool condition ", function (assert) { 108 | 109 | var list = [{ name: 'qone', age: 1, isBaby: true }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]; 110 | 111 | var result = qone({ list: list }).query("\n from a in list \n where a.isBaby\n select a\n "); 112 | 113 | assert.deepEqual(result, [{ name: 'qone', age: 1, isBaby: true }]); 114 | }); 115 | 116 | QUnit.test("Bool condition ", function (assert) { 117 | 118 | var list = [{ name: 'qone', age: 1, isBaby: [true] }, { name: 'linq', age: 18, isBaby: [false] }, { name: 'dntzhang', age: 28, isBaby: [false] }]; 119 | 120 | var result = qone({ list: list }).query("\n from a in list \n where a.isBaby[0]\n select a\n "); 121 | 122 | assert.deepEqual(result, [{ name: 'qone', age: 1, isBaby: [true] }]); 123 | }); 124 | 125 | QUnit.test("Multi from test ", function (assert) { 126 | 127 | var list = [{ name: 'qone', age: 1, isBaby: true, colors: ['red', 'green', 'blue'] }, { name: 'linq', age: 18, colors: ['red', 'blue'] }, { name: 'dntzhang', age: 28, colors: ['red', 'blue'] }]; 128 | 129 | var result = qone({ list: list }).query("\n from a in list \n from c in a.colors \n where c = 'green'\n select a, c\n "); 130 | 131 | assert.deepEqual(result, [[{ "name": "qone", "age": 1, "isBaby": true, "colors": ["red", "green", "blue"] }, "green"]]); 132 | }); 133 | 134 | QUnit.test("Multi deep from test ", function (assert) { 135 | 136 | var list = [{ name: 'qone', age: 1, isBaby: true, colors: [{ xx: [1, 2, 3] }, { xx: [11, 2, 3] }] }, { name: 'linq', age: 18, colors: [{ xx: [100, 2, 3] }, { xx: [11, 2, 3] }] }, { name: 'dntzhang', age: 28, colors: [{ xx: [1, 2, 3] }, { xx: [11, 2, 3] }] }]; 137 | 138 | var result = qone({ list: list }).query("\n from a in list \n from c in a.colors \n from d in c.xx \n select a, c,d\n "); 139 | 140 | assert.equal(result.length, 18); 141 | }); 142 | 143 | QUnit.test("Multi deep from test ", function (assert) { 144 | 145 | var list = [{ name: 'qone', age: 1, isBaby: true, colors: [{ xx: [1, 2, 3] }, { xx: [11, 2, 3] }] }, { name: 'linq', age: 18, colors: [{ xx: [100, 2, 3] }, { xx: [11, 2, 3] }] }, { name: 'dntzhang', age: 28, colors: [{ xx: [1, 2, 3] }, { xx: [11, 2, 3] }] }]; 146 | 147 | var result = qone({ list: list }).query("\n from a in list \n from c in a.colors \n from d in c.xx \n where d === 100\n select a.name, c,d\n "); 148 | 149 | assert.deepEqual(result, [["linq", { "xx": [100, 2, 3] }, 100]]); 150 | }); 151 | 152 | QUnit.test("Deep prop path test ", function (assert) { 153 | 154 | var list = [{ name: 'qone', age: 1, fullName: { first: 'q', last: 'one' } }, { name: 'linq', age: 18, fullName: { first: 'l', last: 'inq' } }, { name: 'dntzhang', age: 28, fullName: { first: 'dnt', last: 'zhang' } }]; 155 | 156 | var result = qone({ list: list }).query("\n from a in list \n where a.fullName.first === 'dnt'\n select a.name\n "); 157 | 158 | assert.deepEqual(result, ["dntzhang"]); 159 | }); 160 | 161 | QUnit.test("Method test 1", function (assert) { 162 | var arr = [1, 2, 3, 4, 5]; 163 | 164 | qone('square', function (num) { 165 | return num * num; 166 | }); 167 | 168 | var result = qone({ arr: arr }).query("\n from n in arr \n where square(n) > 10\n select n\n "); 169 | 170 | assert.deepEqual(result, [4, 5]); 171 | }); 172 | 173 | QUnit.test("Method test 2", function (assert) { 174 | var arr = [1, 2, 3, 4, 5]; 175 | 176 | qone('square', function (num) { 177 | return num * num; 178 | }); 179 | 180 | var result = qone({ arr: arr }).query("\n from n in arr \n where 10 > square(n)\n select n\n "); 181 | 182 | assert.deepEqual(result, [1, 2, 3]); 183 | }); 184 | 185 | QUnit.test("Method test 3", function (assert) { 186 | var arr = [1, 2, 3, 4, 5]; 187 | 188 | qone('square', function (num) { 189 | return num * num; 190 | }); 191 | 192 | var result = qone({ arr: arr }).query("\n from n in arr \n where n > 3\n select square(n)\n "); 193 | 194 | assert.deepEqual(result, [16, 25]); 195 | }); 196 | 197 | QUnit.test("Method test 4", function (assert) { 198 | var arr = [1, 2, 3, 4, 5]; 199 | 200 | qone('square', function (num) { 201 | return num * num; 202 | }); 203 | 204 | var result = qone({ arr: arr }).query("\n from n in arr \n where n > 3\n select n, square(n)\n "); 205 | 206 | assert.deepEqual(result, [[4, 16], [5, 25]]); 207 | }); 208 | 209 | QUnit.test("Method test 5", function (assert) { 210 | var arr = [1, 2, 3, 4, 5]; 211 | 212 | qone('square', function (num) { 213 | return num * num; 214 | }); 215 | 216 | var result = qone({ arr: arr }).query("\n from n in arr \n where n > 3\n select { value : n, squareValue : square(n) }\n "); 217 | 218 | assert.deepEqual(result, [{ "value": 4, "squareValue": 16 }, { "value": 5, "squareValue": 25 }]); 219 | }); 220 | 221 | QUnit.test("Method test 6", function (assert) { 222 | var arr = [1, 2, 3, 4, 5]; 223 | 224 | qone('square', function (num) { 225 | return num * num; 226 | }); 227 | 228 | var result = qone({ arr: arr }).query("\n from n in arr \n where n > 3\n select { squareValue : square(n) }\n "); 229 | 230 | assert.deepEqual(result, [{ "squareValue": 16 }, { "squareValue": 25 }]); 231 | }); 232 | 233 | QUnit.test("Method test 7", function (assert) { 234 | var arr = [1, 2, 3, 4, 5]; 235 | 236 | qone('square', function (num) { 237 | return num * num; 238 | }); 239 | 240 | var result = qone({ arr: arr }).query("\n from n in arr \n where n > 3\n select { squareValue : square(n) }\n "); 241 | 242 | assert.deepEqual(result, [{ "squareValue": 16 }, { "squareValue": 25 }]); 243 | }); 244 | 245 | QUnit.test("Method test 8", function (assert) { 246 | var arr = [1, 2, 3, 4, 5]; 247 | 248 | qone('square', function (num) { 249 | return num * num; 250 | }); 251 | 252 | qone('sqrt', function (num) { 253 | return Math.sqrt(num); 254 | }); 255 | 256 | var result = qone({ arr: arr }).query("\n from n in arr \n where sqrt(n) >= 2 \n select { squareValue : square(n) }\n "); 257 | 258 | assert.deepEqual(result, [{ "squareValue": 16 }, { "squareValue": 25 }]); 259 | }); 260 | 261 | QUnit.test("Method test 9", function (assert) { 262 | var list = [{ name: 'qone', age: 1 }, { name: 'linq', age: 19 }, { name: 'dntzhang', age: 28 }]; 263 | 264 | qone('greaterThan18', function (num) { 265 | return num > 18; 266 | }); 267 | 268 | var result = qone({ list: list }).query("\n from n in list \n where greaterThan18(n.age) && n.name = 'dntzhang'\n select n\n "); 269 | 270 | assert.deepEqual(result, [{ "name": "dntzhang", "age": 28 }]); 271 | }); 272 | 273 | QUnit.test("Method test 10", function (assert) { 274 | var list = [{ name: 'qone', age: 1 }, { name: 'linq', age: 19 }, { name: 'dntzhang', age: 28 }]; 275 | 276 | qone('greaterThan18', function (num) { 277 | return num > 18; 278 | }); 279 | 280 | var result = qone({ list: list }).query("\n from n in list \n where greaterThan18(n.age) && n.name = 'dntzhang'\n select n\n "); 281 | 282 | assert.deepEqual(result, [{ "name": "dntzhang", "age": 28 }]); 283 | }); 284 | 285 | QUnit.test("Order by test 1", function (assert) { 286 | var list = [{ name: 'linq2', age: 7 }, { name: 'qone', age: 1 }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]; 287 | 288 | var result = qone({ list: list }).query("\n from n in list \n where n.age > 0\n orderby n.age desc\n select n\n "); 289 | 290 | assert.deepEqual(result, [{ "name": "dntzhang", "age": 28 }, { "name": "linq", "age": 18 }, { "name": "linq2", "age": 7 }, { "name": "qone", "age": 1 }]); 291 | }); 292 | 293 | QUnit.test("Order by test 2", function (assert) { 294 | var list = [{ name: 'linq2', age: 7 }, { name: 'qone', age: 1 }, { name: 'linq', age: 18 }, { name: 'ainq', age: 18 }, { name: 'dntzhang', age: 28 }]; 295 | 296 | var result = qone({ list: list }).query("\n from n in list \n where n.age > 0\n orderby n.age asc, n.name desc\n select n\n "); 297 | 298 | assert.deepEqual(result, [{ "name": "qone", "age": 1 }, { "name": "linq2", "age": 7 }, { "name": "linq", "age": 18 }, { "name": "ainq", "age": 18 }, { "name": "dntzhang", "age": 28 }]); 299 | }); 300 | 301 | QUnit.test("Order by test 3", function (assert) { 302 | var list = [{ name: 'qone', age: 17, age2: 2 }, { name: 'linq', age: 18, age2: 1 }, { name: 'dntzhang1', age: 28, age2: 2 }, { name: 'dntzhang2', age: 28, age2: 1 }, { name: 'dntzhang3', age: 29, age2: 1 }]; 303 | 304 | qone('sum', function (a, b) { 305 | return a + b; 306 | }); 307 | 308 | var result = qone({ list: list }).query("\n from n in list \n where n.age > 0\n orderby sum(n.age,n.age2) \n select n\n "); 309 | 310 | assert.deepEqual(result, [{ name: 'qone', age: 17, age2: 2 }, { name: 'linq', age: 18, age2: 1 }, { name: 'dntzhang2', age: 28, age2: 1 }, { name: 'dntzhang1', age: 28, age2: 2 }, { name: 'dntzhang3', age: 29, age2: 1 }]); 311 | }); 312 | 313 | QUnit.test("Order by test 4", function (assert) { 314 | var list = [{ name: 'qone', age: 17, age2: 2 }, { name: 'linq', age: 18, age2: 1 }, { name: 'dntzhang1', age: 28, age2: 2 }, { name: 'dntzhang2', age: 28, age2: 1 }, { name: 'dntzhang3', age: 29, age2: 1 }]; 315 | 316 | qone('sum', function (a, b) { 317 | return a + b; 318 | }); 319 | 320 | var result = qone({ list: list }).query("\n from n in list \n where n.age > 0\n orderby sum(n.age,n.age2) ,n.name\n select n\n "); 321 | 322 | assert.deepEqual(result, [{ name: 'linq', age: 18, age2: 1 }, { name: 'qone', age: 17, age2: 2 }, { name: 'dntzhang2', age: 28, age2: 1 }, { name: 'dntzhang1', age: 28, age2: 2 }, { name: 'dntzhang3', age: 29, age2: 1 }]); 323 | }); 324 | 325 | QUnit.test("Simple groupby test 1", function (assert) { 326 | var list = [{ name: 'qone', age: 1 }, { name: 'linq', age: 18 }, { name: 'dntzhang1', age: 28 }, { name: 'dntzhang2', age: 28 }, { name: 'dntzhang3', age: 29 }]; 327 | 328 | var result = qone({ list: list }).query("\n from n in list \n where n.age > 18\n groupby n.age\n "); 329 | 330 | assert.deepEqual(result, [[{ "name": "dntzhang1", "age": 28 }, { "name": "dntzhang2", "age": 28 }], [{ "name": "dntzhang3", "age": 29 }]]); 331 | }); 332 | 333 | QUnit.test("Simple groupby test 2", function (assert) { 334 | var list = [{ name: 'qone', age: 1 }, { name: 'qone', age: 1 }, { name: 'dntzhang', age: 28 }, { name: 'dntzhang2', age: 28 }, { name: 'dntzhang', age: 29 }]; 335 | 336 | var result = qone({ list: list }).query("\n from n in list \n where n.age > 0\n groupby n.age,n.name\n "); 337 | 338 | assert.deepEqual(result, [[{ "name": "qone", "age": 1 }, { "name": "qone", "age": 1 }], [{ "name": "dntzhang", "age": 28 }], [{ "name": "dntzhang2", "age": 28 }], [{ "name": "dntzhang", "age": 29 }]]); 339 | }); 340 | 341 | QUnit.test("Simple groupby with method", function (assert) { 342 | var list = [{ name: 'qone', age: 17, age2: 2 }, { name: 'linq', age: 18, age2: 1 }, { name: 'dntzhang1', age: 28, age2: 1 }, { name: 'dntzhang2', age: 28, age2: 1 }, { name: 'dntzhang3', age: 29, age2: 1 }]; 343 | qone('sum', function (a, b) { 344 | return a + b; 345 | }); 346 | 347 | var result = qone({ list: list }).query("\n from n in list \n groupby sum(n.age,n.age2)\n "); 348 | 349 | assert.deepEqual(result, [[{ name: 'qone', age: 17, age2: 2 }, { name: 'linq', age: 18, age2: 1 }], [{ name: 'dntzhang1', age: 28, age2: 1 }, { name: 'dntzhang2', age: 28, age2: 1 }], [{ name: 'dntzhang3', age: 29, age2: 1 }]]); 350 | }); 351 | 352 | QUnit.test("Simple groupby with method", function (assert) { 353 | var list = [{ name: 'qone', age: 17, age2: 2 }, { name: 'qone', age: 18, age2: 1 }, { name: 'dntzhang1', age: 28, age2: 1 }, { name: 'dntzhang2', age: 28, age2: 1 }, { name: 'dntzhang3', age: 29, age2: 1 }]; 354 | qone('sum', function (a, b) { 355 | return a + b; 356 | }); 357 | 358 | var result = qone({ list: list }).query("\n from n in list \n groupby sum(n.age,n.age2), n.name\n "); 359 | 360 | assert.deepEqual(result, [[{ name: 'qone', age: 17, age2: 2 }, { name: 'qone', age: 18, age2: 1 }], [{ name: 'dntzhang1', age: 28, age2: 1 }], [{ name: 'dntzhang2', age: 28, age2: 1 }], [{ name: 'dntzhang3', age: 29, age2: 1 }]]); 361 | }); 362 | 363 | QUnit.test("Bool condition 1 ", function (assert) { 364 | 365 | var list = [{ name: 'qone', age: 1, isBaby: true }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]; 366 | 367 | var result = qone({ list: list }).query("\n from a in list \n where !a.isBaby\n select a\n "); 368 | 369 | assert.deepEqual(result, [{ "name": "linq", "age": 18 }, { "name": "dntzhang", "age": 28 }]); 370 | }); 371 | 372 | QUnit.test("Bool condition 2 ", function (assert) { 373 | 374 | var list = [{ name: 'qone', age: 1, isBaby: true }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]; 375 | 376 | var result = qone({ list: list }).query("\n from a in list \n where !a.isBaby && a.name='qone'\n select a\n "); 377 | 378 | assert.deepEqual(result, []); 379 | }); 380 | 381 | QUnit.test("Bool condition 3 ", function (assert) { 382 | 383 | var list = [{ name: 'qone', age: 1, isBaby: true }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]; 384 | 385 | var result = qone({ list: list }).query("\n from a in list \n where !(a.isBaby && a.name='qone') \n select a\n "); 386 | 387 | assert.deepEqual(result, [{ name: 'linq', age: 18 }, { "name": "dntzhang", "age": 28 }]); 388 | }); 389 | 390 | QUnit.test("Bool condition 4 ", function (assert) { 391 | 392 | var list = [{ name: 'qone', age: 1, isBaby: true }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]; 393 | 394 | var result = qone({ list: list }).query("\n from a in list \n where !(a.isBaby && a.name='qone') && a.age = 28\n select a\n "); 395 | 396 | assert.deepEqual(result, [{ "name": "dntzhang", "age": 28 }]); 397 | }); 398 | 399 | QUnit.test("Bool condition 5 ", function (assert) { 400 | 401 | var list = [{ name: 'qone', age: 1, isBaby: true }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]; 402 | 403 | var result = qone({ list: list }).query("\n from a in list \n where a.isBaby = true\n select a\n "); 404 | 405 | assert.deepEqual(result, [{ name: 'qone', age: 1, isBaby: true }]); 406 | }); 407 | 408 | QUnit.test("Bool condition 6 ", function (assert) { 409 | 410 | var list = [{ name: 'qone', age: 1, isBaby: true }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]; 411 | 412 | var result = qone({ list: list }).query("\n from a in list \n where a.isBaby = true\n select a\n "); 413 | 414 | assert.deepEqual(result, [{ name: 'qone', age: 1, isBaby: true }]); 415 | }); 416 | 417 | QUnit.test("Bool condition 7 ", function (assert) { 418 | 419 | var list = [{ name: 'qone', age: 1, isBaby: true }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]; 420 | 421 | var result = qone({ list: list }).query("\n from a in list \n where a.isBaby = true\n select a\n "); 422 | 423 | assert.deepEqual(result, [{ name: 'qone', age: 1, isBaby: true }]); 424 | }); 425 | 426 | QUnit.test("Bool condition 8 ", function (assert) { 427 | 428 | var list = [{ name: 'qone', age: 1, isBaby: true }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]; 429 | 430 | var result = qone({ list: list }).query("\n from a in list \n where a.isBaby = undefined\n select a\n "); 431 | 432 | assert.deepEqual(result, [{ name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]); 433 | }); 434 | 435 | QUnit.test("Bool condition 9 ", function (assert) { 436 | 437 | var list = [{ name: 'qone', age: 1, isBaby: true }, { name: 'linq', age: 18, isBaby: false }, { name: 'dntzhang', age: 28, isBaby: false }]; 438 | 439 | var result = qone({ list: list }).query("\n from a in list \n where a.isBaby = false\n select a\n "); 440 | 441 | assert.deepEqual(result, [{ name: 'linq', age: 18, isBaby: false }, { name: 'dntzhang', age: 28, isBaby: false }]); 442 | }); 443 | 444 | QUnit.test("Bool condition 10 ", function (assert) { 445 | 446 | var list = [{ name: 'qone', age: 1, isBaby: true }, { name: 'linq', age: 18, isBaby: false }, { name: 'dntzhang', age: 28, isBaby: null }]; 447 | 448 | var result = qone({ list: list }).query("\n from a in list \n where a.isBaby = false || a.isBaby = null\n select a\n "); 449 | 450 | assert.deepEqual(result, [{ name: 'linq', age: 18, isBaby: false }, { name: 'dntzhang', age: 28, isBaby: null }]); 451 | }); 452 | 453 | QUnit.test("Bool condition 11 ", function (assert) { 454 | 455 | var list = [{ name: 'qone', age: 1, isBaby: true }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]; 456 | 457 | var result = qone({ list: list }).query("\n from a in list \n where !((a.isBaby && a.name='qone') && a.age = 28)\n select a\n "); 458 | 459 | assert.deepEqual(result, [{ "name": "qone", "age": 1, "isBaby": true }, { "name": "linq", "age": 18 }, { "name": "dntzhang", "age": 28 }]); 460 | }); 461 | 462 | QUnit.test("Bool condition 12", function (assert) { 463 | 464 | var list = [{ name: 'qone', age: 1, isBaby: true }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]; 465 | 466 | var result = qone({ list: list }).query("\n from a in list \n where !((a.isBaby && a.name='qone') && ((a.age = 28)||!a.isBaby))\n select a\n "); 467 | 468 | assert.deepEqual(result, [{ name: 'qone', age: 1, isBaby: true }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]); 469 | }); 470 | 471 | QUnit.test("Bool condition 13", function (assert) { 472 | 473 | var list = [{ name: 'qone', age: 1, isBaby: true }, { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]; 474 | 475 | var result = qone({ list: list }).query("\n from a in list \n where ((a.isBaby && a.name='qone') && !((a.age = 28)||!a.isBaby))\n select a\n "); 476 | 477 | assert.deepEqual(result, [{ name: 'qone', age: 1, isBaby: true }]); 478 | }); 479 | 480 | QUnit.test("Method test", function (assert) { 481 | 482 | var list = [{ name: 'qone', age: 1, scores: [1, 2, 3] }, { name: 'linq', age: 18, scores: [11, 2, 3] }, { name: 'dntzhang', age: 28, scores: [100, 2, 3] }]; 483 | 484 | qone('fullCredit', function (item) { 485 | return item[0] === 100; 486 | }); 487 | 488 | var result = qone({ list: list }).query("\n from a in list \n where fullCredit(a.scores)\n select a\n "); 489 | 490 | assert.deepEqual(result, [{ name: 'dntzhang', age: 28, scores: [100, 2, 3] }]); 491 | }); 492 | 493 | QUnit.test("Access array", function (assert) { 494 | 495 | var list = [{ name: 'qone', age: 1, scores: [1, 2, 3] }, { name: 'linq', age: 18, scores: [11, 2, 3] }, { name: 'dntzhang', age: 28, scores: [100, 2, 3] }]; 496 | 497 | var result = qone({ list: list }).query("\n from a in list \n where a.scores[0] === 100\n select a\n "); 498 | 499 | assert.deepEqual(result, [{ name: 'dntzhang', age: 28, scores: [100, 2, 3] }]); 500 | }); 501 | 502 | QUnit.test("Access array", function (assert) { 503 | 504 | var list = [{ name: 'qone', age: 1, scores: [{ a: 1 }, 2, 3] }, { name: 'linq', age: 18, scores: [{ a: 2 }, 2, 3] }, { name: 'dntzhang', age: 28, scores: [{ a: 3 }, 2, 3] }]; 505 | 506 | var result = qone({ list: list }).query("\n from a in list \n where a.scores[0].a === 3\n select a\n "); 507 | 508 | assert.deepEqual(result, [{ name: 'dntzhang', age: 28, scores: [{ a: 3 }, 2, 3] }]); 509 | }); 510 | 511 | QUnit.test("Access array", function (assert) { 512 | 513 | var list = [{ name: 'qone', age: 1, scores: [[1, 2], 2, 3] }, { name: 'linq', age: 18, scores: [[3, 4], 2, 3] }, { name: 'dntzhang', age: 28, scores: [[5, 6], 2, 3] }]; 514 | 515 | var result = qone({ list: list }).query("\n from a in list \n where a.scores[0][1] === 6\n select a\n "); 516 | 517 | assert.deepEqual(result, [{ name: 'dntzhang', age: 28, scores: [[5, 6], 2, 3] }]); 518 | }); 519 | 520 | QUnit.test("Method test", function (assert) { 521 | 522 | var list = [{ name: 'qone', age: 1, scores: [1, 2, 3] }, { name: 'linq', age: 18, scores: [11, 2, 3] }, { name: 'dntzhang', age: 28, scores: [100, 2, 3] }]; 523 | 524 | qone('fullCredit', function (item, x, y) { 525 | return item[0] * x + y === '22abc'; 526 | }); 527 | 528 | var result = qone({ list: list }).query("\n from a in list \n where fullCredit(a.scores, 2, \"abc\")\n select a\n "); 529 | 530 | assert.deepEqual(result, [{ name: 'linq', age: 18, scores: [11, 2, 3] }]); 531 | }); 532 | 533 | QUnit.test("Single line test", function (assert) { 534 | var list = [{ name: 'dntzhang1', age: 1 }, { name: 'dntzhang2', age: 2 }, { name: 'dntzhang3', age: 3 }, { name: 'dntzhang4', age: 4 }, { name: 'dntzhang5', age: 5 }, { name: 'dntzhang6', age: 6 }, { name: 'dntzhang7', age: 7 }, { name: 'dntzhang8', age: 8 }, { name: 'dntzhang9', age: 9 }, { name: 'dntzhang10', age: 10 }]; 535 | 536 | var result = qone({ list: list }).query('from n in list where n.age > 1 select n limit 1, 3'); 537 | 538 | assert.deepEqual(result, [{ name: 'dntzhang3', age: 3 }, { name: 'dntzhang4', age: 4 }, { name: 'dntzhang5', age: 5 }]); 539 | }); 540 | 541 | QUnit.test("Limit one page test", function (assert) { 542 | var list = [{ name: 'dntzhang1', age: 1 }, { name: 'dntzhang2', age: 2 }, { name: 'dntzhang3', age: 3 }, { name: 'dntzhang4', age: 4 }, { name: 'dntzhang5', age: 5 }, { name: 'dntzhang6', age: 6 }, { name: 'dntzhang7', age: 7 }, { name: 'dntzhang8', age: 8 }, { name: 'dntzhang9', age: 9 }, { name: 'dntzhang10', age: 10 }]; 543 | 544 | var pageIndex = 1, 545 | pageSize = 4; 546 | var result = qone({ list: list }).query("\n from n in list \n where n.age > 0\n select n\n limit " + pageIndex * pageSize + ", " + pageSize + "\n "); 547 | 548 | assert.deepEqual(result, [{ name: 'dntzhang5', age: 5 }, { name: 'dntzhang6', age: 6 }, { name: 'dntzhang7', age: 7 }, { name: 'dntzhang8', age: 8 }]); 549 | }); 550 | 551 | QUnit.test("Limit top 3", function (assert) { 552 | var list = [{ name: 'dntzhang1', age: 1 }, { name: 'dntzhang2', age: 2 }, { name: 'dntzhang3', age: 3 }, { name: 'dntzhang4', age: 4 }, { name: 'dntzhang5', age: 5 }, { name: 'dntzhang6', age: 6 }, { name: 'dntzhang7', age: 7 }, { name: 'dntzhang8', age: 8 }, { name: 'dntzhang9', age: 9 }, { name: 'dntzhang10', age: 10 }]; 553 | 554 | var pageIndex = 1, 555 | pageSize = 4; 556 | var result = qone({ list: list }).query("\n from n in list \n select n\n limit 0, 3\n "); 557 | 558 | assert.deepEqual(result, [{ name: 'dntzhang1', age: 1 }, { name: 'dntzhang2', age: 2 }, { name: 'dntzhang3', age: 3 }]); 559 | }); 560 | 561 | QUnit.test("Limit top 13", function (assert) { 562 | var list = [{ name: 'dntzhang1', age: 1 }, { name: 'dntzhang2', age: 2 }, { name: 'dntzhang3', age: 3 }, { name: 'dntzhang4', age: 4 }, { name: 'dntzhang5', age: 5 }, { name: 'dntzhang6', age: 6 }, { name: 'dntzhang7', age: 7 }, { name: 'dntzhang8', age: 8 }, { name: 'dntzhang9', age: 9 }, { name: 'dntzhang10', age: 10 }]; 563 | 564 | var pageIndex = 1, 565 | pageSize = 4; 566 | var result = qone({ list: list }).query("\n from n in list \n select n\n limit 0, 13\n "); 567 | 568 | assert.deepEqual(result, [{ name: 'dntzhang1', age: 1 }, { name: 'dntzhang2', age: 2 }, { name: 'dntzhang3', age: 3 }, { name: 'dntzhang4', age: 4 }, { name: 'dntzhang5', age: 5 }, { name: 'dntzhang6', age: 6 }, { name: 'dntzhang7', age: 7 }, { name: 'dntzhang8', age: 8 }, { name: 'dntzhang9', age: 9 }, { name: 'dntzhang10', age: 10 }]); 569 | }); -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 2 | QUnit.test("Basic test", function (assert) { 3 | var arr = [1, 2, 3, 4, 5] 4 | 5 | var result = qone({ arr }).query(` 6 | from n in arr 7 | where n > 3 8 | select n 9 | `) 10 | 11 | assert.deepEqual(result, [4, 5]) 12 | }) 13 | 14 | QUnit.test("Query json array", function (assert) { 15 | var list = [ 16 | { name: 'qone', age: 1 }, 17 | { name: 'linq', age: 18 }, 18 | { name: 'dntzhang', age: 28 } 19 | ] 20 | 21 | var result = qone({ list }).query(` 22 | from n in list 23 | where n.age > 18 24 | select n 25 | `) 26 | 27 | assert.deepEqual(result, [{ "name": "dntzhang", "age": 28 }]) 28 | 29 | }) 30 | 31 | QUnit.test("Multi conditional query", function (assert) { 32 | var list = [ 33 | { name: 'qone', age: 1 }, 34 | { name: 'linq', age: 18 }, 35 | { name: 'dntzhang', age: 28 } 36 | ] 37 | 38 | var result = qone({ list }).query(` 39 | from n in list 40 | where n.age > 10 && n.name = 'linq' 41 | select n 42 | `) 43 | 44 | assert.deepEqual(result, [{ "name": "linq", "age": 18 }]) 45 | 46 | }) 47 | 48 | QUnit.test("Select test", function (assert) { 49 | var list = [ 50 | { name: 'qone', age: 1 }, 51 | { name: 'linq', age: 18 }, 52 | { name: 'dntzhang', age: 28 } 53 | ] 54 | 55 | var result = qone({ list }).query(` 56 | from n in list 57 | where n.age < 20 58 | select n.age, n.name 59 | `) 60 | 61 | assert.deepEqual(result, [[1, "qone"], [18, "linq"]]) 62 | 63 | }) 64 | 65 | QUnit.test("Select JSON test", function (assert) { 66 | var list = [ 67 | { name: 'qone', age: 1 }, 68 | { name: 'linq', age: 18 }, 69 | { name: 'dntzhang', age: 28 } 70 | ] 71 | 72 | var result = qone({ list }).query(` 73 | from n in list 74 | where n.age < 20 75 | select {n.age, n.name} 76 | `) 77 | 78 | assert.deepEqual(result, [ 79 | { "age": 1, "name": "qone" }, 80 | { "age": 18, "name": "linq" } 81 | ]) 82 | 83 | }) 84 | 85 | QUnit.test("Select full JSON test", function (assert) { 86 | var list = [ 87 | { name: 'qone', age: 1 }, 88 | { name: 'linq', age: 18 }, 89 | { name: 'dntzhang', age: 28 } 90 | ] 91 | 92 | var result = qone({ list }).query(` 93 | from n in list 94 | where n.age < 20 95 | select {a : n.age, b : n.name} 96 | `) 97 | 98 | assert.deepEqual(result, [ 99 | { "a": 1, "b": "qone" }, 100 | { "a": 18, "b": "linq" } 101 | ]) 102 | 103 | }) 104 | 105 | QUnit.test("|| conditional test", function (assert) { 106 | var list = [ 107 | { name: 'qone', age: 1 }, 108 | { name: 'linq', age: 18 }, 109 | { name: 'dntzhang', age: 28 } 110 | ] 111 | 112 | var result = qone({ list }).query(` 113 | from n in list 114 | where n.age > 19 || n.age < 2 115 | select n 116 | `) 117 | 118 | assert.deepEqual(result, [ 119 | { "name": "qone", "age": 1 }, 120 | { "name": "dntzhang", "age": 28 } 121 | ]) 122 | 123 | }) 124 | 125 | QUnit.test("|| and && conditional test", function (assert) { 126 | var list = [ 127 | { name: 'qone', age: 0 }, 128 | { name: 'linq', age: 1 }, 129 | { name: 'linq', age: 18 }, 130 | { name: 'dntzhang', age: 28 } 131 | ] 132 | 133 | var result = qone({ list }).query(` 134 | from n in list 135 | where n.age > 17 || n.age < 2 && n.name != 'dntzhang' 136 | select n 137 | `) 138 | 139 | assert.deepEqual(result, [ 140 | { "name": "qone", "age": 0 }, 141 | { "name": "linq", "age": 1 }, 142 | { "name": "linq", "age": 18 }, 143 | { "name": "dntzhang", "age": 28 }]) 144 | 145 | }) 146 | 147 | QUnit.test("(), || and && conditional test", function (assert) { 148 | var list = [ 149 | { name: 'qone', age: 0 }, 150 | { name: 'linq', age: 1 }, 151 | { name: 'linq', age: 18 }, 152 | { name: 'dntzhang', age: 28 } 153 | ] 154 | 155 | var result = qone({ list }).query(` 156 | from n in list 157 | where (n.age > 17 || n.age < 2) && n.name != 'dntzhang' 158 | select n 159 | `) 160 | 161 | assert.deepEqual(result, [ 162 | { "name": "qone", "age": 0 }, 163 | { "name": "linq", "age": 1 }, 164 | { "name": "linq", "age": 18 }]) 165 | 166 | }) 167 | 168 | 169 | QUnit.test("Query from prop", function (assert) { 170 | var data = { 171 | users: [ 172 | { name: 'qone', age: 1 }, 173 | { name: 'dntzhang', age: 17 }, 174 | { name: 'dntzhang2', age: 27 }] 175 | } 176 | 177 | var result = qone({ data }).query(` 178 | from n in data.users 179 | where n.age < 10 180 | select n 181 | `) 182 | 183 | assert.deepEqual(result, [{ "name": "qone", "age": 1 }]) 184 | 185 | }) 186 | 187 | 188 | QUnit.test("Multi datasource ", function (assert) { 189 | 190 | var listA = [ 191 | { name: 'qone', age: 1 }, 192 | { name: 'linq', age: 18 }, 193 | { name: 'dntzhang', age: 28 }] 194 | 195 | 196 | var listB = [ 197 | { name: 'x', age: 11 }, 198 | { name: 'xx', age: 12 }, 199 | { name: 'xxx', age: 13 } 200 | ] 201 | 202 | 203 | var result = qone({ listA, listB }).query(` 204 | from a in listA 205 | from b in listB 206 | where a.age < 20 && b.age > 11 207 | select a, b 208 | `) 209 | 210 | assert.deepEqual(result, [ 211 | [{ "name": "qone", "age": 1 }, { "name": "xx", "age": 12 }], 212 | [{ "name": "qone", "age": 1 }, { "name": "xxx", "age": 13 }], 213 | [{ "name": "linq", "age": 18 }, { "name": "xx", "age": 12 }], 214 | [{ "name": "linq", "age": 18 }, { "name": "xxx", "age": 13 }]]) 215 | }) 216 | 217 | QUnit.test("Multi datasource with props condition", function (assert) { 218 | 219 | var listA = [ 220 | { name: 'qone', age: 1 }, 221 | { name: 'linq', age: 18 }, 222 | { name: 'dntzhang', age: 28 }] 223 | 224 | 225 | var listB = [ 226 | { name: 'x', age: 11 }, 227 | { name: 'xx', age: 18 }, 228 | { name: 'xxx', age: 13 } 229 | ] 230 | 231 | var result = qone({ listA, listB }).query(` 232 | from a in listA 233 | from b in listB 234 | where a.age = b.age 235 | select a, b 236 | `) 237 | 238 | assert.deepEqual(result, [[{ "name": "linq", "age": 18 }, { "name": "xx", "age": 18 }]]) 239 | }) 240 | 241 | QUnit.test("Bool condition ", function (assert) { 242 | 243 | var list = [ 244 | { name: 'qone', age: 1, isBaby: true }, 245 | { name: 'linq', age: 18 }, 246 | { name: 'dntzhang', age: 28 }] 247 | 248 | var result = qone({ list }).query(` 249 | from a in list 250 | where a.isBaby 251 | select a 252 | `) 253 | 254 | assert.deepEqual(result, [{ name: 'qone', age: 1, isBaby: true }]) 255 | }) 256 | 257 | QUnit.test("Bool condition ", function (assert) { 258 | 259 | var list = [ 260 | { name: 'qone', age: 1, isBaby: [true] }, 261 | { name: 'linq', age: 18, isBaby: [false] }, 262 | { name: 'dntzhang', age: 28, isBaby: [false] }] 263 | 264 | var result = qone({ list }).query(` 265 | from a in list 266 | where a.isBaby[0] 267 | select a 268 | `) 269 | 270 | assert.deepEqual(result, [{ name: 'qone', age: 1, isBaby: [true] }]) 271 | }) 272 | 273 | 274 | 275 | QUnit.test("Multi from test ", function (assert) { 276 | 277 | var list = [ 278 | { name: 'qone', age: 1, isBaby: true, colors: ['red', 'green', 'blue'] }, 279 | { name: 'linq', age: 18, colors: ['red', 'blue'] }, 280 | { name: 'dntzhang', age: 28, colors: ['red', 'blue'] }] 281 | 282 | var result = qone({ list }).query(` 283 | from a in list 284 | from c in a.colors 285 | where c = 'green' 286 | select a, c 287 | `) 288 | 289 | assert.deepEqual(result, [[{ "name": "qone", "age": 1, "isBaby": true, "colors": ["red", "green", "blue"] }, "green"]]) 290 | }) 291 | 292 | 293 | 294 | 295 | QUnit.test("Multi deep from test ", function (assert) { 296 | 297 | var list = [ 298 | { name: 'qone', age: 1, isBaby: true, colors: [{ xx: [1, 2, 3] }, { xx: [11, 2, 3] }] }, 299 | { name: 'linq', age: 18, colors: [{ xx: [100, 2, 3] }, { xx: [11, 2, 3] }] }, 300 | { name: 'dntzhang', age: 28, colors: [{ xx: [1, 2, 3] }, { xx: [11, 2, 3] }] }] 301 | 302 | var result = qone({ list }).query(` 303 | from a in list 304 | from c in a.colors 305 | from d in c.xx 306 | select a, c,d 307 | `) 308 | 309 | assert.equal(result.length, 18) 310 | }) 311 | 312 | 313 | QUnit.test("Multi deep from test ", function (assert) { 314 | 315 | var list = [ 316 | { name: 'qone', age: 1, isBaby: true, colors: [{ xx: [1, 2, 3] }, { xx: [11, 2, 3] }] }, 317 | { name: 'linq', age: 18, colors: [{ xx: [100, 2, 3] }, { xx: [11, 2, 3] }] }, 318 | { name: 'dntzhang', age: 28, colors: [{ xx: [1, 2, 3] }, { xx: [11, 2, 3] }] }] 319 | 320 | var result = qone({ list }).query(` 321 | from a in list 322 | from c in a.colors 323 | from d in c.xx 324 | where d === 100 325 | select a.name, c,d 326 | `) 327 | 328 | assert.deepEqual(result, [["linq", { "xx": [100, 2, 3] }, 100]]) 329 | }) 330 | 331 | QUnit.test("Deep prop path test ", function (assert) { 332 | 333 | var list = [ 334 | { name: 'qone', age: 1, fullName: { first: 'q', last: 'one' } }, 335 | { name: 'linq', age: 18, fullName: { first: 'l', last: 'inq' } }, 336 | { name: 'dntzhang', age: 28, fullName: { first: 'dnt', last: 'zhang' } }] 337 | 338 | var result = qone({ list }).query(` 339 | from a in list 340 | where a.fullName.first === 'dnt' 341 | select a.name 342 | `) 343 | 344 | assert.deepEqual(result, ["dntzhang"]) 345 | }) 346 | 347 | 348 | QUnit.test("Method test 1", function (assert) { 349 | var arr = [1, 2, 3, 4, 5] 350 | 351 | qone('square', function (num) { 352 | return num * num 353 | }) 354 | 355 | var result = qone({ arr }).query(` 356 | from n in arr 357 | where square(n) > 10 358 | select n 359 | `) 360 | 361 | assert.deepEqual(result, [4, 5]) 362 | }) 363 | 364 | 365 | QUnit.test("Method test 2", function (assert) { 366 | var arr = [1, 2, 3, 4, 5] 367 | 368 | qone('square', function (num) { 369 | return num * num 370 | }) 371 | 372 | var result = qone({ arr }).query(` 373 | from n in arr 374 | where 10 > square(n) 375 | select n 376 | `) 377 | 378 | assert.deepEqual(result, [1, 2, 3]) 379 | }) 380 | 381 | 382 | QUnit.test("Method test 3", function (assert) { 383 | var arr = [1, 2, 3, 4, 5] 384 | 385 | qone('square', function (num) { 386 | return num * num 387 | }) 388 | 389 | var result = qone({ arr }).query(` 390 | from n in arr 391 | where n > 3 392 | select square(n) 393 | `) 394 | 395 | assert.deepEqual(result, [16, 25]) 396 | }) 397 | 398 | QUnit.test("Method test 4", function (assert) { 399 | var arr = [1, 2, 3, 4, 5] 400 | 401 | qone('square', function (num) { 402 | return num * num 403 | }) 404 | 405 | var result = qone({ arr }).query(` 406 | from n in arr 407 | where n > 3 408 | select n, square(n) 409 | `) 410 | 411 | assert.deepEqual(result, [[4, 16], [5, 25]]) 412 | }) 413 | 414 | 415 | QUnit.test("Method test 5", function (assert) { 416 | var arr = [1, 2, 3, 4, 5] 417 | 418 | qone('square', function (num) { 419 | return num * num 420 | }) 421 | 422 | var result = qone({ arr }).query(` 423 | from n in arr 424 | where n > 3 425 | select { value : n, squareValue : square(n) } 426 | `) 427 | 428 | assert.deepEqual(result, [ 429 | { "value": 4, "squareValue": 16 }, 430 | { "value": 5, "squareValue": 25 }]) 431 | }) 432 | 433 | QUnit.test("Method test 6", function (assert) { 434 | var arr = [1, 2, 3, 4, 5] 435 | 436 | qone('square', function (num) { 437 | return num * num 438 | }) 439 | 440 | var result = qone({ arr }).query(` 441 | from n in arr 442 | where n > 3 443 | select { squareValue : square(n) } 444 | `) 445 | 446 | assert.deepEqual(result, [{ "squareValue": 16 }, { "squareValue": 25 }]) 447 | }) 448 | 449 | QUnit.test("Method test 7", function (assert) { 450 | var arr = [1, 2, 3, 4, 5] 451 | 452 | qone('square', function (num) { 453 | return num * num 454 | }) 455 | 456 | var result = qone({ arr }).query(` 457 | from n in arr 458 | where n > 3 459 | select { squareValue : square(n) } 460 | `) 461 | 462 | assert.deepEqual(result, [{ "squareValue": 16 }, { "squareValue": 25 }]) 463 | }) 464 | 465 | QUnit.test("Method test 8", function (assert) { 466 | var arr = [1, 2, 3, 4, 5] 467 | 468 | qone('square', function (num) { 469 | return num * num 470 | }) 471 | 472 | qone('sqrt', function (num) { 473 | return Math.sqrt(num) 474 | }) 475 | 476 | var result = qone({ arr }).query(` 477 | from n in arr 478 | where sqrt(n) >= 2 479 | select { squareValue : square(n) } 480 | `) 481 | 482 | assert.deepEqual(result, [{ "squareValue": 16 }, { "squareValue": 25 }]) 483 | }) 484 | 485 | QUnit.test("Method test 9", function (assert) { 486 | var list = [ 487 | { name: 'qone', age: 1 }, 488 | { name: 'linq', age: 19 }, 489 | { name: 'dntzhang', age: 28 } 490 | ] 491 | 492 | qone('greaterThan18', function (num) { 493 | return num > 18 494 | }) 495 | 496 | var result = qone({ list }).query(` 497 | from n in list 498 | where greaterThan18(n.age) && n.name = 'dntzhang' 499 | select n 500 | `) 501 | 502 | assert.deepEqual(result, [{ "name": "dntzhang", "age": 28 }]) 503 | 504 | }) 505 | 506 | QUnit.test("Method test 10", function (assert) { 507 | var list = [ 508 | { name: 'qone', age: 1 }, 509 | { name: 'linq', age: 19 }, 510 | { name: 'dntzhang', age: 28 } 511 | ] 512 | 513 | qone('greaterThan18', function (num) { 514 | return num > 18 515 | }) 516 | 517 | var result = qone({ list }).query(` 518 | from n in list 519 | where greaterThan18(n.age) && n.name = 'dntzhang' 520 | select n 521 | `) 522 | 523 | assert.deepEqual(result, [{ "name": "dntzhang", "age": 28 }]) 524 | 525 | }) 526 | 527 | QUnit.test("Order by test 1", function (assert) { 528 | var list = [ 529 | { name: 'linq2', age: 7 }, 530 | { name: 'qone', age: 1 }, 531 | { name: 'linq', age: 18 }, 532 | { name: 'dntzhang', age: 28 } 533 | ] 534 | 535 | var result = qone({ list }).query(` 536 | from n in list 537 | where n.age > 0 538 | orderby n.age desc 539 | select n 540 | `) 541 | 542 | assert.deepEqual(result, [ 543 | { "name": "dntzhang", "age": 28 }, 544 | { "name": "linq", "age": 18 }, 545 | { "name": "linq2", "age": 7 }, 546 | { "name": "qone", "age": 1 }]) 547 | 548 | }) 549 | 550 | QUnit.test("Order by test 2", function (assert) { 551 | var list = [ 552 | { name: 'linq2', age: 7 }, 553 | { name: 'qone', age: 1 }, 554 | { name: 'linq', age: 18 }, 555 | { name: 'ainq', age: 18 }, 556 | { name: 'dntzhang', age: 28 } 557 | ] 558 | 559 | var result = qone({ list }).query(` 560 | from n in list 561 | where n.age > 0 562 | orderby n.age asc, n.name desc 563 | select n 564 | `) 565 | 566 | assert.deepEqual(result, [ 567 | { "name": "qone", "age": 1 }, 568 | { "name": "linq2", "age": 7 }, 569 | { "name": "linq", "age": 18 }, 570 | { "name": "ainq", "age": 18 }, 571 | { "name": "dntzhang", "age": 28 }]) 572 | 573 | }) 574 | 575 | 576 | 577 | QUnit.test("Order by test 3", function (assert) { 578 | var list = [ 579 | { name: 'qone', age: 17, age2: 2 }, 580 | { name: 'linq', age: 18, age2: 1 }, 581 | { name: 'dntzhang1', age: 28, age2: 2 }, 582 | { name: 'dntzhang2', age: 28, age2: 1 }, 583 | { name: 'dntzhang3', age: 29, age2: 1 } 584 | ] 585 | 586 | qone('sum', function (a, b) { 587 | return a + b 588 | }) 589 | 590 | 591 | var result = qone({ list }).query(` 592 | from n in list 593 | where n.age > 0 594 | orderby sum(n.age,n.age2) 595 | select n 596 | `) 597 | 598 | assert.deepEqual(result, [ 599 | { name: 'qone', age: 17, age2: 2 }, 600 | { name: 'linq', age: 18, age2: 1 }, 601 | { name: 'dntzhang2', age: 28, age2: 1 }, 602 | { name: 'dntzhang1', age: 28, age2: 2 }, 603 | { name: 'dntzhang3', age: 29, age2: 1 }]) 604 | 605 | }) 606 | 607 | 608 | QUnit.test("Order by test 4", function (assert) { 609 | var list = [ 610 | { name: 'qone', age: 17, age2: 2 }, 611 | { name: 'linq', age: 18, age2: 1 }, 612 | { name: 'dntzhang1', age: 28, age2: 2 }, 613 | { name: 'dntzhang2', age: 28, age2: 1 }, 614 | { name: 'dntzhang3', age: 29, age2: 1 } 615 | ] 616 | 617 | qone('sum', function (a, b) { 618 | return a + b 619 | }) 620 | 621 | 622 | var result = qone({ list }).query(` 623 | from n in list 624 | where n.age > 0 625 | orderby sum(n.age,n.age2) ,n.name 626 | select n 627 | `) 628 | 629 | assert.deepEqual(result, [ 630 | { name: 'linq', age: 18, age2: 1 }, 631 | { name: 'qone', age: 17, age2: 2 }, 632 | 633 | { name: 'dntzhang2', age: 28, age2: 1 }, 634 | { name: 'dntzhang1', age: 28, age2: 2 }, 635 | { name: 'dntzhang3', age: 29, age2: 1 }]) 636 | 637 | }) 638 | 639 | QUnit.test("Simple groupby test 1", function (assert) { 640 | var list = [ 641 | { name: 'qone', age: 1 }, 642 | { name: 'linq', age: 18 }, 643 | { name: 'dntzhang1', age: 28 }, 644 | { name: 'dntzhang2', age: 28 }, 645 | { name: 'dntzhang3', age: 29 } 646 | ] 647 | 648 | var result = qone({ list }).query(` 649 | from n in list 650 | where n.age > 18 651 | groupby n.age 652 | `) 653 | 654 | assert.deepEqual(result, [ 655 | [{ "name": "dntzhang1", "age": 28 }, { "name": "dntzhang2", "age": 28 }], 656 | [{ "name": "dntzhang3", "age": 29 }]]) 657 | 658 | }) 659 | 660 | QUnit.test("Simple groupby test 2", function (assert) { 661 | var list = [ 662 | { name: 'qone', age: 1 }, 663 | { name: 'qone', age: 1 }, 664 | { name: 'dntzhang', age: 28 }, 665 | { name: 'dntzhang2', age: 28 }, 666 | { name: 'dntzhang', age: 29 } 667 | ] 668 | 669 | var result = qone({ list }).query(` 670 | from n in list 671 | where n.age > 0 672 | groupby n.age,n.name 673 | `) 674 | 675 | assert.deepEqual(result, [ 676 | [{ "name": "qone", "age": 1 }, { "name": "qone", "age": 1 }], 677 | [{ "name": "dntzhang", "age": 28 }], 678 | [{ "name": "dntzhang2", "age": 28 }], 679 | [{ "name": "dntzhang", "age": 29 }]]) 680 | 681 | }) 682 | 683 | QUnit.test("Simple groupby with method", function (assert) { 684 | var list = [ 685 | { name: 'qone', age: 17, age2: 2 }, 686 | { name: 'linq', age: 18, age2: 1 }, 687 | { name: 'dntzhang1', age: 28, age2: 1 }, 688 | { name: 'dntzhang2', age: 28, age2: 1 }, 689 | { name: 'dntzhang3', age: 29, age2: 1 } 690 | ] 691 | qone('sum', function (a, b) { 692 | return a + b 693 | }) 694 | 695 | 696 | var result = qone({ list }).query(` 697 | from n in list 698 | groupby sum(n.age,n.age2) 699 | `) 700 | 701 | assert.deepEqual(result, [ 702 | [{ name: 'qone', age: 17, age2: 2 }, { name: 'linq', age: 18, age2: 1 }], 703 | [{ name: 'dntzhang1', age: 28, age2: 1 }, { name: 'dntzhang2', age: 28, age2: 1 }], 704 | [{ name: 'dntzhang3', age: 29, age2: 1 }]]) 705 | 706 | }) 707 | 708 | 709 | QUnit.test("Simple groupby with method", function (assert) { 710 | var list = [ 711 | { name: 'qone', age: 17, age2: 2 }, 712 | { name: 'qone', age: 18, age2: 1 }, 713 | { name: 'dntzhang1', age: 28, age2: 1 }, 714 | { name: 'dntzhang2', age: 28, age2: 1 }, 715 | { name: 'dntzhang3', age: 29, age2: 1 } 716 | ] 717 | qone('sum', function (a, b) { 718 | return a + b 719 | }) 720 | 721 | 722 | var result = qone({ list }).query(` 723 | from n in list 724 | groupby sum(n.age,n.age2), n.name 725 | `) 726 | 727 | assert.deepEqual(result, [ 728 | [{ name: 'qone', age: 17, age2: 2 }, { name: 'qone', age: 18, age2: 1 }], 729 | [{ name: 'dntzhang1', age: 28, age2: 1 }], 730 | [{ name: 'dntzhang2', age: 28, age2: 1 }], 731 | [{ name: 'dntzhang3', age: 29, age2: 1 }]]) 732 | 733 | }) 734 | 735 | QUnit.test("Bool condition 1 ", function (assert) { 736 | 737 | var list = [ 738 | { name: 'qone', age: 1, isBaby: true }, 739 | { name: 'linq', age: 18 }, 740 | { name: 'dntzhang', age: 28 }] 741 | 742 | var result = qone({ list }).query(` 743 | from a in list 744 | where !a.isBaby 745 | select a 746 | `) 747 | 748 | assert.deepEqual(result, [ 749 | { "name": "linq", "age": 18 }, 750 | { "name": "dntzhang", "age": 28 }]) 751 | }) 752 | 753 | 754 | 755 | QUnit.test("Bool condition 2 ", function (assert) { 756 | 757 | var list = [ 758 | { name: 'qone', age: 1, isBaby: true }, 759 | { name: 'linq', age: 18 }, 760 | { name: 'dntzhang', age: 28 }] 761 | 762 | var result = qone({ list }).query(` 763 | from a in list 764 | where !a.isBaby && a.name='qone' 765 | select a 766 | `) 767 | 768 | assert.deepEqual(result, []) 769 | }) 770 | 771 | 772 | QUnit.test("Bool condition 3 ", function (assert) { 773 | 774 | var list = [ 775 | { name: 'qone', age: 1, isBaby: true }, 776 | { name: 'linq', age: 18 }, 777 | { name: 'dntzhang', age: 28 }] 778 | 779 | var result = qone({ list }).query(` 780 | from a in list 781 | where !(a.isBaby && a.name='qone') 782 | select a 783 | `) 784 | 785 | 786 | assert.deepEqual(result, [{ name: 'linq', age: 18 }, { "name": "dntzhang", "age": 28 }]) 787 | }) 788 | 789 | 790 | QUnit.test("Bool condition 4 ", function (assert) { 791 | 792 | var list = [ 793 | { name: 'qone', age: 1, isBaby: true }, 794 | { name: 'linq', age: 18 }, 795 | { name: 'dntzhang', age: 28 }] 796 | 797 | var result = qone({ list }).query(` 798 | from a in list 799 | where !(a.isBaby && a.name='qone') && a.age = 28 800 | select a 801 | `) 802 | 803 | 804 | assert.deepEqual(result, [{ "name": "dntzhang", "age": 28 }]) 805 | }) 806 | 807 | 808 | QUnit.test("Bool condition 5 ", function (assert) { 809 | 810 | var list = [ 811 | { name: 'qone', age: 1, isBaby: true }, 812 | { name: 'linq', age: 18 }, 813 | { name: 'dntzhang', age: 28 }] 814 | 815 | var result = qone({ list }).query(` 816 | from a in list 817 | where a.isBaby = true 818 | select a 819 | `) 820 | 821 | assert.deepEqual(result, [{ name: 'qone', age: 1, isBaby: true }]) 822 | }) 823 | 824 | 825 | 826 | QUnit.test("Bool condition 6 ", function (assert) { 827 | 828 | var list = [ 829 | { name: 'qone', age: 1, isBaby: true }, 830 | { name: 'linq', age: 18 }, 831 | { name: 'dntzhang', age: 28 }] 832 | 833 | var result = qone({ list }).query(` 834 | from a in list 835 | where a.isBaby = true 836 | select a 837 | `) 838 | 839 | assert.deepEqual(result, [{ name: 'qone', age: 1, isBaby: true }]) 840 | }) 841 | 842 | QUnit.test("Bool condition 7 ", function (assert) { 843 | 844 | var list = [ 845 | { name: 'qone', age: 1, isBaby: true }, 846 | { name: 'linq', age: 18 }, 847 | { name: 'dntzhang', age: 28 }] 848 | 849 | var result = qone({ list }).query(` 850 | from a in list 851 | where a.isBaby = true 852 | select a 853 | `) 854 | 855 | assert.deepEqual(result, [ 856 | { name: 'qone', age: 1, isBaby: true }]) 857 | }) 858 | 859 | QUnit.test("Bool condition 8 ", function (assert) { 860 | 861 | var list = [ 862 | { name: 'qone', age: 1, isBaby: true }, 863 | { name: 'linq', age: 18 }, 864 | { name: 'dntzhang', age: 28 }] 865 | 866 | var result = qone({ list }).query(` 867 | from a in list 868 | where a.isBaby = undefined 869 | select a 870 | `) 871 | 872 | assert.deepEqual(result, [ 873 | { name: 'linq', age: 18 }, { name: 'dntzhang', age: 28 }]) 874 | }) 875 | 876 | QUnit.test("Bool condition 9 ", function (assert) { 877 | 878 | var list = [ 879 | { name: 'qone', age: 1, isBaby: true }, 880 | { name: 'linq', age: 18, isBaby: false }, 881 | { name: 'dntzhang', age: 28, isBaby: false }] 882 | 883 | var result = qone({ list }).query(` 884 | from a in list 885 | where a.isBaby = false 886 | select a 887 | `) 888 | 889 | assert.deepEqual(result, [ 890 | { name: 'linq', age: 18, isBaby: false }, { name: 'dntzhang', age: 28, isBaby: false }]) 891 | }) 892 | 893 | QUnit.test("Bool condition 10 ", function (assert) { 894 | 895 | var list = [ 896 | { name: 'qone', age: 1, isBaby: true }, 897 | { name: 'linq', age: 18, isBaby: false }, 898 | { name: 'dntzhang', age: 28, isBaby: null }] 899 | 900 | var result = qone({ list }).query(` 901 | from a in list 902 | where a.isBaby = false || a.isBaby = null 903 | select a 904 | `) 905 | 906 | assert.deepEqual(result, [ 907 | { name: 'linq', age: 18, isBaby: false }, { name: 'dntzhang', age: 28, isBaby: null }]) 908 | }) 909 | 910 | QUnit.test("Bool condition 11 ", function (assert) { 911 | 912 | var list = [ 913 | { name: 'qone', age: 1, isBaby: true }, 914 | { name: 'linq', age: 18 }, 915 | { name: 'dntzhang', age: 28 }] 916 | 917 | var result = qone({ list }).query(` 918 | from a in list 919 | where !((a.isBaby && a.name='qone') && a.age = 28) 920 | select a 921 | `) 922 | 923 | assert.deepEqual(result, [ 924 | { "name": "qone", "age": 1, "isBaby": true }, 925 | { "name": "linq", "age": 18 }, 926 | { "name": "dntzhang", "age": 28 }]) 927 | }) 928 | 929 | 930 | QUnit.test("Bool condition 12", function (assert) { 931 | 932 | var list = [ 933 | { name: 'qone', age: 1, isBaby: true }, 934 | { name: 'linq', age: 18 }, 935 | { name: 'dntzhang', age: 28 }] 936 | 937 | var result = qone({ list }).query(` 938 | from a in list 939 | where !((a.isBaby && a.name='qone') && ((a.age = 28)||!a.isBaby)) 940 | select a 941 | `) 942 | 943 | assert.deepEqual(result, [ 944 | { name: 'qone', age: 1, isBaby: true }, 945 | { name: 'linq', age: 18 }, 946 | { name: 'dntzhang', age: 28 }]) 947 | }) 948 | 949 | 950 | QUnit.test("Bool condition 13", function (assert) { 951 | 952 | var list = [ 953 | { name: 'qone', age: 1, isBaby: true }, 954 | { name: 'linq', age: 18 }, 955 | { name: 'dntzhang', age: 28 }] 956 | 957 | var result = qone({ list }).query(` 958 | from a in list 959 | where ((a.isBaby && a.name='qone') && !((a.age = 28)||!a.isBaby)) 960 | select a 961 | `) 962 | 963 | assert.deepEqual(result, [ 964 | { name: 'qone', age: 1, isBaby: true }]) 965 | }) 966 | 967 | 968 | QUnit.test("Method test", function (assert) { 969 | 970 | var list = [ 971 | { name: 'qone', age: 1, scores: [1, 2, 3] }, 972 | { name: 'linq', age: 18, scores: [11, 2, 3] }, 973 | { name: 'dntzhang', age: 28, scores: [100, 2, 3] }] 974 | 975 | qone('fullCredit', function (item) { 976 | return item[0] === 100 977 | }) 978 | 979 | var result = qone({ list }).query(` 980 | from a in list 981 | where fullCredit(a.scores) 982 | select a 983 | `) 984 | 985 | assert.deepEqual(result, [ 986 | { name: 'dntzhang', age: 28, scores: [100, 2, 3] }]) 987 | }) 988 | 989 | 990 | QUnit.test("Access array", function (assert) { 991 | 992 | var list = [ 993 | { name: 'qone', age: 1, scores: [1, 2, 3] }, 994 | { name: 'linq', age: 18, scores: [11, 2, 3] }, 995 | { name: 'dntzhang', age: 28, scores: [100, 2, 3] }] 996 | 997 | 998 | var result = qone({ list }).query(` 999 | from a in list 1000 | where a.scores[0] === 100 1001 | select a 1002 | `) 1003 | 1004 | assert.deepEqual(result, [ 1005 | { name: 'dntzhang', age: 28, scores: [100, 2, 3] }]) 1006 | }) 1007 | 1008 | QUnit.test("Access array", function (assert) { 1009 | 1010 | var list = [ 1011 | { name: 'qone', age: 1, scores: [{ a: 1 }, 2, 3] }, 1012 | { name: 'linq', age: 18, scores: [{ a: 2 }, 2, 3] }, 1013 | { name: 'dntzhang', age: 28, scores: [{ a: 3 }, 2, 3] }] 1014 | 1015 | 1016 | var result = qone({ list }).query(` 1017 | from a in list 1018 | where a.scores[0].a === 3 1019 | select a 1020 | `) 1021 | 1022 | assert.deepEqual(result, [ 1023 | { name: 'dntzhang', age: 28, scores: [{ a: 3 }, 2, 3] }]) 1024 | }) 1025 | 1026 | 1027 | QUnit.test("Access array", function (assert) { 1028 | 1029 | var list = [ 1030 | { name: 'qone', age: 1, scores: [[1, 2], 2, 3] }, 1031 | { name: 'linq', age: 18, scores: [[3, 4], 2, 3] }, 1032 | { name: 'dntzhang', age: 28, scores: [[5, 6], 2, 3] }] 1033 | 1034 | 1035 | var result = qone({ list }).query(` 1036 | from a in list 1037 | where a.scores[0][1] === 6 1038 | select a 1039 | `) 1040 | 1041 | assert.deepEqual(result, [ 1042 | { name: 'dntzhang', age: 28, scores: [[5, 6], 2, 3] }]) 1043 | }) 1044 | 1045 | 1046 | QUnit.test("Method test", function (assert) { 1047 | 1048 | var list = [ 1049 | { name: 'qone', age: 1, scores: [1, 2, 3] }, 1050 | { name: 'linq', age: 18, scores: [11, 2, 3] }, 1051 | { name: 'dntzhang', age: 28, scores: [100, 2, 3] }] 1052 | 1053 | qone('fullCredit', function (item, x, y) { 1054 | return item[0] * x + y === '22abc' 1055 | }) 1056 | 1057 | var result = qone({ list }).query(` 1058 | from a in list 1059 | where fullCredit(a.scores, 2, "abc") 1060 | select a 1061 | `) 1062 | 1063 | assert.deepEqual(result, [ 1064 | { name: 'linq', age: 18, scores: [11, 2, 3] }]) 1065 | }) 1066 | 1067 | 1068 | QUnit.test("Single line test", function (assert) { 1069 | var list = [ 1070 | { name: 'dntzhang1', age: 1 }, 1071 | { name: 'dntzhang2', age: 2 }, 1072 | { name: 'dntzhang3', age: 3 }, 1073 | { name: 'dntzhang4', age: 4 }, 1074 | { name: 'dntzhang5', age: 5 }, 1075 | { name: 'dntzhang6', age: 6 }, 1076 | { name: 'dntzhang7', age: 7 }, 1077 | { name: 'dntzhang8', age: 8 }, 1078 | { name: 'dntzhang9', age: 9 }, 1079 | { name: 'dntzhang10', age: 10 } 1080 | 1081 | ] 1082 | 1083 | var result = qone({ list }).query('from n in list where n.age > 1 select n limit 1, 3') 1084 | 1085 | 1086 | assert.deepEqual(result, [ 1087 | { name: 'dntzhang3', age: 3 }, 1088 | { name: 'dntzhang4', age: 4 }, 1089 | { name: 'dntzhang5', age: 5 }]) 1090 | 1091 | }) 1092 | 1093 | 1094 | QUnit.test("Limit one page test", function (assert) { 1095 | var list = [ 1096 | { name: 'dntzhang1', age: 1 }, 1097 | { name: 'dntzhang2', age: 2 }, 1098 | { name: 'dntzhang3', age: 3 }, 1099 | { name: 'dntzhang4', age: 4 }, 1100 | { name: 'dntzhang5', age: 5 }, 1101 | { name: 'dntzhang6', age: 6 }, 1102 | { name: 'dntzhang7', age: 7 }, 1103 | { name: 'dntzhang8', age: 8 }, 1104 | { name: 'dntzhang9', age: 9 }, 1105 | { name: 'dntzhang10', age: 10 } 1106 | ] 1107 | 1108 | var pageIndex = 1, 1109 | pageSize = 4 1110 | var result = qone({ list }).query(` 1111 | from n in list 1112 | where n.age > 0 1113 | select n 1114 | limit ${pageIndex * pageSize}, ${pageSize} 1115 | `) 1116 | 1117 | 1118 | assert.deepEqual(result, [{ name: 'dntzhang5', age: 5 }, 1119 | { name: 'dntzhang6', age: 6 }, 1120 | { name: 'dntzhang7', age: 7 }, 1121 | { name: 'dntzhang8', age: 8 }]) 1122 | 1123 | }) 1124 | 1125 | 1126 | QUnit.test("Limit top 3", function (assert) { 1127 | var list = [ 1128 | { name: 'dntzhang1', age: 1 }, 1129 | { name: 'dntzhang2', age: 2 }, 1130 | { name: 'dntzhang3', age: 3 }, 1131 | { name: 'dntzhang4', age: 4 }, 1132 | { name: 'dntzhang5', age: 5 }, 1133 | { name: 'dntzhang6', age: 6 }, 1134 | { name: 'dntzhang7', age: 7 }, 1135 | { name: 'dntzhang8', age: 8 }, 1136 | { name: 'dntzhang9', age: 9 }, 1137 | { name: 'dntzhang10', age: 10 } 1138 | ] 1139 | 1140 | var pageIndex = 1, 1141 | pageSize = 4 1142 | var result = qone({ list }).query(` 1143 | from n in list 1144 | select n 1145 | limit 0, 3 1146 | `) 1147 | 1148 | 1149 | assert.deepEqual(result, [ 1150 | 1151 | { name: 'dntzhang1', age: 1 }, 1152 | { name: 'dntzhang2', age: 2 }, 1153 | { name: 'dntzhang3', age: 3 } 1154 | ]) 1155 | 1156 | }) 1157 | 1158 | 1159 | 1160 | 1161 | QUnit.test("Limit top 13", function (assert) { 1162 | var list = [ 1163 | { name: 'dntzhang1', age: 1 }, 1164 | { name: 'dntzhang2', age: 2 }, 1165 | { name: 'dntzhang3', age: 3 }, 1166 | { name: 'dntzhang4', age: 4 }, 1167 | { name: 'dntzhang5', age: 5 }, 1168 | { name: 'dntzhang6', age: 6 }, 1169 | { name: 'dntzhang7', age: 7 }, 1170 | { name: 'dntzhang8', age: 8 }, 1171 | { name: 'dntzhang9', age: 9 }, 1172 | { name: 'dntzhang10', age: 10 } 1173 | ] 1174 | 1175 | var pageIndex = 1, 1176 | pageSize = 4 1177 | var result = qone({ list }).query(` 1178 | from n in list 1179 | select n 1180 | limit 0, 13 1181 | `) 1182 | 1183 | 1184 | assert.deepEqual(result, [ 1185 | 1186 | { name: 'dntzhang1', age: 1 }, 1187 | { name: 'dntzhang2', age: 2 }, 1188 | { name: 'dntzhang3', age: 3 }, 1189 | { name: 'dntzhang4', age: 4 }, 1190 | { name: 'dntzhang5', age: 5 }, 1191 | { name: 'dntzhang6', age: 6 }, 1192 | { name: 'dntzhang7', age: 7 }, 1193 | { name: 'dntzhang8', age: 8 }, 1194 | { name: 'dntzhang9', age: 9 }, 1195 | { name: 'dntzhang10', age: 10 } 1196 | ]) 1197 | 1198 | }) --------------------------------------------------------------------------------