README.md 25.8 KB
Newer Older
1
# About g1
S Anand's avatar
S Anand committed
2

3
`g1` is library of interaction patterns in [Gramex](https://learn.gramener.com/guide/).
S Anand's avatar
S Anand committed
4

5
Install using:
S Anand's avatar
S Anand committed
6

7
8
9
    yarn install g1
    # ... OR ...
    npm install --save g1
S Anand's avatar
S Anand committed
10

11
To use all features, add this to your HTML:
S Anand's avatar
S Anand committed
12

13
    <script src="node_modules/g1/dist/g1.min.js"></script>
S Anand's avatar
S Anand committed
14

S Anand's avatar
S Anand committed
15
16
Or import one of the individual libraries below. [g1.min.js](dist/g1.min.js) has all of these
functions. [g.js](dist/g.js) is an un-minified version for debugging.
S Anand's avatar
S Anand committed
17

18
19
Interactions:

S Anand's avatar
S Anand committed
20
- [urlfilter.min.js](dist/urlfilter.min.js): URL filtering library
21
22
23
24
    - [$.urlfilter](#urlfilter) changes URL query parameters when clicked. Used to filter data.
    - [g1.url.parse](#urlparse) parses a URL into a structured object
    - [g1.url.join](#urljoin) joins two URLs
    - [g1.url.update](#urlupdate) updates a URL's query parameters
25
26
27
28
- [highlight.min.js](dist/highlight.min.js): highlighting library
    - [$.highlight](#highlight) toggles classes on elements when clicked or hover
- [search.min.js](dist/search.min.js): search-as-you-type library
    - [$.search](#search)
29
30
- [datafilter.min.js](dist/datafilter.min.js): filtering data library
    - [$.datafilter](#datafilter) filters the data based on the options
31
32
33

Data components:

S Anand's avatar
S Anand committed
34
35
- [formhandler.min.js](dist/formhandler.min.js): Table renderer using [FormHandler](https://learn.gramener.com/guide/formhandler/)
    - [$.formhandler](#formhandler) renders a HTML table from a [FormHandler URL](https://learn.gramener.com/guide/formhandler/)
36
37
38
39
40
- [leaflet.min.js](dist/leaflet.min.js): Leaflet utilities
    - [L.TopoJSON](#ltopojson) loads TopoJSON files just like GeoJSON. Requires [topojson](https://github.com/topojson/topojson)

Utilities:

S Anand's avatar
S Anand committed
41
- [template.min.js](dist/template.min.js): template library
42
    - [$.template](#template) renders lodash templates. Requires [lodash](https://lodash.com/)
S Anand's avatar
S Anand committed
43
- [event.min.js](dist/event.min.js): event library
44
    - [$.dispatch](#dispatch) is like [trigger](https://api.jquery.com/trigger/) but sends a native event (triggers non-jQuery events too)
S Anand's avatar
S Anand committed
45
46
- [types.min.js](dist/types.min.js): type detection library
    - [g1.types](#types) returns the data types of columns in a DataFrames
S Anand's avatar
S Anand committed
47

48
## $.urlfilter
S Anand's avatar
S Anand committed
49
50
51
52
53
54
55
56
57
58

Example:

```html
<a class="urlfilter" href="?name=John">Link</a>
<script>
  $('body').urlfilter()
</script>
```

59
60
61
Let's say the page is `?city=NY`. Clicking on any `.urlfilter` (trigger) in
`body` (container) opens `?city=NY&name=John`. The `href=` in the `.urlfilter`
link *updates* the current page URL instead of replacing it.
S Anand's avatar
S Anand committed
62
63
64
65
66
67
68
69
70
71

`data-mode` controls the way the URL is updated by the `href`:

| URL    | href      | default    | `data-mode="add"` | `data-mode="toggle"` | `data-mode="del"` |
|--------|-----------|------------|-------------------|----------------------|-------------------|
| `?`    | `?x=1`    | `?x=1`     | `?x=1`            | `?x=1`               | `?`               |
| `?x=1` | `?x=1`    | `?x=1`     | `?x=1&x=1`        | `?`                  | `?`               |
| `?x=1` | `?y=1`    | `?x=1&y=1` | `?x=1&y=1`        | `?x=1&y=1`           | `?x=1`            |
| `?x=1` | `?x=2`    | `?x=2`     | `?x=1&x=2`        | `?x=1&x=2`           | `?x=1`            |

72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
For example:

```html
<a class="urlfilter" href="city=NY">                        Change ?city= to NY</a>
<a class="urlfilter" href="city=NY" data-mode="add">        Add ?city= to NY</a>
<a class="urlfilter" href="city=NY" data-mode="del">        Remove NY from ?city=</a>
<a class="urlfilter" href="city=NY" data-mode="toggle">     Toggle NY in ?city=</a>

<a class="urlfilter" href="city=NY" data-target="pushState">Change ?city= to NY using pushState</a>
<a class="urlfilter" href="city=NY" data-target="#">        Change location.hash, i.e. #?city= to NY</a>
<a class="urlfilter" href="city=NY" data-target="iframe">   Change iframe URL ?city= NY</a>
    <iframe src="?country=US"></iframe>
<a class="urlfilter" href="city=NY" data-target=".block">   Use AJAX to load ?city=NY into .block</a>
    <div class="block" src="?country=US"></div>
<script>
  $('body').urlfilter()     // Activate all the .urlfilter elements above
</script>
```

91
### $.urlfilter attributes
S Anand's avatar
S Anand committed
92

93
URLFilter triggers use these attributes:
S Anand's avatar
S Anand committed
94

95
96
97
- `class="urlfilter"` indicates that this is a trigger
- `href=` updates the page URL with this link
- `data-target` defines the target where the URL is updated:
S Anand's avatar
S Anand committed
98
99
100
101
    - default: updates `window.location`
    - `pushState`: updates the current page using pushState
    - `#`: updates the `window.location.hash`
    - `.class`: loads URL into `.class` by updating its `src=` attribute as the base URL
102
103
104
105
106
- `data-mode` can be
    - empty - updates existing query key with value
    - `add` - adds a new query key and value
    - `del` - deletes query key. If value exists, only deletes the (key, value) combination
    - `toggle` - toggles the query key and value combination
S Anand's avatar
S Anand committed
107
- `data-remove`: removes any URL query parameters without values. e.g. `?x&y=1` becomes `?`
108
- `data-src` changes which attribute holds the current URL when `data-target=` is a selector. Default: `src`
S Anand's avatar
S Anand committed
109

110
URLFilter containers uses these attributes:
S Anand's avatar
S Anand committed
111

112
113
114
115
- `data-selector` changes which nodes urlfilter applies to. Default: `.urlfilter`
- `data-attr` changes which attribute updates the URL. Default: `href`
- You can also specify `data-mode`, `data-remove` and `data-src`. This is the same as specifying on
  every `.urlfilter` trigger.
S Anand's avatar
S Anand committed
116

117
### $.urlfilter events
S Anand's avatar
S Anand committed
118

119
- `urlfilter` is fired on the trigger when the URL is changed. Attributes:
S Anand's avatar
S Anand committed
120
121
122
123
    - `url`: the new URL
- `load` is fired on the target when the URL is loaded -- only if the `data-target=` is a selector. Attributes:
    - `url`: the new URL

124
## $.highlight
S Anand's avatar
S Anand committed
125

126
Highlight elements when hovering on or clicking another element.
S Anand's avatar
S Anand committed
127

128
Example:
S Anand's avatar
S Anand committed
129

130
131
132
133
134
135
136
137
138
```html
<div data-toggle="highlight" data-target="a.red" data-classes="active">Link</a>
<div data-toggle="highlight" data-target="a.red" data-classes="active" data-mode="click">Link</a>
<script>
  $('body').highlight({
    classes: 'shadow',
    mode: 'click'
  })
</script>
139
```
S Anand's avatar
S Anand committed
140

141
### $.highlight attributes
S Anand's avatar
S Anand committed
142

143
Highlight triggers use these attributes:
S Anand's avatar
S Anand committed
144

145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
- `data-toggle="highlight"` indicates that the element acts as a highlighter
- `data-target=` selectors to highlight (required)
- `data-mode="click"` highlights on click. Use `data-mode="hover"` to higlight on hover (default)
- `data-classes=` space-separated class names to toggle on target elements

Highlight containers use these attributes:

- `data-selector=` changes which nodes highlight applies to. Default: `[data-toggle="highlight"]`
- `data-mode` is the same as specifying data-mode on every highlighter. Default: `hover`
- `data-attr` is the attribute that defines classes to toggle. Default: `data-classes`
- `data-classes=` is the same as specifiying the data-classes on every highlighter. Default: `active`

### $.highlight events

- `highlight` is fired on the trigger when activated. Attributes:
    - `target`: elements that match the `data-target=` selector


## $.search

Highlight elements by searching as you type.

Example:

### $.search attributes

`.search` triggers use these attributes:

- `data-toggle="search"`
S Anand's avatar
S Anand committed
174

175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
## datafilter


`g1.datafiilter(data, filters)` returns the filtered data based on the filters. For example:

```js
var data = [
  {"ID": "1", "product": "Fan", "sales": "100", "city": "NY"},
  {"ID": "2", "product": "Fan", "sales": "80", "city": "London"},
  {"ID": "3", "product": "Fan", "sales": "120", "city": "NJ"},
  {"ID": "4", "product": "Fan", "sales": "130", "city": "London"},
  {"ID": "5", "product": "Light", "sales": "500", "city": "NY"},
  {"ID": "5", "product": "Light", "sales": "100", "city": "London"}
]

190
191
192
g1.datafilter(data, [{col: 'sales', op: '>', val: 100},
                    {col: 'city', op: 'in', val: ['London', 'NY']},
                    {col: 'product', val: 'Fan'}])
193
194
195
196
197
198
199
200
201
202
203
204
205
206

// Returns [{"ID": "3", "product": "Fan", "sales": "120", "city": "NJ"}, {"ID": "4", "product": "Fan", "sales": "130", "city": "London"}]
```

## datafilter options

datafilter() contains three parameters:

  - data: a list of objects
  - filters: a list of objects, that will contains the below keys:
    - col: column to be filtered.
    - op: operator to be applied for filteration. default: `=`
    - val: value of the selected column
  - options: a dictionary that contains the below keys:
207
    - limit: result is limited to. default: `1000`
208
209
210
211
212
213
214
215
216
    - offset: filtering data should start from. default: `0`
    - sort: a list of objects, that will contains the below keys:
      - column: column to be sorted
      - order: asc or desc. default: `asc`
    - columns: a list of objects, that will contains the below keys:
      - allow: a list of column names to be returned in the filtered data
      - not: a list of column names to be skiped in the filtered data

Rules:
217

218
219
220
221
222
223
224
225
226
227
228
229
  - the key `op` may contains any one of the below values:
    - `=`
    - `!=`
    - `>`
    - `<`
    - `>=`
    - `<=`
    - `~`
    - `!~`
    - `in`


S Anand's avatar
S Anand committed
230

S Anand's avatar
S Anand committed
231
232
## $.formhandler

233
234
An interactive table component for [FormHandler][formhandler] data.

S Anand's avatar
S Anand committed
235
```html
236
<div class="formhandler" data-src="formhandler-url" data-page-size="10"></div>
S Anand's avatar
S Anand committed
237
<script>
238
239
240
$('.formhandler').formhandler({
  pageSize: 20
})
S Anand's avatar
S Anand committed
241
242
243
</script>
```

244
245
Options can passed via an options dict, and over-ridden using `data-` attributes.
In the above example, `data-page-size="10"` over-rides `pageSize: 20`.
S Anand's avatar
S Anand committed
246

247
[formhandler]: https://learn.gramener.com/guide/formhandler/
S Anand's avatar
S Anand committed
248
249
250

### $.formhandler options

251
The full list of options is below. Simple options can be specified as `data-` attributes as well.
S Anand's avatar
S Anand committed
252

253
- `src`: [FormHandler][formhandler] URL endpoint
S Anand's avatar
S Anand committed
254
- `columns`: comma-separated column names to display, or a list of objects with these keys:
255
    - `name`: column name. `"*"` is a special column placeholder for "all columns"
256
257
258
    - `title`: for header display. Defaults to the same value as `name`
    - `type`: `text` (default) / `number` / `date`. Data type. Determines filters to be used
    - `format`: string / function that renders the cell contents.
259
260
261
262
263
264
265
      - functions are applied to the value and the return value is used
      - strings specify a numeral.js format if the value is a number (you must include numeral.js)
      - strings specify a moment.js format if the value is a date (you must include moment.js)
    - `sort`: `true` / `false` / operators dict with:
      - `{'': 'Sort ascending', '-': 'Sort descending'}` (default)
    - `filters`: `true` (default) / `false` / operators dict with:
      - `{'', 'Equals...', '!', 'Does not equal...', ...}`.
S Anand's avatar
S Anand committed
266
        The default list of operators is based on the auto-detected type of the column.
267
268
269
270
271
272
273
    - `link`: string / function that generates a link for this each cell.
      - If no `link:` is specified, clicking on the cell filters by that cell.
      - If `link:` is a string, opens a new window with the string URL interpolated as a lodash template with `row` as data.
        Example: `"https://example.org/city/<%- city >"`
      - If `link:` is a function, opens a new window with the URL as `fn(row)`.
        Example: `function(row) { return 'https://example.org/city/' + row.city }`
    - `hideable`: `true` (default) / `false`. Hides the column
274
    - `unique`: TODO: {dict of query parameter and display value} or [list of values] or function?
S Anand's avatar
S Anand committed
275
- `table`: Shows the table control. Can be `true` (default) / `false`
276
- `count`: Shows the number of rows. Can be `true` (default) / `false`
S Anand's avatar
S Anand committed
277
278
279
280
281
282
283
- `page`: Shows the page control. Can be `true` (default) / `false`.
- `pageSize`: page size. Defaults to 100
- `size`: Shows the page size control. Can be `true` (default) / `false`
- `sizeValues`: Allowed page size values. Defaults to `[10, 20, 50, 100, 500, 1000]`
- `export`: Shows the export control. Can be `true` (default) / `false`
- `exportFormats`: {xlsx: 'Excel'}
- `filters`: Shows the applied filters control. Can be `true` (default) / `false`
284
285
286
287
288
289
- `transform`: an optional function() that modifies data. It accepts a dict that has keys:
    - `data`: the FormHandler data
    - `meta`: the FormHandler metadata from the `FH-*` HTTP headers
    - `args`: the URL query parameters passed to the FormHandler
    - `options`: the options applicable to the FormHandler
    - returns a dict with modified values of `data` and `meta`
S Anand's avatar
S Anand committed
290
291
292
293

**Advanced**. Each component can have a target which specifies a selector. For
e.g., to render the export button somewhere else, use
`data-export-target=".navbar-export"`. This replaces the `.navbar-export`
294
295
296
contents with the export button. (It searches within the table container for
`.navbar-export` first, and if not found, searches everywhere.) Available
targets are:
S Anand's avatar
S Anand committed
297
- `tableTarget`
298
- `countTarget`
S Anand's avatar
S Anand committed
299
300
301
302
303
304
305
306
307
308
- `pageTarget`
- `sizeTarget`
- `exportTarget`
- `filtersTarget`
- `searchTarget`

**Advanced**: Each component's template string can be over-ridden. For example,
`data-search-template="<input type='search'>"` will replace the search template
with a simple input. Available template strings are:
- `tableTemplate`
309
- `countTemplate`
S Anand's avatar
S Anand committed
310
311
312
313
314
315
- `pageTemplate`
- `sizeTemplate`
- `exportTemplate`
- `filtersTemplate`
- `searchTemplate`

316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
Features to be implemented:

- Loading indicator
- Full text search
- URL prefix / namespace, if there are multiple tables on the same page
- URL targets other than '#', e.g. pushState

### $.formhandler events

- `load` is fired on the source when the template is rendered. Attributes:
    - `formdata`: the FormHandler data
    - `meta`: the FormHandler metadata
    - `args`: the URL query parameters passed to the request
    - `options`: applied options to the FormHandler

### $.formhandler examples
S Anand's avatar
S Anand committed
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348

Add a simple table using the FormHandler at `./data` that shows specific columns
with a page size of 10 rows, and does not show the export filter.

```html
<div class="formhandler"
     src="./data"
     data-columns="id,country,state,sales"
     data-page-size="10"
     data-export="false"
></div>
<script>
  $('.formhandler').formhandler()
</script>
```


349
350
351
352
353
## $.template

```html
<script type="text/html">Your platform is <%= navigator.userAgent %></script>
<script>
354
  $('body').template()
355
356
357
</script>
```

358
359
renders all `script[type="text/html"]` as [lodash templates](https://lodash.com/docs/#template).
This displays `Your platform is ...` and shows the userAgent just below the script tag.
360
361
362
363

- Anything inside `<% ... %>` is executed as Javascript.
- Anything inside `<%= ... %>` is evaluated in-place.

364
365
The template can use all global variables. You can pass additional variables
using as `.template({var1: value, var2: value, ...})`. For example:
366
367
368
369
370
371
372
373

```html
<script type="text/html">
  <% list.forEach(function(item) { %>
    <div><%= item %></div>
  <% }) %>
</script>
<script>
374
  $('body').template({list: ['a', 'b', 'c']})
375
376
377
</script>
```

378
379
To re-render the template, run `.template(data)` again with different data.

380
381
382
383
384
## $.template options

To re-use the template, i.e. render the same template on a different DOM node,
run `.template(data, {target: selector})`. This allows you to declare templates
once and apply them across the body. For example:
385
386
387

```js
$('script.chart')
388
389
390
    .template({heading: 'Dashboard 1'}, {target: '.dashboard1'})
    .template({heading: 'Dashboard 2'}, {target: '.dashboard2'})
    .template({}, {target: '.no-heading'})
391
```
392

393
394
395
396
397
398
399
## $.template attributes

Template containers can have an `src=` attribute that loads the template from a file:

```html
<script type="text/html" src="template.html"></script>
<script>
400
  $('body').template()
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
</script>
```

If the `src=` URL returns a HTTP error, the HTML *inside* the script is rendered as
a template. The template can use:

- all data passed by the `$().template()` function, and
- an [xhr](http://api.jquery.com/Types/#jqXHR) object - which has error details

For example:

```html
<script type="text/html" src="missing.html">
  Template returned error code: <%= xhr.status %>.
  Data is <%= data %>
</script>
<script>
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
  $('body').template({data: data})
</script>
```

`$().template()` renders all `script[type="text/html"]` nodes.
Use `data-selector=` attribute to change the selector. For example:

```html
<section data-selector="script.lodash-template">
  <script class="lodash-template">...</script>
</section>
<script>
  $('section').template()
</script>
```

Or you can directly render templates using

```html
<script>
  $('script.lodash-template').template()
439
440
441
442
443
444
445
446
447
448
</script>
```

### $.template events

- `template` is fired on the source when the template is rendered. Attributes:
    - `templatedata`: the passed data
    - `target`: the target nodes rendered, as a jQuery object

For example:
449
450
451

```js
$('script[type="text/html"]')
452
453
454
455
456
457
  .on('template', function(e) {       // Returns nodes rendered by the template
    e.target                          // Get the target nodes
      .filter('div')                  // Filter all <div> elements inside
      .attr('class', 'item')          // Change their class
  })
  .template()                         // Trigger the template AFTER binding the event handler
458
459
460
461
```

## $.dispatch

S Anand's avatar
S Anand committed
462
463
Triggers a native JavaScript event. For example:

464
465
466
467
```js
$('a.action').dispatch('click')
```

S Anand's avatar
S Anand committed
468
469
470
471
sends a click to `a.action`. Like [$.trigger](https://api.jquery.com/trigger/),
but this will fire non-jQuery event handlers as well.

### $.dispatch options
472
473
474
475
476
477
478
479
480

You can add an optional dict as the second parameter. It can have any
[event properties](https://developer.mozilla.org/en-US/docs/Web/API/Event#Properties)
as attributes. For example:

```js
$('a.action').dispatch('click', {bubbles: true, cancelable: false})
```

S Anand's avatar
S Anand committed
481
482
483
484
485
486
- bubbles: whether the event bubbles or not. default: true
- cancelable: whether the event is cancelable or not. default: true
- All other `new Event()` options will also work

https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events

487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507

## L.TopoJSON

```js
var layer = new L.TopoJSON(topojson_data).addTo(map)
```

adds a TopoJSON layer to a leaflet map. The usage is identical to [L.GeoJSON()](http://leafletjs.com/reference-1.2.0.html#geojson).

Typical usage is below:

```js
$.get('topojson-file.json')
  .done(function(topojson_data) {
    var map = L.map('map-id')
    var layer = new L.TopoJSON(topojson_data).addTo(map)
    map.fitBounds(layer.getBounds())
  })
```


S Anand's avatar
S Anand committed
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
## url.parse

`g1.url` provides URL manipulation utilities.

```js
var url = g1.url.parse("https://username:password@example.com:80/~folder/subfolder/filename.html?a=1&a=2&b=3%2E&d#hash")
```

### url object attributes

This parses the URL and returns an object with the following attributes matching `window.location`:

| Attribute  | Value                              |
|------------|------------------------------------|
| `href`     | the original URL                   |
| `protocol` | `https`                            |
| `origin`   | `username:password@example.com:80` |
| `username` | `username`                         |
| `password` | `password`                         |
| `hostname` | `example.com`                      |
| `port`     | `80`                               |
| `pathname` | `folder/subfolder/filename.html`   |
| `search`   | `a=1&a=2&b=3%2E&d`                 |
| `hash`     | `hash`                             |

... and additional attributes:

| Attribute    | Value                                                  |
|--------------|--------------------------------------------------------|
| `userinfo`   | `username:password`                                    |
| `relative`   | `folder/subfolder/filename.html?a=1&a=2&b=3%2E&d#hash` |
| `directory`  | `folder/subfolder/`                                    |
| `file`       | `filename.html`                                        |
| `searchKey`  | `{'a:'2', b:'3.', d:''}`                               |
| `searchList` | `{'a:['1', '2'], b:['3.'], d:['']}`                    |

It can also parse URL query strings.

```js
var url = g1.url.parse('?a=1&a=2&b=3%2E&d#hash')
```

| Attribute    | Value                            |
|--------------|----------------------------------|
| `search`     | `a=1&a=2&b=3%2E&d`               |
| `hash`       | `hash`                           |
| `searchKey`  | `{a:'2', b:'3.', d:''}`          |
| `searchList` | `a:['1', '2'], b:['3.'], d:['']` |

These attributes are **not mutable**. To change the URL, use
[url.join](#urljoin) or [url.update](#urlupdate).

### url object methods

The url object has a `.toString()` method that converts the object back into a
string.


## url.join

```js
var url = url.join(another_url)
```

updates the `url` with the attributes from `another_url`. For example:

| url                    | joined with          | gives                      |
|------------------------|----------------------|----------------------------|
| `/path/p`              | `a/b/c`              | `/path/a/b/c`              |
| `/path/p/q/`           | `../a/..`            | `/path/p/`                 |
| `http://host1/p`       | `http://host2/q`     | `http://host2/q`           |
| `https://a:b@host1/p`  | `//c:d@host2/q?x=1`  | `https://c:d@host2/q?x=1`  |
| `/path/p?b=1`          | `./?a=1#top`         | `/path/?a=1#top`           |

`.join()` updates the query parameters and hash fragment as well. To prevent this, use:

```js
url.join(another_url, {query: false, hash: false})
```

For example:

```js
g1.url.parse('/').join('/?x=1#y=1', {hash: false}).toString() == '/?x=1';
g1.url.parse('/').join('/?x=1#y=1', {query: false}).toString() == '/#y=1';
```


## url.update

```js
var url = url.update(object)
```

updates the `url` query parameters with the attributes from `object`. For example:

| url          | updated with         | gives                 |
|--------------|----------------------|-----------------------|
| `/`          | `{a:1}`              | `/?a=1`               |
| `/?a=1&b=2`  | `{b:3, a:4, c:''}`   | `/?a=4&b=3&c=`        |
| `/?a=1&b=2`  | `{a:null}`           | `/?b=2`               |
| `/?a=1&b=2`  | `{a:[3,4], b:[4,5]}` | `/?a=3&a=4&b=4&b=5`   |

By default, it *updates* the query parameters. But:

- `url.update(object, 'add')` *adds* the query parameters instead of updating
- `url.update(object, 'del')` *deletes* the query parameters instead of updating
- `url.update(object, 'toggle')` *toggles* the query parameters (i.e. adds if missing, deletes if present)

For example:

| url                 | updated with         | in mode             | gives                 |
|---------------------|----------------------|---------------------|-----------------------|
| `/?a=1&a=2`         | `{a:3, b:1}`         | `add`               | `/?a=1&a=2&a=3&b=1`   |
| `/?a=1&a=2'`        | `{a:[3,4]}`          | `add`               | `/?a=1&a=2&a=3&a=4`   |
| `/?a=1&a=2&b=1`     | `{a:2, b:2}`         | `del`               | `/?a=1&b=1`           |
| `/?a=1&a=2&b=1`     | `{a:[1,4]}`          | `del`               | `/?a=2&b=1`           |
| `/?a=1&a=2`         | `{a:1, b:1}`         | `toggle`            | `/?a=2&b=1`           |
| `/?a=1&a=2&b=1&b=2` | `{a:[2,3], b:[1,3]}` | `toggle`            | `/?a=1&a=3&b=2&b=3`   |

You can specify different modes for different query parameters.


```js
g1.url.parse('/?a=1&b=2&c=3&d=4')       // Update this URL
  .update({a:1, b:[2,3], c:6, d:7},     // With this object
          'a=del&b=toggle&c=add')       // Delete ?a, Toggle ?b, add ?c, update ?d (default)
// Returns /?b=3&c=3&c=6&d=7
```
S Anand's avatar
S Anand committed
637

S Anand's avatar
S Anand committed
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
## types

`g1.types(data)` returns the column data types. For example:

```js
var data = [
  {a: 1, b: 1.1, c: 'c', d: '2014-04-04', e: true},
  {a: 2, b: 2},
]
g1.types(data)      // Returns {"a": "number", "b": "number", "c": "string", "d": "date", "e": "boolean"}
```

### types options

`types()` accepts 2 parameters:

- `data`: a list of objects
- `options`: a dictionary that may contain these keys:
  - `convert`: converts values to the right type. For example, "1" is converted to 1. default: `false`
  - `limit`: number of rows to evaluate. default: 1000
  - `ignore`: list of values that should be ignored. default: `[null, undefined]`

Rules:

- Evaluate up to `limit` rows
- Ignore values that are keys in the `ignore` option. Only consider the rest
- If `convert` is `false`, then for each column:
  - If all values are Date objects -> `date`
  - Else if all values are numbers -> `number`
  - Else if all values are strings -> `string`
  - Else if all values are bools -> `boolean`
  - Else if there are no values or is undefined or null -> `null`
  - Else -> `mixed`
- Else if `convert` is `true`, then for each column:
  - If all values can be converted to Date -> `date`
  - Else if all values can be converted to numbers -> `number`
  - Else if all values are bools -> `boolean`
  - Else if there are no values or is undefined or null  -> `null`
  - Else -> `string`

678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
## Interaction conventions

All interaction components use this naming convention:

- Interactions are enabled on a *container*, typically `body`. For example,
  `$('body').urlfilter()`. Containers have these common attributes:
    - `data-selector`: selector to identify triggers. e.g. `.urlfilter`, `.highlight`
    - `data-target`: selector that all triggers act on by default
    - `data-mode`: mode of interaction for all triggers
    - `data-attr`: attribute that contains the interaction data, e.g. `href` for `.urlfilter`
- Interactions are triggered on a *trigger*. For example, `.urlfilter` for `$().urlfilter()`.
  Clicking / hovering on / typing in a trigger triggers the interaction.
    - `data-target`: selector that this trigger acts on
    - `data-mode`: mode of interaction for this trigger
- Interactions change a *target*. For example, `urlfilter` changes `location.href` by default. The
  `data-target` on containers and triggers define this.
- Interactions data is contained in an attribute. This is applied to the target. For example,
  `.urlfilter` applied `href` to the target. The attribute name is stored in `data-attr`.
- Interactions have *modes*. This can be controlled using `data-mode=`.

All container `data-` attributes can also be passed as an option to the
function. For example, `<body data-selector=".link">` is the same as
`$('body').urlfilter({selector: '.link'})`.

S Anand's avatar
S Anand committed
702
703
704
705
706
707
708
## Contributing

Contributions are welcome.

- Please report bug fixes on the [issues page](https://code.gramener.com/s.anand/g1/issues)
- Developers may read [CONTRIBUTING.md](CONTRIBUTING.md) to understand the file
  structure and how to set this repository up