├── tool ├── lib │ ├── exclude.lst │ └── html.sed ├── d2d └── d2j ├── testcase ├── web │ ├── core-01-html.dart │ ├── core-01-html.html │ ├── event-04-massive.html │ ├── offset-01.dart │ ├── css-01.dart │ ├── manipulation-01.dart │ ├── event-05-on-off.html │ ├── manipulation-01.html │ ├── data-01.html │ ├── data-01.dart │ ├── event-03-namespace.html │ ├── event-01.dart │ ├── event-05-on-off.dart │ ├── manipulation-02.dart │ ├── event-04-massive.dart │ ├── event-01.html │ ├── manipulation-02.html │ ├── offset-01.html │ ├── event-03-namespace.dart │ ├── index.html │ ├── css-01.html │ ├── event-02-blur.html │ └── event-02-blur.dart └── pubspec.yaml ├── analysis_options.yaml ├── NOTICE ├── .gitattributes ├── pubspec.yaml ├── .gitignore ├── lib ├── src │ ├── util │ │ └── util.dart │ ├── selector.dart │ ├── cookie.dart │ ├── data.dart │ ├── dimension.dart │ ├── offset.dart │ ├── manipulation.dart │ ├── css.dart │ ├── traversing.dart │ ├── dquery_api.dart │ ├── dquery_impl.dart │ └── event.dart └── dquery.dart ├── CHANGELOG.md ├── README.md ├── doc └── Comparison.md └── LICENSE /tool/lib/exclude.lst: -------------------------------------------------------------------------------- 1 | meta 2 | metadata 3 | -------------------------------------------------------------------------------- /testcase/web/core-01-html.dart: -------------------------------------------------------------------------------- 1 | import 'package:dquery/dquery.dart'; 2 | 3 | void main() { 4 | $('#div').append($('')); 5 | } -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | analyzer: 2 | errors: 3 | todo: ignore 4 | linter: 5 | rules: 6 | - annotate_overrides 7 | - avoid_init_to_null 8 | - camel_case_types 9 | - constant_identifier_names 10 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Rikulo DQuery 2 | Copyright 2013 Potix corporation 3 | 4 | Portions of this software were developed based on the following: 5 | - Copyright 2013 jQuery Foundation and other contributors 6 | http://jquery.com/ 7 | MIT License 8 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.dart text eol=lf 2 | *.html text eol=lf 3 | *.css text eof=lf 4 | *.less text eof=lf 5 | *.xml text eol=lf 6 | *.md text eol=lf 7 | *.yaml text eol=lf 8 | *.txt text eol=lf 9 | *.js text eof=lf 10 | *.lst text eof=lf 11 | d2d text eof=lf 12 | d2j text eof=lf 13 | l2c text eof=lf 14 | -------------------------------------------------------------------------------- /testcase/web/core-01-html.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | core-01-html 5 | 6 | 7 | 8 | You shall see button append to here 9 |
10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /testcase/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: dquery_example_app 2 | description: A web app example for dquery. 3 | 4 | environment: 5 | sdk: '>=3.7.0 <4.0.0' 6 | 7 | dependencies: 8 | dquery: 9 | path: ../ 10 | 11 | dev_dependencies: 12 | build_runner: ^2.4.1 13 | build_test: ^2.1.7 14 | build_web_compilers: ^4.0.3 15 | -------------------------------------------------------------------------------- /testcase/web/event-04-massive.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | event-04-massive 5 | 6 | 7 | 8 | 9 |
10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: dquery 2 | version: 4.0.0+1 3 | description: | 4 | DQuery is a porting of jQuery in Dart. 5 | homepage: https://github.com/rikulo/dquery 6 | documentation: https://pub.dev/documentation/dquery/latest/ 7 | environment: 8 | sdk: '>=3.7.0 <4.0.0' 9 | dependencies: 10 | intl: any 11 | web: '>=1.0.0' 12 | dev_dependencies: 13 | test: any 14 | -------------------------------------------------------------------------------- /testcase/web/offset-01.dart: -------------------------------------------------------------------------------- 1 | import 'dart:js_interop'; 2 | 3 | import 'package:web/web.dart'; 4 | import 'package:dquery/dquery.dart'; 5 | 6 | void main() { 7 | 8 | final offset = $('#target').offset!; 9 | 10 | final info = document.querySelector('#info')!; 11 | info.innerHTML = "${info.innerHTML} (${offset.x}, ${offset.y})".toJS; 12 | 13 | $('span').offset = offset; 14 | 15 | } -------------------------------------------------------------------------------- /testcase/web/css-01.dart: -------------------------------------------------------------------------------- 1 | import 'package:dquery/dquery.dart'; 2 | 3 | void main() { 4 | 5 | $('#show').on('click', (QueryEvent e) { 6 | $(':checked + .target').show(); 7 | }); 8 | 9 | $('#hide').on('click', (QueryEvent e) { 10 | $(':checked + .target').hide(); 11 | }); 12 | 13 | $('#toggle').on('click', (QueryEvent e) { 14 | $(':checked + .target').toggle(); 15 | }); 16 | 17 | } -------------------------------------------------------------------------------- /testcase/web/manipulation-01.dart: -------------------------------------------------------------------------------- 1 | import 'package:web/web.dart'; 2 | import 'package:dquery/dquery.dart'; 3 | 4 | void main() { 5 | 6 | final input = document.querySelector('#input') as HTMLInputElement; 7 | final $html = $('#html'); 8 | final $text = $('#text'); 9 | 10 | $('#go').on('click', (QueryEvent event) { 11 | $html.html = input.value; 12 | $text.text = input.value; 13 | }); 14 | 15 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .pub 3 | .idea/ 4 | .packages 5 | debug 6 | codegen 7 | *.local 8 | *.class 9 | Thumbs.db 10 | .project 11 | .children 12 | *.dart.js 13 | *.dart.js_ 14 | *.dart.js.map 15 | *.dart.js.deps 16 | out.js 17 | out.js.map 18 | out.js.deps 19 | out 20 | *.bak 21 | ~$*.pptx 22 | ~$*.docx 23 | ~$*.xlsx 24 | *.tmp 25 | packages 26 | packages/ 27 | pubspec.lock 28 | .pub 29 | dquery.iml 30 | .dart_tool 31 | -------------------------------------------------------------------------------- /testcase/web/event-05-on-off.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | event-05-on-off 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /testcase/web/manipulation-01.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | manipulation-01 5 | 6 | 7 | 8 | 9 | 10 |

html

11 |
12 |

text

13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /testcase/web/data-01.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | data-01 6 | 7 | 8 | 9 | 10 |

11 |
Element
12 |
13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /testcase/web/data-01.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:dquery/dquery.dart'; 3 | 4 | void main() { 5 | $('#addBtn').on('click', (QueryEvent e) { 6 | $('#e').data.set('time', DateTime.now()); 7 | }); 8 | $('#rmBtn').on('click', (QueryEvent e) { 9 | $('#e').data.remove('time'); 10 | }); 11 | 12 | $('#showBtn').on('click', (QueryEvent e) { 13 | Data d = $('#e').data; 14 | $('#msg').append('
time: ${d.get("time")}
'); 15 | }); 16 | 17 | } -------------------------------------------------------------------------------- /testcase/web/event-03-namespace.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | event-03-namespace 6 | 7 | 8 | 9 | 10 |

11 |
Element
12 |
13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /lib/src/util/util.dart: -------------------------------------------------------------------------------- 1 | part of dquery; 2 | 3 | // TODO: shall move to commons later 4 | 5 | int? _max(List nums) { 6 | if (nums.isEmpty) 7 | return null; 8 | int? m; 9 | for (final n in nums) 10 | m = m == null ? n: n == null ? m: max(m, n); 11 | return m; 12 | } 13 | 14 | 15 | // html // 16 | 17 | bool _hasAction(node, String name) { 18 | // TODO 19 | return false; 20 | } 21 | 22 | void _performAction(node, String name) { 23 | // TODO 24 | } 25 | -------------------------------------------------------------------------------- /testcase/web/event-01.dart: -------------------------------------------------------------------------------- 1 | import 'package:dquery/dquery.dart'; 2 | import 'package:web/web.dart'; 3 | void main() { 4 | 5 | $document().on('click', (QueryEvent event) { 6 | print("${event.target}, data:${event.data}"); 7 | 8 | }, selector: 'div.button'); 9 | 10 | $('#trigger').on('click', (QueryEvent event) { 11 | print('trigger'); 12 | $('div.button').trigger('click', data: 88); 13 | }); 14 | $('#trigger').click((QueryEvent event) { print(event.type);}); 15 | $('#input1').change((QueryEvent event) { 16 | var value=(event.target as HTMLInputElement).value; 17 | print("${event.target}, value:${value}"); 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /testcase/web/event-05-on-off.dart: -------------------------------------------------------------------------------- 1 | import 'package:web/web.dart'; 2 | import 'package:dquery/dquery.dart'; 3 | 4 | void main() { 5 | 6 | final on = document.querySelector('#on') as HTMLButtonElement; 7 | final off = document.querySelector('#off') as HTMLButtonElement; 8 | 9 | $(on).on('click', (_) { 10 | $('#btn').on('click', f); 11 | off.disabled = false; 12 | on.disabled = true; 13 | }); 14 | 15 | $(off).on('click', (_) { 16 | $('#btn').off('click', handler: f); 17 | off.disabled = true; 18 | on.disabled = false; 19 | }); 20 | 21 | $('#one').one('click', (_) { 22 | window.alert('one'); 23 | }); 24 | } 25 | 26 | void f(QueryEvent event) { 27 | window.alert('hit!'); 28 | } 29 | -------------------------------------------------------------------------------- /testcase/web/manipulation-02.dart: -------------------------------------------------------------------------------- 1 | import 'package:web/web.dart'; 2 | import 'package:dquery/dquery.dart'; 3 | 4 | void main() { 5 | 6 | final input = document.querySelector('#input') as HTMLInputElement; 7 | final $target = $('#target'); 8 | 9 | $('#append').on('click', (QueryEvent event) => $target.append(input.value)); 10 | $('#prepend').on('click', (QueryEvent event) => $target.prepend(input.value)); 11 | $('#after').on('click', (QueryEvent event) => $target.after(input.value)); 12 | $('#before').on('click', (QueryEvent event) => $target.before(input.value)); 13 | 14 | $('#appendTo').on('click', (QueryEvent event) => $(input.value).appendTo('#target')); 15 | $('#prependTo').on('click', (QueryEvent event) => $(input.value).prependTo('#target')); 16 | 17 | } -------------------------------------------------------------------------------- /testcase/web/event-04-massive.dart: -------------------------------------------------------------------------------- 1 | import 'dart:js_interop'; 2 | 3 | import 'package:web/web.dart'; 4 | import 'package:dquery/dquery.dart'; 5 | 6 | void main() { 7 | 8 | $('#btn').on('click', render); 9 | 10 | } 11 | 12 | void render(QueryEvent event) { 13 | final root = HTMLUListElement(); 14 | final container = document.querySelector('#list')!; 15 | Element item; 16 | 17 | container.children.item(0)?.remove(); 18 | final t = DateTime.now().millisecondsSinceEpoch; 19 | for (var i = 0; i < 1000; i++) { 20 | item = HTMLLIElement()..innerHTML = '$i'.toJS; 21 | $(item).on('click', (QueryEvent e) { 22 | window.alert("$i"); 23 | }); 24 | root.append(item); 25 | } 26 | print(DateTime.now().millisecondsSinceEpoch - t); 27 | container.append(root); 28 | } 29 | -------------------------------------------------------------------------------- /testcase/web/event-01.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | test 6 | 7 | 8 | 9 | 10 | 11 | 20 |

Text

21 |
22 | 30 |
31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /testcase/web/manipulation-02.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | manipulation-02 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 |
15 |
16 | 17 | 18 | (Different APIs, should act the same as Append/Prepend) 19 |
20 |
21 | Some text here. 22 |
23 | Some text here. 24 |
25 | Some text here. 26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /testcase/web/offset-01.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | offset-01 5 | 6 | 7 | 8 | 16 |
Text should be in the box.
17 |
18 |
19 |
20 |
21 |
22 |
23 | 24 |
25 |
26 | Text 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /testcase/web/event-03-namespace.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:dquery/dquery.dart'; 3 | 4 | void main() { 5 | $('#e').on('show',(QueryEvent e) { 6 | $('#msg').append('
show
'); 7 | }); 8 | 9 | $('#e').on('show.a',(QueryEvent e) { 10 | $('#msg').append('
show.a
'); 11 | }); 12 | 13 | $('#e').on('show.b',(QueryEvent e) { 14 | $('#msg').append('
show.b
'); 15 | }); 16 | 17 | $('#e').on('show.a.c',(QueryEvent e) { 18 | $('#msg').append('
show.a.c
'); 19 | }); 20 | $('#e').on('show.a.d',(QueryEvent e) { 21 | $('#msg').append('
show.a.d
'); 22 | }); 23 | 24 | 25 | 26 | $('#btn1').on('click', (QueryEvent e) { 27 | $('#e').trigger('show'); 28 | }); 29 | $('#btn2').on('click', (QueryEvent e) { 30 | $('#e').trigger('show.a'); 31 | }); 32 | 33 | $('#btn3').on('click', (QueryEvent e) { 34 | $('#e').trigger('show.a.c'); 35 | }); 36 | 37 | } -------------------------------------------------------------------------------- /testcase/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

7 | core-01-html.html 8 |

9 |

10 | css-01.html 11 |

12 |

13 | data-01.html 14 |

15 |

16 | event-01.html 17 |

18 |

19 | event-02-blur.html 20 |

21 |

22 | event-03-namespace.html 23 |

24 |

25 | event-04-massive.html 26 |

27 |

28 | event-05-on-off.html 29 |

30 |

31 | manipulation-01.html 32 |

33 |

34 | manipulation-02.html 35 |

36 |

37 | offset-01.html 38 |

39 | 40 | 41 | -------------------------------------------------------------------------------- /testcase/web/css-01.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | css-01 5 | 6 | 7 | 8 | 9 | 31 | 32 |
33 | 34 | 35 | 36 |
37 | 38 |
39 | 40 |
41 |
42 | 43 |
44 | 45 |
46 |
47 | 48 | 49 | -------------------------------------------------------------------------------- /lib/src/selector.dart: -------------------------------------------------------------------------------- 1 | part of dquery; 2 | 3 | // TODO: unique requires sorting 4 | /* src: 5 | selector_sortOrder = function( a, b ) { 6 | // Flag for duplicate removal 7 | if ( a === b ) { 8 | selector_hasDuplicate = true; 9 | return 0; 10 | } 11 | 12 | var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b ); 13 | 14 | if ( compare ) { 15 | // Disconnected nodes 16 | if ( compare & 1 ) { 17 | 18 | // Choose the first element that is related to our document 19 | if ( a === document || jQuery.contains(document, a) ) { 20 | return -1; 21 | } 22 | if ( b === document || jQuery.contains(document, b) ) { 23 | return 1; 24 | } 25 | 26 | // Maintain original order 27 | return 0; 28 | } 29 | 30 | return compare & 4 ? -1 : 1; 31 | } 32 | 33 | // Not directly comparable, sort on existence of method 34 | return a.compareDocumentPosition ? -1 : 1; 35 | }; 36 | */ 37 | 38 | List _unique(List elements) { 39 | // USE our own implementation 40 | // TODO: need to sort/preserve order 41 | return elements.toSet().toList(growable: true); 42 | } 43 | -------------------------------------------------------------------------------- /tool/lib/html.sed: -------------------------------------------------------------------------------- 1 | s|Dart Documentation / Dart Documentation|API Reference / DQuery| 2 | s|Dart Documentation|API Reference / DQuery| 3 | s|]*>|| 4 | s|

Dart Documentation

|

DQuery API Reference

| 5 | s||| 6 | s|Dart Documentation|API Reference {{{version}}}| 7 | s||\ 18 | | 19 | -------------------------------------------------------------------------------- /testcase/web/event-02-blur.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | test 6 | 7 | 30 | 31 | 32 | 33 | 34 |
35 |

Focus

36 | 37 |
0
38 |
0
39 |
40 | 41 |
42 |

Blur

43 | 44 |
0
45 |
0
46 |
47 | 48 |
49 |

Click

50 | 51 |
0
52 |
0
53 |
54 | 55 |
56 |

Mouseenter

57 |
58 |
59 |
60 | Here 61 |
62 |
63 |
64 |
0
65 |
0
66 |
67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /tool/d2d: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # d2d 3 | # 4 | # Purpose: 5 | # Generates API Reference for rikulo 6 | # 7 | #Copyright (C) 2012 Potix Corporation. All Rights Reserved. 8 | # 9 | nodosfilewarning=true 10 | SDK=/usr/dart/dart-sdk 11 | if [ ! -d $SDK ] ; then 12 | echo $SDK not found 13 | echo "Please link /usr/dart to Dart Editor's folder." 14 | exit 15 | fi 16 | homedir=$0 17 | homedir=${homedir%/*} 18 | if [ "$homedir" = "." ] ; then 19 | homedir=".." 20 | elif [ "$homedir" = "${homedir%/*}" ] ; then 21 | homedir="." 22 | else 23 | homedir=${homedir%/*} 24 | fi 25 | if [ "$TERM" = "cygwin" ] || [ "$OSTYPE" = "cygwin" ] ; then 26 | homedir=$(cygpath -u $homedir) 27 | dartdoc=dartdoc.bat 28 | else 29 | dartdoc=dartdoc 30 | fi 31 | outdir=$homedir/out/dartdoc 32 | libdir=$homedir/lib 33 | yaml=$homedir/pubspec.yaml 34 | 35 | mkdir -p $outdir 36 | rm -rf $outdir/* 37 | 38 | if [ ! -f $yaml ] ; then 39 | echo $yaml not found 40 | exit 1 41 | fi 42 | ver=$(grep version: $yaml) 43 | ver=${ver#version: } 44 | 45 | PATH=$PATH:$SDK/bin 46 | echo Compiling $libdir $ver 47 | exclude_list=$(cat $homedir/tool/lib/exclude.lst | tr '\n' ',') 48 | $dartdoc --link-api --package-root $homedir/packages --exclude-lib $exclude_list --out $outdir $libdir/*.dart 49 | if [ ! -f $outdir/index.html ] ; then 50 | echo $outdir/index.html not found. Failed? 51 | exit 1 52 | fi 53 | 54 | cd $outdir 55 | echo Process HTML files at $(pwd) 56 | tooldir=../../tool/lib 57 | function doHtml { 58 | sedfl=$1 59 | if [ ! -f $sedfl ] ; then 60 | echo $sedfl not found 61 | exit 62 | fi 63 | for f in *; do 64 | if [ -d $f ] ; then 65 | ( 66 | echo Process $f 67 | cd $f 68 | doHtml ../$sedfl 69 | ) 70 | elif [ ${f%.html} != $f ] ; then 71 | sed -f $sedfl -e "s/{{{version}}}/$ver/" $f > dd.tmp 72 | mv -f dd.tmp $f 73 | fi 74 | done 75 | } 76 | 77 | doHtml $tooldir/html.sed 78 | -------------------------------------------------------------------------------- /testcase/web/event-02-blur.dart: -------------------------------------------------------------------------------- 1 | import 'package:web/web.dart'; 2 | import 'package:dquery/dquery.dart'; 3 | 4 | void main() { 5 | 6 | //blur 7 | final blurSec = document.querySelector('.blur')!; 8 | 9 | $document().on('blur', (QueryEvent e) { 10 | printDoc(blurSec); 11 | }, selector: '.blur input'); 12 | 13 | $('.blur input').on('blur', (QueryEvent e) { 14 | printElem(blurSec); 15 | }); 16 | 17 | 18 | //focus 19 | final focusSec = document.querySelector('.focus')!; 20 | 21 | $document().on('focus', (QueryEvent e) { 22 | printDoc(focusSec); 23 | }, selector: '.focus input'); 24 | 25 | $('.focus input').on('focus', (QueryEvent e) { 26 | printElem(focusSec); 27 | }); 28 | 29 | //click 30 | final clickSec = document.querySelector('.click')!; 31 | 32 | $document().on('click', (QueryEvent e) { 33 | printDoc(clickSec); 34 | }, selector: '.click button'); 35 | 36 | $('.click button').on('click', (QueryEvent e) { 37 | printElem(clickSec); 38 | }); 39 | 40 | //click 41 | final enterSec = document.querySelector('.mouseenter')!; 42 | 43 | $document().on('mouseenter', (QueryEvent e) { 44 | printDoc(enterSec); 45 | }, selector: '.mouseenter .test-area'); 46 | 47 | $('.mouseenter .test-area').on('mouseenter', (QueryEvent e) { 48 | printElem(enterSec); 49 | }); 50 | } 51 | 52 | String NUM_DOC = 'num_doc'; 53 | String NUM_ELEM = 'num_elem'; 54 | 55 | void printDoc(Element sec) { 56 | var n = $(sec).data.get(NUM_DOC); 57 | if (n == null) 58 | n = 0; 59 | 60 | $(sec).data.set(NUM_DOC, ++n); 61 | sec.querySelector('.doc-result')!.textContent = 'doc: $n'; 62 | } 63 | 64 | void printElem(Element sec) { 65 | var n = $(sec).data.get(NUM_ELEM); 66 | if (n == null) 67 | n = 0; 68 | 69 | $(sec).data.set(NUM_ELEM, ++n); 70 | sec.querySelector('.elem-result')!.textContent = 'elem: $n'; 71 | } -------------------------------------------------------------------------------- /lib/src/cookie.dart: -------------------------------------------------------------------------------- 1 | //Copyright (C) 2014 Potix Corporation. All Rights Reserved. 2 | //History: Wed, Jun 18, 2014 12:07:42 PM 3 | // Author: tomyeh 4 | part of dquery; 5 | 6 | //--The code is ported from Carhartl's jquery-cookie--// 7 | 8 | String _encode(String s) => Uri.encodeComponent(s); 9 | String _decode(String s) => Uri.decodeComponent(s); 10 | 11 | _getCookie(Document doc, [String? key]) { 12 | final cookies = key != null ? null: {}; 13 | String? value = doc.cookie; 14 | for (final cookie in value.split('; ')) { 15 | final i = cookie.indexOf('='); 16 | if (i < 0) continue; //just in case 17 | 18 | final name = _decode(cookie.substring(0, i)); 19 | value = _parseCookieValue(cookie.substring(i + 1)); 20 | 21 | if (key == null) { 22 | if (value != null) 23 | cookies![name] = value; 24 | } else if (key == name) { 25 | return value; 26 | } 27 | } 28 | return cookies; 29 | } 30 | 31 | String? _parseCookieValue(String value) { 32 | if (value.startsWith('"')) { 33 | // This is a quoted cookie as according to RFC2068, unescape... 34 | value = value.substring(1, value.length - 1) 35 | .replaceAll(_reQuot, '"').replaceAll(_reBS, r'\'); 36 | } 37 | 38 | try { 39 | // Replace server-side written pluses with spaces. 40 | return _decode(value.replaceAll(_rePlus, ' ')); 41 | } catch (_) { 42 | return null; 43 | } 44 | } 45 | final RegExp _reQuot = new RegExp(r'\\"'), _reBS = new RegExp(r'\\\\'), 46 | _rePlus = new RegExp(r'\+'); 47 | 48 | 49 | void _setCookie(Document doc, String name, String value, Duration? expires, 50 | String? path, bool? secure) { 51 | final buf = [_encode(name), '=', _encode(value)]; 52 | 53 | if (expires != null) 54 | buf..add("; expires=") 55 | ..add(_utcfmt.format(new DateTime.now().add(expires).toUtc())) 56 | ..add(" GMT"); 57 | if (path != null) 58 | buf..add("; path=")..add(path); 59 | if (secure == true) 60 | buf.add("; secure"); 61 | 62 | document.cookie = buf.join(""); 63 | } 64 | final _utcfmt = new DateFormat("E dd MMM yyyy kk:mm:ss", "en_US"); 65 | -------------------------------------------------------------------------------- /lib/src/data.dart: -------------------------------------------------------------------------------- 1 | part of dquery; 2 | 3 | class _Storage { 4 | 5 | final Expando> _cache; 6 | 7 | _Storage(String name) : 8 | _cache = Expando>(name); 9 | 10 | void set(owner, String key, value) { 11 | getSpace(owner)[key] = value; 12 | } 13 | 14 | void setAll(Node owner, Map props) { 15 | final space = getSpace(owner); 16 | props.forEach((String key, value) => space[key] = value); 17 | } 18 | 19 | get(owner, String key) { 20 | final space = _cache[owner]; 21 | return space == null ? null : space[key]; 22 | } 23 | 24 | Map getSpace(owner) { 25 | var space = _cache[owner]; 26 | if (space == null) 27 | space = _cache[owner] = {}; 28 | return space; 29 | } 30 | 31 | void remove(Node owner, String key) { 32 | final space = _cache[owner]; 33 | if (space != null) { 34 | space.remove(key); 35 | if (space.isEmpty) 36 | _cache[owner] = null; 37 | } 38 | } 39 | 40 | bool hasData(owner) { 41 | final space = _cache[owner]; 42 | return space != null && !space.isEmpty; 43 | } 44 | 45 | void discard(owner) { 46 | _cache[owner] = null; 47 | } 48 | 49 | } 50 | 51 | final _dataUser = _Storage('dquery-data-user'); 52 | final _dataPriv = _Storage('dquery-data-priv'); 53 | 54 | /** The interface for accessing element data. 55 | */ 56 | class Data { 57 | 58 | final _Query _dq; 59 | 60 | Data._(this._dq); 61 | 62 | /** Retrieve the entire space of element data. 63 | */ 64 | Map? get space => _dq.isEmpty ? null : _dataUser.getSpace(_dq.first); 65 | 66 | /** Retrieve the data of the given [key]. 67 | */ 68 | get(String key) => _dq.isEmpty ? null: space![key]; 69 | 70 | /** Set the data of the given [key]. 71 | */ 72 | void set(String key, value) => 73 | _dq.forEach((t) => _dataUser.set(t, key, value)); 74 | 75 | /** Delete the data of the given [key]. 76 | */ 77 | void remove(String key) => 78 | _dq.forEach((t) => _dataUser.remove(t as Node, key)); 79 | 80 | } 81 | 82 | /* 83 | _dataAttr(Element elem, String key, data) { 84 | // TODO: it's a function that offers some fix to the key and data to leverege HTML 5 85 | // data- attributes, should be important to plug-in environment 86 | 87 | return data; 88 | } 89 | */ 90 | -------------------------------------------------------------------------------- /tool/d2j: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # d2j 3 | # 4 | # Purpose: 5 | # Compile dart to js 6 | # 7 | #Copyright (C) 2012 Potix Corporation. All Rights Reserved. 8 | # 9 | defdir=web/client/dart 10 | SDK=/usr/dart/dart-sdk 11 | if [ ! -d $SDK ] ; then 12 | echo $SDK not found 13 | echo "Please link /usr/dart to Dart Editor's folder." 14 | exit 15 | fi 16 | if [ "$TERM" = "cygwin" ] || [ "$OSTYPE" = "cygwin" ] ; then 17 | dart2js=$SDK/bin/dart2js.bat 18 | else 19 | dart2js=$SDK/bin/dart2js 20 | fi 21 | 22 | if [ "$1" == "--help" ] || [ "$1" == "-h" ] ; then 23 | echo d2j - Compile Dart to Javascript 24 | echo 25 | echo Usage: 26 | echo " d2j" 27 | echo " d2j --help" 28 | echo " d2j --cleanup" 29 | echo " d2j [--debug] file1.dart directory1 file2.dart..." 30 | echo 31 | echo "* If a directory is specified, all dart files containing main() will be compiled" 32 | echo "* If nothing is specified, $defdir is assumed" 33 | exit 1 34 | fi 35 | 36 | if [ "$1" == "--cleanup" ] ; then 37 | echo cleanup 38 | find -name '*.dart.js' | xargs rm -f 39 | find -name '*.dart.js.map' | xargs rm -f 40 | exit 41 | fi 42 | 43 | minify=--minify 44 | warning=false 45 | function d2j { 46 | f=$1 47 | if [ ${f%.dart} != $f ] ; then 48 | grep -w 'main[(] *[)]' $f > /dev/null 49 | if [ "$?" = "0" ] ; then #found 50 | echo $dart2js $minify -o$f.js $(pwd)/$f 51 | $dart2js $minify -o$f.js $f 52 | if [ "$?" = "0" ] ; then 53 | if [ "$minify" = "--minify" ] ; then 54 | rm $f.js.map 55 | rm $f.js.deps 56 | rm $f.precompiled.js 57 | fi 58 | else 59 | exit 1 60 | fi 61 | elif [ $warning = true ] ; then 62 | echo "$f are ignored since main() not found" 63 | fi 64 | fi 65 | } 66 | function d2jx { 67 | for f in *; do 68 | if [ -d $f ] ; then 69 | if [ $f != dartdoc ] && [ $f != issues ] && [ $f != packages ] ; then 70 | ( 71 | cd $f 72 | d2jx 73 | ) 74 | fi 75 | else 76 | d2j $f 77 | fi 78 | done 79 | } 80 | function d2jy { 81 | f=$1 82 | if [ -f "$f" ] ; then 83 | warning=true 84 | d2j "$f" 85 | elif [ -d "$f" ] ; then 86 | warning=false 87 | ( 88 | cd "$f" 89 | d2jx 90 | ) 91 | else 92 | echo "$f not found" 93 | fi 94 | } 95 | 96 | if [ "$1" == "--debug" ] ; then 97 | minify= 98 | shift 99 | elif [ "$f" == "--minify" ] ; then 100 | minify=--minify 101 | shift 102 | fi 103 | 104 | if [ $# == 0 ] ; then 105 | if [ -d "$defdir" ] ; then 106 | echo "Compile $defdir..." 107 | d2jy "$defdir" 108 | else 109 | echo "Directory not found: $defdir" 110 | exit 1 111 | fi 112 | else 113 | for f in $* ; do 114 | d2jy $f 115 | done 116 | fi 117 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGES 2 | **4.0.0** 3 | * Dart 3.7 required 4 | 5 | **3.0.1** 6 | * Fixed ConcurrentModificationError when detach element 7 | 8 | **3.0.0** 9 | * Dart 3 required 10 | 11 | **2.0.4** 12 | Add "key" and "code" getter for keyboard input 13 | 14 | **2.0.3** 15 | Fixed concurrent modification error when detach element 16 | 17 | **2.0.2** 18 | Remove dart:html_common package 19 | 20 | **2.0.1** 21 | * Fixed set a wrong offsetTop 22 | * Fixed relatedTarget cast error 23 | 24 | **2.0.0** 25 | * Migrate to null safety 26 | 27 | **1.0.1** 28 | * Fixed type errors for Dart 2 29 | * Fixed get css value by property name 30 | 31 | **1.0.0** 32 | * Dart 2 required 33 | 34 | **0.8.6** 35 | * Fixed QueryEvent missing overrides 36 | 37 | **0.8.5** 38 | 39 | * QueryEvent.shiftKey introduced. 40 | 41 | **0.8.4** 42 | 43 | * Deprecate QueryEvent.type since Dart 1.16 doesn't support MouseEvent.which, and it is not non-standard. 44 | * Deprecate keyLocation. Use location instead. 45 | 46 | **0.8.3** 47 | 48 | * Remove QueryEvent's isDefaultPrevented and isPropagationStopped. Please use defaultPrevented and propagationStopped instead. 49 | 50 | **0.8.2** 51 | 52 | * Remove QueryEvent's which and clipboardData to be compatible with Dart 1.16 53 | 54 | **0.8.1** 55 | 56 | * Close #11: QueryEvent implements Event 57 | # Fixed client error when access pageX (Firefox) 58 | 59 | **0.8.0 60 | 61 | * Refector: DQuery doesn't depend on Event.target/type for basic event handling 62 | 63 | **0.7.1** 64 | 65 | * Focus/blur event bubbling ready. 66 | * Fix mouseenter/mouseleave delegate event. 67 | * Fix delegate target not found error. 68 | * Removed deprecated event API. 69 | * Fine tune QueryEvent constructor. 70 | 71 | **0.7.0** 72 | 73 | * #6: DocumentQuery supported cookie API. 74 | * #2: QueryEvent supported keystroke API. 75 | 76 | **0.6.1** 77 | 78 | * Fix event memory issue. 79 | 80 | **0.6.0** 81 | 82 | * Query was added for query any object including shadow roots. 83 | * DocumentQuery and WindowQuery were removed, and replaced with DQuery. 84 | * DQueryEvent was renamed to QueryEvent. 85 | * Data.space() became a getter: Date.space 86 | 87 | **0.5.4** 88 | 89 | * Fix event name space. 90 | * Fix data remove. 91 | * Fine tune JavaScript compatibility. 92 | 93 | **0.5.2** 94 | 95 | * Add getter/setter of offset, getter of offsetLeft, offsetTop 96 | * Add getter of offsetParent 97 | * Add getter/setter of html, text 98 | * Add .clone() 99 | * Add .append(), .prepend(), .after(), .before() 100 | * Add .appendTo(), .prependTo() 101 | * Minor bug fixes 102 | 103 | **0.5.1** 104 | 105 | * Add $(html) support. 106 | * Add getter, setter of scrollLeft, scrollTop 107 | * Add .css() 108 | * Add getter of width, height 109 | -------------------------------------------------------------------------------- /lib/dquery.dart: -------------------------------------------------------------------------------- 1 | library dquery; 2 | 3 | import 'dart:math'; 4 | import 'package:web/web.dart'; 5 | 6 | import 'dart:collection'; 7 | 8 | import 'dart:js_interop'; 9 | import "package:intl/intl.dart" show DateFormat; 10 | 11 | part 'src/util/util.dart'; 12 | part 'src/dquery_api.dart'; 13 | part 'src/dquery_impl.dart'; 14 | part 'src/selector.dart'; 15 | part 'src/traversing.dart'; 16 | part 'src/dimension.dart'; 17 | part 'src/offset.dart'; 18 | part 'src/manipulation.dart'; 19 | part 'src/css.dart'; 20 | part 'src/data.dart'; 21 | part 'src/event.dart'; 22 | part 'src/cookie.dart'; 23 | 24 | /** Return an [ElementQuery] based on given [selector] and [context]. 25 | */ 26 | ElementQuery $(selector, [context]) { 27 | if (selector is String) 28 | selector = selector.trim(); 29 | 30 | if (selector == null || selector == '') 31 | return ElementQuery([]); 32 | 33 | if (selector is String) { 34 | // html 35 | if (selector.startsWith('<')) { 36 | final template = HTMLTemplateElement(); 37 | template.innerHTML = selector.toJS; 38 | return ElementQuery(JSImmutableListWrapper( 39 | template.content.childNodes).toList().cast()); 40 | } 41 | 42 | if (context == null) 43 | return _rootDQuery.find(selector); 44 | 45 | if (context is DQuery) 46 | return context.find(selector); 47 | 48 | if (context is JSObject) { 49 | if (context.isA()) 50 | return $document(context as Document).find(selector); 51 | 52 | if (context.isA()) 53 | return ElementQuery([context as HTMLElement]).find(selector); 54 | } 55 | 56 | throw ArgumentError("Context type should be Document, Element, or DQuery: $context"); 57 | } 58 | 59 | if (selector is JSObject) { 60 | if (selector.isA()) 61 | return ElementQuery([selector as Element]); 62 | 63 | if (selector.isA() || selector.isA()) 64 | return ElementQuery(JSImmutableListWrapper(selector)); 65 | } 66 | 67 | if (selector is List) 68 | return ElementQuery(selector); 69 | 70 | throw ArgumentError("Selector type should be String, Element, or List: $selector"); 71 | } 72 | 73 | /** Return a [DocumentQuery] wrapping the given [document]. If [document] is 74 | * omitted, the default document instance is assumed. 75 | */ 76 | DocumentQuery $document([Document? document]) => _DocumentQuery(document); 77 | 78 | /** Return a [WindowQuery] wrapping the given [window]. If [window] is omitted, 79 | * the default window instance is used. 80 | */ 81 | DQuery $window([Window? window]) => _WindowQuery(window); 82 | 83 | /** Return a [WindowQuery] wrapping the given [window]. If [window] is omitted, 84 | * the default window instance is used. 85 | */ 86 | Query $shadowRoot(ShadowRoot shadowRoot) => _ShadowRootQuery(shadowRoot); 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DQuery 2 | 3 | DQuery is a porting of [jQuery](http://jquery.com/) in Dart. 4 | 5 | 6 | * [API Reference](https://pub.dev/documentation/dquery/latest/) 7 | * [Git Repository](https://github.com/rikulo/dquery) 8 | * [Discussion](http://stackoverflow.com/questions/tagged/rikulo) 9 | * [Issues](https://github.com/rikulo/dquery/issues) 10 | 11 | ## Install from Dart Pub Repository 12 | 13 | Include the following in your `pubspec.yaml`: 14 | 15 | dependencies: 16 | dquery: any 17 | 18 | Then run the [Pub Package Manager](http://pub.dartlang.org/doc) in Dart Editor (Tool > Pub Install). If you are using a different editor, run the command 19 | (comes with the Dart SDK): 20 | 21 | pub install 22 | 23 | ## Usage 24 | 25 | You can create a query object by selector. With context provided, the query will be based on different element. 26 | 27 | // selects all elements containing 'active' in CSS class 28 | ElementQuery $elems = $('.active'); 29 | 30 | // selects all descendant elements of div containing 'active' in CSS class 31 | ElementQuery $elems = $('.active', div); 32 | 33 | It implements List. 34 | 35 | $('.active')[0]; 36 | $('.active').isEmpty; 37 | for (Element e in $('.active')) { ... } 38 | 39 | Create another query object with traversing API, including [find](https://pub.dev/documentation/dquery/latest/dquery/Query/find.html), [closest](https://pub.dev/documentation/dquery/latest/dquery/ElementQuery/closest.html), [parent](https://pub.dev/documentation/dquery/latest/dquery/ElementQuery/parent.html), [children](https://pub.dev/documentation/dquery/latest/dquery/ElementQuery/children.html). 40 | 41 | $('.active').closest('ul'); 42 | $('#myDiv').find('a.btn'); 43 | 44 | Manipulate selected elements. 45 | 46 | $('.active').removeClass('active'); 47 | $('.fade').hide(); 48 | 49 | Register event handlers on queried elements, or trigger an event by API. 50 | 51 | $('#myBtn').on('click', (QueryEvent e) { 52 | ... 53 | }); 54 | $('#myBtn').trigger('click', data: 'my data'); 55 | 56 | There are query objects of `Document` and `Window` too. 57 | 58 | Query $doc = $document(); 59 | Query $win = $window(); 60 | 61 | Check the [API reference](https://pub.dev/documentation/dquery/latest/dquery/dquery-library.html) for more features. 62 | 63 | ## Comparison to jQuery 64 | 65 | See [here](https://github.com/rikulo/dquery/blob/master/doc/Comparison.md). 66 | 67 | ## Notes to Contributors 68 | 69 | ### Test and Debug 70 | 71 | You are welcome to submit [bugs and feature requests](https://github.com/rikulo/dquery/issues). Or even better if you can fix or implement them! 72 | 73 | ### Fork DQuery 74 | 75 | If you'd like to contribute back to the core, you can [fork this repository](https://help.github.com/articles/fork-a-repo) and send us a pull request, when it is ready. 76 | 77 | Please be aware that one of Rikulo's design goals is to keep the sphere of API as neat and consistency as possible. Strong enhancement always demands greater consensus. 78 | 79 | If you are new to Git or GitHub, please read [this guide](https://help.github.com/) first. 80 | 81 | ## Who Uses 82 | 83 | * [Quire](https://quire.io) - a simple, collaborative, multi-level task management tool. 84 | * [Keikai](https://keikai.io) - a sophisticated spreadsheet for big data 85 | -------------------------------------------------------------------------------- /lib/src/dimension.dart: -------------------------------------------------------------------------------- 1 | part of dquery; 2 | /* 3 | // Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods 4 | jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { 5 | jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) { 6 | // margin is only for outerHeight, outerWidth 7 | jQuery.fn[ funcName ] = function( margin, value ) { 8 | var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ), 9 | extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" ); 10 | 11 | return jQuery.access( this, function( elem, type, value ) { 12 | var doc; 13 | 14 | return value === undefined ? 15 | // Get width or height on the element, requesting but not forcing parseFloat 16 | jQuery.css( elem, type, extra ) : 17 | 18 | // Set width or height on the element 19 | jQuery.style( elem, type, value, extra ); 20 | }, type, chainable ? margin : undefined, chainable, null ); 21 | }; 22 | }); 23 | }); 24 | */ 25 | 26 | /* 27 | function getWidthOrHeight( elem, name, extra ) { 28 | 29 | // Start with offset property, which is equivalent to the border-box value 30 | var valueIsBorderBox = true, 31 | val = name === "width" ? elem.offsetWidth : elem.offsetHeight, 32 | styles = getStyles( elem ), 33 | isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; 34 | 35 | // some non-html elements return undefined for offsetWidth, so check for null/undefined 36 | // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 37 | // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 38 | if ( val <= 0 || val == null ) { 39 | // Fall back to computed then uncomputed css if necessary 40 | val = curCSS( elem, name, styles ); 41 | if ( val < 0 || val == null ) { 42 | val = elem.style[ name ]; 43 | } 44 | 45 | // Computed unit is not pixels. Stop here and return. 46 | if ( rnumnonpx.test(val) ) { 47 | return val; 48 | } 49 | 50 | // we need the check for style in case a browser which returns unreliable values 51 | // for getComputedStyle silently falls back to the reliable elem.style 52 | valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] ); 53 | 54 | // Normalize "", auto, and prepare for extra 55 | val = parseFloat( val ) || 0; 56 | } 57 | 58 | // use the active box-sizing model to add/subtract irrelevant styles 59 | return ( val + 60 | augmentWidthOrHeight( 61 | elem, 62 | name, 63 | extra || ( isBorderBox ? "border" : "content" ), 64 | valueIsBorderBox, 65 | styles 66 | ) 67 | ) + "px"; 68 | } 69 | */ 70 | 71 | /* 72 | function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { 73 | var i = extra === ( isBorderBox ? "border" : "content" ) ? 74 | // If we already have the right measurement, avoid augmentation 75 | 4 : 76 | // Otherwise initialize for horizontal or vertical properties 77 | name === "width" ? 1 : 0, 78 | 79 | val = 0; 80 | 81 | for ( ; i < 4; i += 2 ) { 82 | // both box models exclude margin, so add it if we want it 83 | if ( extra === "margin" ) { 84 | val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); 85 | } 86 | 87 | if ( isBorderBox ) { 88 | // border-box includes padding, so remove it if we want content 89 | if ( extra === "content" ) { 90 | val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); 91 | } 92 | 93 | // at this point, extra isn't border nor margin, so remove border 94 | if ( extra !== "margin" ) { 95 | val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); 96 | } 97 | } else { 98 | // at this point, extra isn't content, so add padding 99 | val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); 100 | 101 | // at this point, extra isn't content nor padding, so add border 102 | if ( extra !== "padding" ) { 103 | val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); 104 | } 105 | } 106 | } 107 | 108 | return val; 109 | } 110 | */ 111 | 112 | int _getElementWidth(Element elem) { 113 | return elem.getBoundingClientRect().width.ceil(); 114 | } 115 | 116 | int _getElementHeight(Element elem) { 117 | return elem.getBoundingClientRect().height.ceil(); 118 | } 119 | -------------------------------------------------------------------------------- /lib/src/offset.dart: -------------------------------------------------------------------------------- 1 | part of dquery; 2 | 3 | Point? _getOffset(Element? elem) { 4 | if (elem == null) 5 | return null; 6 | 7 | final doc = elem.ownerDocument; 8 | if (doc == null) 9 | return null; 10 | 11 | var box = Point(0, 0); 12 | final docElem = doc.documentElement; 13 | // jQuery: Make sure it's not a disconnected DOM node 14 | /* 15 | if ( !jQuery.contains( docElem, elem ) ) { 16 | return box; 17 | } 18 | */ 19 | 20 | // skipped 21 | // jQuery: If we don't have gBCR, just use 0,0 rather than error 22 | // BlackBerry 5, iOS 3 (original iPhone) 23 | //if ( typeof elem.getBoundingClientRect !== core_strundefined ) 24 | final r = elem.getBoundingClientRect(); 25 | box = Point(r.left, r.top); 26 | 27 | 28 | 29 | return box + Point(window.scrollX, window.scrollY) 30 | - Point(docElem!.clientLeft, docElem.clientTop); 31 | } 32 | 33 | //setOffset: function( elem, options, i ) { 34 | void _setOffset(Element elem, {num? left, num? top}) { 35 | if ((left == null && top == null) || elem is! HTMLElement) 36 | return; 37 | 38 | final position = _getCss(elem, 'position'); 39 | 40 | // jQuery: Set position first, in-case top/left are set even on static elem 41 | if (position == 'static') 42 | elem.style.position = 'relative'; 43 | 44 | final curOffset = _getOffset(elem), 45 | curCSSTop = _getCss(elem, 'top'), 46 | curCSSLeft = _getCss(elem, 'left'); 47 | 48 | // jQuery: Need to be able to calculate position if either top or left is auto 49 | // and position is either absolute or fixed 50 | final calculatePosition = 51 | (position == 'absolute' || position == 'fixed') && 52 | ("$curCSSTop $curCSSLeft").indexOf("auto") > -1; 53 | final curPosition = calculatePosition ? _getPosition(elem) : null; 54 | 55 | // skipped for now 56 | /* 57 | if ( jQuery.isFunction( options ) ) { 58 | options = options.call( elem, i, curOffset ); 59 | } 60 | */ 61 | 62 | // skipped 63 | /* 64 | if ( "using" in options ) { 65 | options.using.call( elem, props ); 66 | } 67 | */ 68 | 69 | if (left != null) { 70 | final curLeft = calculatePosition ? curPosition!.x : _parseDouble(curCSSLeft); 71 | elem.style.left = "${left - curOffset!.x + curLeft}px"; 72 | } 73 | 74 | if (top != null) { 75 | final curTop = calculatePosition ? curPosition!.y : _parseDouble(curCSSTop); 76 | elem.style.top = "${top - curOffset!.y + curTop}px"; 77 | } 78 | 79 | } 80 | 81 | Point? _getPosition(Element? elem) { 82 | if (elem is! HTMLElement) 83 | return null; 84 | 85 | Point offset; 86 | var parentOffset = Point(0, 0); 87 | 88 | // jQuery: Fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is it's only offset parent 89 | if (_getCss(elem, 'position') == 'fixed') { 90 | final rec = elem.getBoundingClientRect(); 91 | // jQuery: We assume that getBoundingClientRect is available when computed position is fixed 92 | offset = Point(rec.left, rec.top); 93 | } else { 94 | // jQuery: Get *real* offsetParent 95 | final offsetParent = _getOffsetParent(elem); 96 | 97 | // jQuery: Get correct offsets 98 | offset = Point(elem.offsetLeft, elem.offsetTop); 99 | if (offsetParent is HTMLElement && offsetParent.tagName != 'html') 100 | parentOffset = Point(offsetParent.offsetLeft, offsetParent.offsetTop); 101 | 102 | // jQuery: Add offsetParent borders 103 | parentOffset += _parseCssPoint(offsetParent!, 'borderLeftWidth', 'borderTopWidth', 0, 0); 104 | } 105 | 106 | // jQuery: Subtract parent offsets and element margins 107 | return offset + parentOffset - _parseCssPoint(elem, 'marginLeft', 'marginTop', 0, 0); 108 | } 109 | 110 | Point _parseCssPoint(Element elem, String nameX, String nameY, num defaultX, num defaultY) => 111 | Point(_parseCss(elem, nameX, defaultX), _parseCss(elem, nameY, defaultY)); 112 | 113 | num _parseCss(Element elem, String name, num defaultValue) => 114 | _parseDouble(_getCss(elem, name), defaultValue); // TODO: double.parse() is different from parseFloat() 115 | 116 | num _parseDouble(String? src, [num defaultValue = 0.0]) => 117 | double.tryParse(_trimSuffix(src, 'px') ?? '') ?? defaultValue; 118 | 119 | String? _trimSuffix(String? src, String suffix) => 120 | src == null ? null : 121 | src.endsWith(suffix) ? src.substring(0, src.length - suffix.length): src; 122 | 123 | Element? _getOffsetParent(Element elem) { 124 | var offsetParent = (elem as HTMLElement).offsetParent; 125 | if (offsetParent == null) 126 | offsetParent = document.documentElement; 127 | 128 | while (offsetParent != null && (offsetParent.tagName != 'html') && 129 | _getCss(offsetParent, "position") == 'static') { 130 | offsetParent = (offsetParent as HTMLElement).offsetParent; 131 | } 132 | 133 | if (offsetParent == null) 134 | offsetParent = document.documentElement; 135 | return offsetParent; 136 | } 137 | -------------------------------------------------------------------------------- /lib/src/manipulation.dart: -------------------------------------------------------------------------------- 1 | part of dquery; 2 | 3 | void _cleanData(Element element) { 4 | for (final c in JSImmutableListWrapper(element.children).toList().cast()) { 5 | if (!_dataPriv.hasData(c)) 6 | continue; 7 | final space = _dataPriv.getSpace(c); 8 | // remove event handlers 9 | if (space.containsKey('events')) 10 | for (final type in (space['events'] as Map).keys.toList()) 11 | _EventUtil.remove(c, type, null, null); 12 | 13 | _dataPriv.discard(c); 14 | _cleanData(c); 15 | } 16 | } 17 | 18 | void _detach(Element elem, bool data) { 19 | if (data) 20 | _cleanData(elem); 21 | 22 | if (elem.parentElement != null) { 23 | /* 24 | if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { 25 | setGlobalEval( getAll( elem, "script" ) ); 26 | } 27 | */ 28 | elem.remove(); 29 | } 30 | } 31 | 32 | void _empty(Element elem) { 33 | for (final c in JSImmutableListWrapper(elem.children).cast()) 34 | _cleanData(c); 35 | elem.innerHTML = ''.toJS; 36 | } 37 | 38 | ElementQuery? _resolveManipTarget(target) => 39 | target is ElementQuery ? target : 40 | target is String || target is Element ? $(target) : null; 41 | 42 | ElementQuery? _resolveManipContent(value) => 43 | value is ElementQuery ? value : 44 | value is Element ? $(value) : 45 | value is String && value.startsWith('<') ? $(value) : null; // TODO: function later 46 | 47 | void _domManip(ElementQuery? refs, content, void f(Element ref, ElementQuery obj)) { 48 | if (refs?.isEmpty ?? true) 49 | return; 50 | 51 | final objs = _resolveManipContent(content); 52 | if (objs?.isEmpty ?? true) 53 | return; 54 | 55 | final last = refs!.last; 56 | for (final n in refs) 57 | f(n, n == last ? objs! : objs!.clone()); 58 | 59 | } 60 | 61 | void _appendFunc(Element ref, ElementQuery obj) { 62 | for (final elem in obj.toList()) { 63 | ref.append(elem); 64 | } 65 | } 66 | 67 | void _prependFunc(Element ref, ElementQuery obj) { 68 | final before = JSImmutableListWrapper(ref.childNodes).firstOrNull; 69 | if (before is Node) { 70 | for (final elem in obj.toList()) { 71 | ref.insertBefore(elem, before); 72 | } 73 | } else 74 | _appendFunc(ref, obj); 75 | } 76 | 77 | void _afterFunc(Element ref, ElementQuery obj) { 78 | final parent = ref.parentNode as Node, 79 | before = ref.nextSibling; 80 | for (final elem in obj.toList()) { 81 | parent.insertBefore(elem, before); 82 | } 83 | } 84 | 85 | void _beforeFunc(Element ref, ElementQuery obj) { 86 | final parent = ref.parentNode as Node; 87 | for (final elem in obj.toList()) { 88 | parent.insertBefore(elem, ref); 89 | } 90 | } 91 | 92 | Element _clone(Element elem, [bool dataAndEvents = false, bool? deepDataAndEvents]) { 93 | if (deepDataAndEvents == null) 94 | deepDataAndEvents = dataAndEvents; 95 | 96 | final clone = elem.cloneNode(true) as Element; // deep 97 | 98 | //inPage = jQuery.contains( elem.ownerDocument, elem ); 99 | 100 | // skipped for now 101 | // jQuery: Support: IE >= 9 102 | // Fix Cloning issues 103 | /* 104 | if ( !jQuery.support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && !jQuery.isXMLDoc( elem ) ) { 105 | // jQuery: We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 106 | destElements = getAll( clone ); 107 | srcElements = getAll( elem ); 108 | for ( i = 0, l = srcElements.length; i < l; i++ ) 109 | fixInput( srcElements[ i ], destElements[ i ] ); 110 | } 111 | */ 112 | 113 | // jQuery: Copy the events from the original to the clone 114 | if (dataAndEvents) 115 | _cloneCopyEvent(elem, clone, deepDataAndEvents); 116 | 117 | // skipped for now 118 | // jQuery: Preserve script evaluation history 119 | /* 120 | destElements = getAll( clone, "script" ); 121 | if ( destElements.length > 0 ) { 122 | setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); 123 | } 124 | */ 125 | 126 | // Return the cloned set 127 | return clone; 128 | 129 | } 130 | 131 | void _cloneCopyEvent(Element src, Element dest, [bool deep = false]) { 132 | 133 | // jQuery: 1. Copy private data: events, handlers, etc. 134 | if (_dataPriv.hasData(src)) { 135 | final pdataOld = _dataPriv.getSpace(src); 136 | final pdataCur = _dataPriv.getSpace(dest); 137 | pdataOld.forEach((String k, v) { 138 | if (k != 'events') 139 | pdataCur[k] = v; 140 | }); 141 | 142 | final events = pdataOld['events']; 143 | if (events != null && !events.isEmpty) { 144 | events.forEach((String type, _HandleObjectContext hoc) { 145 | for (_HandleObject h in hoc.handlers) 146 | _EventUtil.add(dest, type, h.handler, h.selector); 147 | for (_HandleObject h in hoc.delegates) 148 | _EventUtil.add(dest, type, h.handler, h.selector); 149 | }); 150 | } 151 | } 152 | 153 | // jQuery: 2. Copy user data 154 | if (_dataUser.hasData(src)) 155 | _dataUser.setAll(dest, _dataUser.getSpace(src)); 156 | 157 | // unlike jQuery, we do deep clone by recursion 158 | if (deep) { 159 | int i = 0; 160 | for (final c in JSImmutableListWrapper(src.children).cast()) 161 | _cloneCopyEvent(c, dest.children.item(i++) as Element, true); 162 | } 163 | 164 | } 165 | 166 | 167 | 168 | void _setText(Element elem, String value) { 169 | elem.innerHTML = ''.toJS; 170 | elem.append(Text(value)); 171 | } 172 | 173 | // in strong type system, no way to get to text node or document fragment 174 | /* 175 | String _getText(Node node) => 176 | node is Element ? (node as Element).text : 177 | node is Document ? (node as Document).text : 178 | node is DocumentFragment ? (node as DocumentFragment).text : 179 | node is Text ? node.nodeValue : ''; 180 | */ -------------------------------------------------------------------------------- /doc/Comparison.md: -------------------------------------------------------------------------------- 1 | ###Member Functions 2 | 3 | | jQuery | DQuery | History | Note | 4 | |:-----------|:------------|:------------|:------------| 5 | | [.add()](http://api.jquery.com/add/) | *Dart List API* | since 0.5.0 | 6 | | [.addClass()](http://api.jquery.com/addClass/) | .addClass() | since 0.5.0 | 7 | | [.after()](http://api.jquery.com/after/) | .after() | since 0.5.2 | 8 | | [.append()](http://api.jquery.com/append/) | .append() | since 0.5.2 | 9 | | [.appendTo()](http://api.jquery.com/appendTo/) | .appendTo() | since 0.5.2 | 10 | | [.before()](http://api.jquery.com/before/) | .before() | since 0.5.2 | 11 | | [.bind()](http://api.jquery.com/bind/) | -- | | (Use .on() instead) 12 | | [.blur()](http://api.jquery.com/blur/) | | | (Later) 13 | | [.change()](http://api.jquery.com/change/) | | | (Later) 14 | | [.children()](http://api.jquery.com/children/) | .children() | since 0.5.0 | 15 | | [.click()](http://api.jquery.com/click/) | | | (Later) 16 | | [.clone()](http://api.jquery.com/clone/) | .clone() | since 0.5.2 | 17 | | [.closest()](http://api.jquery.com/closest/) | .closest() | since 0.5.0 | 18 | | [.css()](http://api.jquery.com/css/) | .css() | since 0.5.1 | 19 | | [.data()](http://api.jquery.com/data/) | .data.get() | since 0.5.0 | get data 20 | | | .data.set() | since 0.5.0 | set data 21 | | | .data.space() | since 0.5.0 | set all data map 22 | | [.dblclick()](http://api.jquery.com/dblclick/) | | | (Later) 23 | | [.delegate()](http://api.jquery.com/delegate/) | -- | | (Use .on() instead) 24 | | [.detach()](http://api.jquery.com/detach/) | .detach() | since 0.5.1 | 25 | | [.each()](http://api.jquery.com/each/) | *Dart List API* | since 0.5.0 | 26 | | [.empty()](http://api.jquery.com/empty/) | .empty() | since 0.5.1 | 27 | | [.end()](http://api.jquery.com/end/) | .end() | since 0.5.0 | 28 | | [.eq()](http://api.jquery.com/eq/) | | | (Later) 29 | | [.filter()](http://api.jquery.com/filter/) | | | (Later) 30 | | [.find()](http://api.jquery.com/find/) | .find() | since 0.5.0 | 31 | | [.first()](http://api.jquery.com/first/) | | | (Later) 32 | | [.focus()](http://api.jquery.com/focus/) | | | (Later) 33 | | [.focusin()](http://api.jquery.com/focusin/) | | | (Later) 34 | | [.focusout()](http://api.jquery.com/focusout/) | | | (Later) 35 | | [.get()](http://api.jquery.com/get/) | *Dart List API* | since 0.5.0 | 36 | | [.has()](http://api.jquery.com/has/) | | | (Later) 37 | | [.hasClass()](http://api.jquery.com/hasClass/) | .hasClass() | since 0.5.0 | 38 | | [.height()](http://api.jquery.com/height/) | get height | since 0.5.1 | (Only getter part) 39 | | [.hide()](http://api.jquery.com/hide/) | .hide() | since 0.5.0 | (No animation) 40 | | [.hover()](http://api.jquery.com/hover/) | | | (Later) 41 | | [.html()](http://api.jquery.com/html/) | get html | since 0.5.2 | 42 | | | set html | since 0.5.2 | (String value only) 43 | | [.index()](http://api.jquery.com/index/) | *Dart List API* | since 0.5.0 | 44 | | [.is()](http://api.jquery.com/is/) | | | (Later) 45 | | [.keydown()](http://api.jquery.com/keydown/) | | | (Later) 46 | | [.keypress()](http://api.jquery.com/keypress/) | | | (Later) 47 | | [.keyup()](http://api.jquery.com/keyup/) | | | (Later) 48 | | [.last()](http://api.jquery.com/last/) | | | (Later) 49 | | [.length](http://api.jquery.com/length/) | *Dart List API* | since 0.5.0 | 50 | | [.map()](http://api.jquery.com/map/) | *Dart List API* | since 0.5.0 | 51 | | [.mousedown()](http://api.jquery.com/mousedown/) | | | (Later) 52 | | [.mouseenter()](http://api.jquery.com/mouseenter/) | | | (Later) 53 | | [.mouseleave()](http://api.jquery.com/mouseleave/) | | | (Later) 54 | | [.mousemove()](http://api.jquery.com/mousemove/) | | | (Later) 55 | | [.mouseout()](http://api.jquery.com/mouseout/) | | | (Later) 56 | | [.mouseover()](http://api.jquery.com/mouseover/) | | | (Later) 57 | | [.mouseup()](http://api.jquery.com/mouseup/) | | | (Later) 58 | | [.next()](http://api.jquery.com/next/) | | | (Later) 59 | | [.nextAll()](http://api.jquery.com/nextAll/) | | | (Later) 60 | | [.nextUntil()](http://api.jquery.com/nextUntil/) | | | (Later) 61 | | [.not()](http://api.jquery.com/not/) | | | (Later) 62 | | [.off()](http://api.jquery.com/off/) | .off() | since 0.5.0 | 63 | | [.offset()](http://api.jquery.com/offset/) | get offset | since 0.5.2 | 64 | | | set offset | since 0.5.2 | (Point only) 65 | | | set offsetLeft | since 0.5.2 | (numeric value only) 66 | | | set offsetTop | since 0.5.2 | (numeric value only) 67 | | [.offsetParent()](http://api.jquery.com/offsetParent/) | get offsetParent | since 0.5.2 | 68 | | [.on()](http://api.jquery.com/on/) | .on() | since 0.5.0 | 69 | | [.one()](http://api.jquery.com/one/) | .one() | since 0.5.0 | 70 | | [.parent()](http://api.jquery.com/parent/) | .parent() | since 0.5.0 | 71 | | [.parentsUntil()](http://api.jquery.com/parentsUntil/) | | | (Later) 72 | | [.position()](http://api.jquery.com/position/) | get position | since 0.5.2 | 73 | | [.prepend()](http://api.jquery.com/prepend/) | .prepend() | since 0.5.2 | 74 | | [.prependTo()](http://api.jquery.com/prependTo/) | .prependTo() | since 0.5.2 | 75 | | [.prev()](http://api.jquery.com/prev/) | | | (Later) 76 | | [.prevAll()](http://api.jquery.com/prevAll/) | | | (Later) 77 | | [.prevUntil()](http://api.jquery.com/prevUntil/) | | | (Later) 78 | | [.pushStack()](http://api.jquery.com/pushStack/) | .pushStack() | since 0.5.0 | 79 | | [.ready()](http://api.jquery.com/ready/) | | | (Later) 80 | | [.remove()](http://api.jquery.com/remove/) | -- | | (Merged into .detach() API) 81 | | [.removeClass()](http://api.jquery.com/removeClass/) | .removeClass() | since 0.5.0 | 82 | | [.removeData()](http://api.jquery.com/removeData/) | .removeData() | since 0.5.0 | 83 | | [.resize()](http://api.jquery.com/resize/) | | | (Later) 84 | | [.scroll()](http://api.jquery.com/scroll/) | | | (Later) 85 | | [.scrollLeft()](http://api.jquery.com/scrollLeft/) | get scrollLeft | since 0.5.1 | 86 | | | set scrollLeft | since 0.5.1 | 87 | | [.scrollTop()](http://api.jquery.com/scrollTop/) | get scrollTop | since 0.5.1 | 88 | | | set scrollTop | since 0.5.1 | 89 | | [.select()](http://api.jquery.com/select/) | | | (Later) 90 | | [.show()](http://api.jquery.com/show/) | .show() | since 0.5.0 | (No animation) 91 | | [.siblings()](http://api.jquery.com/siblings/) | | | (Later) 92 | | [.slice()](http://api.jquery.com/slice/) | | | (Later) 93 | | [.submit()](http://api.jquery.com/submit/) | | | (Later) 94 | | [.text()](http://api.jquery.com/text/) | get text | since 0.5.2 | 95 | | | set text | since 0.5.2 | (String value only) 96 | | [.toggle()](http://api.jquery.com/toggle/) | .toggle() | since 0.5.0 | (No animation) 97 | | [.toggleClass()](http://api.jquery.com/toggleClass/) | .toggleClass() | since 0.5.0 | 98 | | [.trigger()](http://api.jquery.com/trigger/) | .trigger() | since 0.5.0 | 99 | | [.triggerHandler()](http://api.jquery.com/triggerHandler/) | .triggerHandler() | since 0.5.0 | 100 | | [.unbind()](http://api.jquery.com/unbind/) | -- | | (Use .off() instead) 101 | | [.undelegate()](http://api.jquery.com/undelegate/) | -- | | (Use .off() instead) 102 | | [.unload()](http://api.jquery.com/unload/) | | | (Later) 103 | | [.unwrap()](http://api.jquery.com/unwrap/) | | | (Later) 104 | | [.width()](http://api.jquery.com/width/) | get width | since 0.5.1 | (Only getter part) 105 | | [.wrap()](http://api.jquery.com/wrap/) | | | (Later) 106 | | [.wrapAll()](http://api.jquery.com/wrapAll/) | | | (Later) 107 | | [.wrapInner()](http://api.jquery.com/wrapInner/) | | | (Later) 108 | 109 | ###Static Functions 110 | 111 | | jQuery | DQuery | History | 112 | |:-----------|:------------|:------------| 113 | | [jQuery.unique()](http://api.jquery.com/jQuery.unique/) | DQuery.unique() | since 0.5.0 | 114 | -------------------------------------------------------------------------------- /lib/src/css.dart: -------------------------------------------------------------------------------- 1 | part of dquery; 2 | 3 | final Map _elemDisplay = { 4 | 'body': 'block' 5 | }; 6 | 7 | bool _isHidden(Element elem) => 8 | (elem as HTMLElement).style.display == 'none' || 9 | window.getComputedStyle(elem).display == 'none' || 10 | elem.ownerDocument?.contains(elem) != true; // TODO: do experiment 11 | 12 | void _showHide(List elements, bool show) { 13 | 14 | final values = {}; 15 | 16 | for (final elem in elements.cast()) { 17 | final oldDisplay = _dataPriv.get(elem, 'olddisplay'); 18 | if (oldDisplay != null) 19 | values[elem] = oldDisplay; 20 | final display = elem.style.display; 21 | 22 | if (show) { 23 | // jQuery: Reset the inline display of this element to learn if it is 24 | // being hidden by cascaded rules or not 25 | if (oldDisplay == null && display == "none") 26 | elem.style.display = ''; 27 | 28 | // jQuery: Set elements which have been overridden with display: none 29 | // in a stylesheet to whatever the default browser style is 30 | // for such an element 31 | if (elem.style.display == '' && _isHidden(elem)) 32 | _dataPriv.set(elem, 'olddisplay', values[elem] = _cssDefaultDisplay(elem.tagName)); 33 | 34 | } else if (!values.containsKey(elem)) { 35 | final hidden = _isHidden(elem); 36 | if (display.isNotEmpty && display != 'none' || !hidden) 37 | _dataPriv.set(elem, 'olddisplay', hidden ? display : elem.style.display); 38 | 39 | } 40 | 41 | } 42 | 43 | // Set the display of most of the elements in a second loop 44 | // to avoid the constant reflow 45 | for (final elem in elements.cast()) { 46 | final String display = elem.style.display; 47 | if (!show || display == 'none' || display == '') 48 | elem.style.display = show ? (values[elem] ?? '') : 'none'; 49 | } 50 | 51 | } 52 | 53 | // Try to determine the default display value of an element 54 | String _cssDefaultDisplay(String nodeName) { 55 | var display = _elemDisplay[nodeName]; 56 | if (display == null) { 57 | display = _actualDisplay(nodeName, document); 58 | 59 | // TODO: later (handle iframe) 60 | /* 61 | // If the simple way fails, read from inside an iframe 62 | if ( display == "none" || !display ) { 63 | // Use the already-created iframe if possible 64 | iframe = ( iframe || 65 | jQuery("