pixl-xyapp 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +11 -0
- package/README.md +485 -0
- package/css/base.css +2736 -0
- package/css/boilerplate.css +295 -0
- package/css/normalize.css +349 -0
- package/js/base.js +648 -0
- package/js/calendar.js +166 -0
- package/js/datetime.js +233 -0
- package/js/dialog.js +385 -0
- package/js/misc.js +311 -0
- package/js/page.js +1940 -0
- package/js/popover.js +158 -0
- package/js/select.js +845 -0
- package/js/tools.js +1212 -0
- package/js/unscroll.min.js +3 -0
- package/package.json +20 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# License
|
|
2
|
+
|
|
3
|
+
**The MIT License (MIT)**
|
|
4
|
+
|
|
5
|
+
*Copyright (c) 2025 PixlCore.com*
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
8
|
+
|
|
9
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
10
|
+
|
|
11
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
# Overview
|
|
2
|
+
|
|
3
|
+
The **pixl-xyapp** package is a client-side JavaScript/CSS framework, designed for the [xyOps Web Application](https://xyops.io). It consists of a number of JavaScript classes, utility functions, and basic CSS elements (header, tabs, dialogs, progress bars, form elements, etc.). [jQuery](http://jquery.com/) is required for all features to work properly.
|
|
4
|
+
|
|
5
|
+
# Usage
|
|
6
|
+
|
|
7
|
+
You can use [npm](https://www.npmjs.com/) to install the module:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
npm install pixl-xyapp
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or just download the files from the [GitHub repo](https://github.com/pixlcore/pixl-xyapp). There is no installation script. This is basically just a collection of JavaScript, CSS and web fonts that you must include manually.
|
|
14
|
+
|
|
15
|
+
It is important that you include the JavaScript files in the proper order. You can of course use tools such as [UglifyJS](https://www.npmjs.com/package/uglify-js) to compact them all together into a single blob for distribution. But for development, it is best to include them separately. For example, the CSS:
|
|
16
|
+
|
|
17
|
+
```html
|
|
18
|
+
<link rel="stylesheet" href="css/normalize.css">
|
|
19
|
+
<link rel="stylesheet" href="css/base.css">
|
|
20
|
+
<link rel="stylesheet" href="css/YOUR-OWN-STYLE.css">
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
And the JavaScript:
|
|
24
|
+
|
|
25
|
+
```html
|
|
26
|
+
<script src="js/common/misc.js"></script>
|
|
27
|
+
<script src="js/common/tools.js"></script>
|
|
28
|
+
<script src="js/common/datetime.js"></script>
|
|
29
|
+
<script src="js/common/page.js"></script>
|
|
30
|
+
<script src="js/common/dialog.js"></script>
|
|
31
|
+
<script src="js/common/popover.js"></script>
|
|
32
|
+
<script src="js/common/select.js"></script>
|
|
33
|
+
<script src="js/common/calendar.js"></script>
|
|
34
|
+
<script src="js/common/base.js"></script>
|
|
35
|
+
<script src="js/YOUR-APP-CODE.js"></script>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Configuration
|
|
39
|
+
|
|
40
|
+
The app framework requires a configuration object to be loaded into `window.config` and copied in `app.config`. This can be loaded however you like (inline script tag, etc.). An simple example is shown here (taken from the demo app):
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
window.config = app.config = {
|
|
44
|
+
// Define all app's pages in 'Page' array
|
|
45
|
+
Page: [
|
|
46
|
+
{ ID: 'Home' },
|
|
47
|
+
{ ID: 'MoreDemos' }
|
|
48
|
+
],
|
|
49
|
+
|
|
50
|
+
// Which page to load by default (if not in URL hash)
|
|
51
|
+
DefaultPage: 'Home'
|
|
52
|
+
};
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The only required properties in the configuration object are the `Page` array, which contains objects for each of your pages (more on this below in [Pages](#pages)), and the `DefaultPage` string, which declares which page loads by default. Everything else is optional, and this structure can be extended for your own uses.
|
|
56
|
+
|
|
57
|
+
## Main Application
|
|
58
|
+
|
|
59
|
+
Your main application object is located in the global scope under the name `app`. It is a plain object with variables and classes you can override, and of course add your own. You can use the built-in `extend()` method to add your own properties and methods, if you like. Example:
|
|
60
|
+
|
|
61
|
+
```javascript
|
|
62
|
+
app.extend({
|
|
63
|
+
|
|
64
|
+
// This name will appear in the window title
|
|
65
|
+
name: 'My App',
|
|
66
|
+
|
|
67
|
+
// init() is called on page load
|
|
68
|
+
init: function() {
|
|
69
|
+
// initialize application
|
|
70
|
+
|
|
71
|
+
// Setup page manager for tabs
|
|
72
|
+
this.page_manager = new PageManager( config.Page );
|
|
73
|
+
|
|
74
|
+
// start monitoring URL hash changes for page transitions
|
|
75
|
+
Nav.init();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The first thing you need to do is add a `name` property, set to the name of your application. This is used in a number of places (such as window titles). Also, an `init()` method, which is called when the DOM is ready.
|
|
82
|
+
|
|
83
|
+
The only other requirement is the `init()` method, in which you need to construct a `PageManager()` instance, passing it your `Page` array from the configuration object, and assigning it to `this.page_manager` (must be exact). Then call `Nav.init()` to start the page navigation system. You can also add your own application startup tasks here.
|
|
84
|
+
|
|
85
|
+
Feel free to extend this object with whatever properties or methods your app requires.
|
|
86
|
+
|
|
87
|
+
## Pages
|
|
88
|
+
|
|
89
|
+
Each "page" in your web application is virtual. It's basically a DIV that is shown when the page is activated, hidden when deactivated, and a JavaScript class upon which methods are called when the page state changes (activated, deactivated, etc.). Each page has a unique ID, which must first be defined in your configuration:
|
|
90
|
+
|
|
91
|
+
```javascript
|
|
92
|
+
{
|
|
93
|
+
Page: [
|
|
94
|
+
{ ID: 'Home' },
|
|
95
|
+
{ ID: 'MoreDemos' }
|
|
96
|
+
],
|
|
97
|
+
DefaultPage: 'Home'
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
In this example your HTML should be setup like this:
|
|
102
|
+
|
|
103
|
+
```html
|
|
104
|
+
<div id="main" class="main">
|
|
105
|
+
<div id="page_Home" style="display:none"></div>
|
|
106
|
+
<div id="page_MoreDemos" style="display:none"></div>
|
|
107
|
+
</div>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Finally, each virtual page in your app should inherit from the `Page` base class. It contains placeholders for all the methods you can override (described below), as well as a few utility methods for rendering tables and tabs.
|
|
111
|
+
|
|
112
|
+
Here is an example class, showing the bare minimum you'll need to add:
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
Class.subclass( Page, "Page.Home", {
|
|
116
|
+
|
|
117
|
+
onInit: function() {
|
|
118
|
+
// called once at page load
|
|
119
|
+
var html = '';
|
|
120
|
+
|
|
121
|
+
// include initial HTML here, if you want
|
|
122
|
+
|
|
123
|
+
this.div.html( html );
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
onActivate: function(args) {
|
|
127
|
+
// page activation
|
|
128
|
+
if (!args) args = {};
|
|
129
|
+
this.args = args;
|
|
130
|
+
|
|
131
|
+
app.setWindowTitle('Home');
|
|
132
|
+
app.showTabBar(true);
|
|
133
|
+
|
|
134
|
+
// activate page here (show live / updated content)
|
|
135
|
+
var html = 'Hello there!';
|
|
136
|
+
this.div.html( html );
|
|
137
|
+
|
|
138
|
+
return true;
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
onDeactivate: function() {
|
|
142
|
+
// called when page is deactivated
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
} );
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
You have the choice of including your HTML markup in the `index.html` file, or building the HTML as a string in the `onInit()` method (called *only once*), or building the HTML as a string in the `onActivate()` method (called *every time* your page is activated).
|
|
150
|
+
|
|
151
|
+
### onInit
|
|
152
|
+
|
|
153
|
+
The `onInit()` method is called on your page only once, at load time. This allows you to setup things like initial HTML markup (or you can just put this in the `index.html` file), and any other initialization tasks your page might need. A `div` property points to your page's DIV element (jQuery wrapped).
|
|
154
|
+
|
|
155
|
+
The function takes no arguments, and there is no return value.
|
|
156
|
+
|
|
157
|
+
### onActivate
|
|
158
|
+
|
|
159
|
+
The `onActivate()` method is called *every time* your page is activated. Your DIV is automatically shown, but you can use this method to update the page contents if you want.
|
|
160
|
+
|
|
161
|
+
Your function may be passed an `args` object, if the URL hash contains a query string. For example:
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
http://myapp.com/#Home?foo=bar&baz=1234
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
This URL would load the `Home` virtual page, and the `onActivate()` method would be passed an `args` object containing:
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
{
|
|
171
|
+
foo: "bar",
|
|
172
|
+
baz: 1234
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Another typical thing to do in your `onActivate()` method is to call `app.setWindowTitle()` to set the browser window / tab title, and `app.showTabBar()` to show the tab bar:
|
|
177
|
+
|
|
178
|
+
```javascript
|
|
179
|
+
app.setWindowTitle('Home');
|
|
180
|
+
app.showTabBar(true);
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The window title will contain the page name, *and* your application name, taken from `app.name`. Showing the tab bar is typical for most web apps and pages, but there are exceptions. For example, a "login" page may not want to show the tab bar. See [Tabs](#tabs) below for more on this.
|
|
184
|
+
|
|
185
|
+
Your `onActivate()` method *must* return either `true` or `false`. Returning `true` means that the page accepted the activation, and the app can proceed. Returning `false` means that something went wrong (error or other), and the page should *not* be activated. In this case the page remains hidden and the *previous* page's DIV is still displayed.
|
|
186
|
+
|
|
187
|
+
### onDeactivate
|
|
188
|
+
|
|
189
|
+
The `onDeactivate()` method is called when your page is deactivated. Meaning, the user is navigating to another virtual page in the app. Your method is passed the ID of the new page being activated:
|
|
190
|
+
|
|
191
|
+
```javascript
|
|
192
|
+
onDeactivate: function(new_id) {
|
|
193
|
+
// called when page is deactivated
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
You can use this method to shut down or cleanup things happening in the page. For example: timers, IFRAMEs, or anything else that should be stopped. You may want to clear your entire DIV element (if your `onActivate()` redraws everything, for example).
|
|
199
|
+
|
|
200
|
+
Your `onDeactivate()` method *must* return either `true` or `false`. Returning `true` means that the page accepted the deactivation, and the app can proceed. Returning `false` means that something went wrong (error or other), and the page should *not* be deactivated. In this case the current page remains displayed.
|
|
201
|
+
|
|
202
|
+
### Accessing Pages
|
|
203
|
+
|
|
204
|
+
You can access any page by looking it up by its ID. This is done by calling the `page_manager` object in the `app` global. It provides a `find()` method that accepts an ID string. Example:
|
|
205
|
+
|
|
206
|
+
```javascript
|
|
207
|
+
var page = app.page_manager.find('Home');
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
There is a global shortcut for this, available by calling `$P()`. It also accepts a Page ID, but if omitted, it defaults to the current page. This is very useful for getting back into the context of the page from an inline HTML callback.
|
|
211
|
+
|
|
212
|
+
```javascript
|
|
213
|
+
var page = $P('Home');
|
|
214
|
+
var cur_page = $P();
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Navigation
|
|
218
|
+
|
|
219
|
+
The built-in navigation system listens for URL hash change events, and switches virtual pages based on the anchor tag present in the URL. Example:
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
http://myapp.com/#Home
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
This would activate the virtual page with ID `Home`. It also supports URL query string params after the page ID, which are passed to the page class `onActivate()` method. Example:
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
http://myapp.com/#Home?foo=bar&baz=1234
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
So a typical way of triggering a page change event is to simply redirect the browser to a new hash anchor tag. However, some convenience methods are also provided:
|
|
232
|
+
|
|
233
|
+
### Nav.go
|
|
234
|
+
|
|
235
|
+
This forces a page change event, and accepts a new anchor tag, optionally with a query string at the end.
|
|
236
|
+
|
|
237
|
+
```javascript
|
|
238
|
+
Nav.go('SomePage');
|
|
239
|
+
Nav.go('SomePage?foo=bar');
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Nav.refresh
|
|
243
|
+
|
|
244
|
+
This refreshes the current page (calls `onDeactivate()`, then `onActivate()`).
|
|
245
|
+
|
|
246
|
+
```javascript
|
|
247
|
+
Nav.refresh();
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Nav.prev
|
|
251
|
+
|
|
252
|
+
This jumps back to the previous page.
|
|
253
|
+
|
|
254
|
+
```javascript
|
|
255
|
+
Nav.prev();
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Nav.currentAnchor
|
|
259
|
+
|
|
260
|
+
This returns the name of the current anchor, including query string if present.
|
|
261
|
+
|
|
262
|
+
```javascript
|
|
263
|
+
var loc = Nav.currentAnchor();
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Tables
|
|
267
|
+
|
|
268
|
+
The library provides CSS styles and JavaScript functions for creating data tables, optionally with pagination. The HTML markup is simple; just use CSS class `data_table`, then provide your headers in `<TH>` elements, and your data in `<TD>` elements. HTML example of a simple table:
|
|
269
|
+
|
|
270
|
+
```html
|
|
271
|
+
<table class="data_table">
|
|
272
|
+
<tr>
|
|
273
|
+
<th>Username</th>
|
|
274
|
+
<th>Full Name</th>
|
|
275
|
+
<th>Status</th>
|
|
276
|
+
<th>Created</th>
|
|
277
|
+
<th>Modified</th>
|
|
278
|
+
</tr>
|
|
279
|
+
<tr>
|
|
280
|
+
<td>jhuckaby</td>
|
|
281
|
+
<td>Joseph Huckaby</td>
|
|
282
|
+
<td>Administrator</td>
|
|
283
|
+
<td>Jan 3, 2014</td>
|
|
284
|
+
<td>Oct 5, 2015</td>
|
|
285
|
+
</tr>
|
|
286
|
+
<tr>
|
|
287
|
+
<td>fsmith</td>
|
|
288
|
+
<td>Fred Smith</td>
|
|
289
|
+
<td>Standard User</td>
|
|
290
|
+
<td>Oct 5, 2015</td>
|
|
291
|
+
<td>Oct 5, 2015</td>
|
|
292
|
+
</tr>
|
|
293
|
+
</table>
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
In addition to the CSS, a pagination system is provided, to assist you with generating tables from a large dataset that have pagination links built-in. The function to call is `this.getPaginatedTable()` and is available in the `Page` base class. It returns the final rendered HTML for the page.
|
|
297
|
+
|
|
298
|
+
To use it, you'll need to provide an object containing the following pieces of information:
|
|
299
|
+
|
|
300
|
+
| Property Name | Description |
|
|
301
|
+
|---------------|-------------|
|
|
302
|
+
| `cols` | An array of header column labels, displayed in bold at the top of the table. |
|
|
303
|
+
| `rows` | The current page of data (array). Each element is passed to your callback for each visible row of the table. |
|
|
304
|
+
| `data_type` | A string identifying the type of data, e.g. `user`. Used in strings such as `No users found`. |
|
|
305
|
+
| `offset` | The current offset into the full dataset. This should be `0` for the first page. |
|
|
306
|
+
| `limit` | The number of items shown on each page. This should equal the length of the `rows` array. |
|
|
307
|
+
| `total` | The total number of items in the dataset. This is used to render proper pagination links. |
|
|
308
|
+
| `callback` | A user callback which is fired for each row, so you can provide your own `<TD>` elements. |
|
|
309
|
+
|
|
310
|
+
Here is an example:
|
|
311
|
+
|
|
312
|
+
```javascript
|
|
313
|
+
var cols = [
|
|
314
|
+
'Name', 'Color', 'Size', 'Quantity', 'Price', 'Created'
|
|
315
|
+
];
|
|
316
|
+
|
|
317
|
+
var rows = [
|
|
318
|
+
{ name: 'Celery', color: 'Green', size: '1ft', quantity: 450, price: '$2.75', created: 1442984544 },
|
|
319
|
+
{ name: 'Beets', color: 'Purple', size: '4in', quantity: 30, price: '$3.50', created: 1442380043 },
|
|
320
|
+
{ name: 'Lettuce', color: 'Green', size: '1ft', quantity: 1000, price: '$2.50', created: 1442264863 },
|
|
321
|
+
{ name: 'Carrots', color: 'Orange', size: '8in', quantity: 60, price: '$4.00', created: 1442084869 },
|
|
322
|
+
{ name: 'Rhubarb', color: 'Purple', size: '2ft', quantity: 190, price: '$3.99', created: 1441724876 }
|
|
323
|
+
];
|
|
324
|
+
|
|
325
|
+
var html = this.getPaginatedTable({
|
|
326
|
+
cols: cols,
|
|
327
|
+
rows: rows,
|
|
328
|
+
data_type: 'vegetable',
|
|
329
|
+
offset: 0,
|
|
330
|
+
limit: 5,
|
|
331
|
+
total: 10,
|
|
332
|
+
|
|
333
|
+
callback: function(row, idx) {
|
|
334
|
+
return [
|
|
335
|
+
row.name,
|
|
336
|
+
row.color,
|
|
337
|
+
row.size,
|
|
338
|
+
commify( row.quantity ),
|
|
339
|
+
row.price,
|
|
340
|
+
get_nice_date_time( row.created )
|
|
341
|
+
];
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
So the idea here is, we have a dataset of 10 items total, but we are only showing 5 items per page. So we have an array of 5 items in `rows`, but we're specifying the `total` as 10, and `offset` as 0 (first page). Based on this, the `getPaginatedTable()` will generate the proper pagination links.
|
|
347
|
+
|
|
348
|
+
Your callback is fired once per row, and is passed the current row (array element from `rows`), and the localized index in `idx` (starts from `0` regardless of `offset`). Your function should return an array of values which should match up with the `cols`, and each will be stuffed into a `<TD>` element.
|
|
349
|
+
|
|
350
|
+
The pagination links work by constructing self-referencing URL to the current page, but adding or modifying an `offset` query parameter, set to the appropriate value. For example, in this case there would be a `Next Page` link, which would be set to:
|
|
351
|
+
|
|
352
|
+
```
|
|
353
|
+
http://myapp.com/#Home?foo=bar&baz=1234&offset=5
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Since the `limit` is set to 5 items per page, and `offset` starts at `0`, then the next page (page 2) will be at offset `5`. This link is simply a hashtag anchor tag, which doesn't reload the browser page, but will instead be caught by the navigation system, and call your page's `onDeactivate()` then its `onActivate()` with the new values. It is up to your page code to redraw the table with the new data chunk and new `offset` value.
|
|
357
|
+
|
|
358
|
+
Instead of generating hashtag anchor links, you can optionally provide a custom JavaScript function in a `pagination_link` property, which will be written into the HTML as an `onMouseUp` handler on each link, and called instead of a standard link. Note that it must be a string and globally accessible, so remember the `$P()` shortcut to get access to the current page. Example:
|
|
359
|
+
|
|
360
|
+
```javascript
|
|
361
|
+
pagination_link: '$P().tableNavClick'
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
In this case your custom page `tableNavClick()` method will be called for each table pagination click, and passed the new offset value.
|
|
365
|
+
|
|
366
|
+
## Notification
|
|
367
|
+
|
|
368
|
+
Notification messages are shown in a fixed bar at the top of the screen, regardless of the scroll position. Messages can have one of three styles (highlight color), and custom HTML. They can remain in place until clicked, or disappear after N seconds. Only one notification may be shown at a time.
|
|
369
|
+
|
|
370
|
+
To use the notification system in your app, make sure this markup is in your main HTML page:
|
|
371
|
+
|
|
372
|
+
```html
|
|
373
|
+
<div id="d_message" class="message" style="display:none" onMouseUp="app.hideMessage(250)">
|
|
374
|
+
<div id="d_message_inner" class="message_inner"></div>
|
|
375
|
+
</div>
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Then, call `app.showMessage()` and pass in a style name (see below), a text or HTML message string, and optionally a lifetime (number of seconds before it auto-hides). Here are the three supported message styles:
|
|
379
|
+
|
|
380
|
+
| Style | Description |
|
|
381
|
+
|-------|-------------|
|
|
382
|
+
| `success` | Highlighted in green, used for successful completion messages. By default, these automatically hide after 8 seconds. |
|
|
383
|
+
| `warning` | Highlighted in yellow, used for warning messages. By default these are persistent until user click. |
|
|
384
|
+
| `error` | Highlighted in red, used for error messages. By default these are persistent until user click. |
|
|
385
|
+
|
|
386
|
+
Example use:
|
|
387
|
+
|
|
388
|
+
```javascript
|
|
389
|
+
app.showMessage( 'success', "The user was saved successfully.", 8 );
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
To programmatically hide the notification message, call `app.hideMessage()`. You can optionally pass in a number of milliseconds to animate the hide, if you want (uses jQuery's animation system).
|
|
393
|
+
|
|
394
|
+
For form field validation errors, you can call `app.badField()` and pass in the DOM ID (or CSS selector) of the form field containing an invalid value, and an error message. The form field will be focused, highlighted in red (background color, works well for text fields), and an error message notification will be displayed. To clear an error, call `app.clearError()`.
|
|
395
|
+
|
|
396
|
+
```javascript
|
|
397
|
+
app.badField( '#my_username', "Usernames must contain alphanumeric characters only." );
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
If you include [Font Awesome Icons](https://fortawesome.github.io/Font-Awesome/icons/) in your HTML page, the notification messages will also contain an appropriate icon matching the style:
|
|
401
|
+
|
|
402
|
+
```html
|
|
403
|
+
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
## API
|
|
407
|
+
|
|
408
|
+
The library contains a simple JSON REST API wrapper built around jQuery's [$.ajax()](http://api.jquery.com/jquery.ajax/) call, designed to support JSON API backends. API calls can be sent to the server using HTTP GET or POST, and JSON responses are parsed for you. Errors are handled automatically, but you can specify custom handlers as well.
|
|
409
|
+
|
|
410
|
+
By default, API calls are sent to the same hostname as the one hosting the page, using the URI `/api/COMMAND`, where `COMMAND` is a custom command passed in, e.g. `/api/user_login`. You can change the base API URL by calling `app.setAPIBaseURL()`. Example:
|
|
411
|
+
|
|
412
|
+
```javascript
|
|
413
|
+
app.setAPIBaseURL( '/myapp/API.php' );
|
|
414
|
+
app.setAPIBaseURL( 'http://myotherserver.com/myapp/API.php' );
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
To send an API call, use `app.api.get()` or `app.api.post()` depending on whether you want an HTTP GET or HTTP POST. Pass in a command name (is appended to the base URI), a params object (serialized to JSON or a query string), and a callback. Example:
|
|
418
|
+
|
|
419
|
+
```javascript
|
|
420
|
+
app.api.post( 'user_login', { username: 'joe', password: '12345' }, function(resp) {
|
|
421
|
+
// successfully logged user in
|
|
422
|
+
// 'resp' is response JSON from server
|
|
423
|
+
} );
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
So this example would send an HTTP POST to `/api/user_login`, and serialize the params into JSON, sent as the body of the post. The response is expected to be in JSON, and is parsed and sent to the callback.
|
|
427
|
+
|
|
428
|
+
Sending an HTTP GET is similar. Just call `app.api.get()` instead, and note that the params object is serialized into a URL query string, not a JSON POST body. The response and callback are handled the same. Example:
|
|
429
|
+
|
|
430
|
+
```javascript
|
|
431
|
+
app.api.get( 'user_get_info', { username: 'joe' }, function(resp) {
|
|
432
|
+
// successfully fetched user info
|
|
433
|
+
// 'resp' is response JSON from server
|
|
434
|
+
} );
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
API errors are handled automatically by default, meaning your callback is *not* fired, and instead an error notification is displayed. This includes HTTP related errors, as well as errors specified inside the response JSON. The API system expects the response to include a `code` property, and if this is non-zero, it is considered an error, and it looks for a `description` property for the error message.
|
|
438
|
+
|
|
439
|
+
To set a custom error handler, specify a second callback after the first one:
|
|
440
|
+
|
|
441
|
+
```javascript
|
|
442
|
+
app.api.post( 'user_login', { username: 'joe', password: '12345' },
|
|
443
|
+
function(resp) {
|
|
444
|
+
// successfully logged user in
|
|
445
|
+
// 'resp' is response JSON from server
|
|
446
|
+
},
|
|
447
|
+
function(err) {
|
|
448
|
+
// an error occurred
|
|
449
|
+
// see err.code and err.description
|
|
450
|
+
}
|
|
451
|
+
);
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
## Misc
|
|
455
|
+
|
|
456
|
+
### Page Resize
|
|
457
|
+
|
|
458
|
+
If your pages need to take special action when the browser is resized, you can define an `onResize()` method in your page classes. This is fired for every browser resize event, and your method is passed an object containing the new inner window `width` and `height` in pixels. Example:
|
|
459
|
+
|
|
460
|
+
```javascript
|
|
461
|
+
onResize: function(size) {
|
|
462
|
+
// window was resized
|
|
463
|
+
// see 'size.width' and 'size.height'
|
|
464
|
+
}
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Page Unload
|
|
468
|
+
|
|
469
|
+
If you need to intercept the user navigating away from the app entirely or closing the browser tab/window, you can define an `onBeforeUnload()` method in your page classes. This method should return a text message to be displayed, if you want to intercept the event and alert the user, or return `false` to allow the app to shut down without any intervention. Example:
|
|
470
|
+
|
|
471
|
+
```javascript
|
|
472
|
+
onBeforeUnload: function() {
|
|
473
|
+
// if dirty, warn user before navigating away from app
|
|
474
|
+
if (this.dirty) return "There are unsaved changes in this document. If you leave now they will be abandoned.";
|
|
475
|
+
else return false;
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
This example assumes your page class has a `dirty` property, which is `true` when the user has made changes which are unsaved.
|
|
480
|
+
|
|
481
|
+
Please note that alerting the user in this way is very jarring and disruptive, and should *only* be done when there really is a good reason to keep the user on the page, i.e. unsaved changes that will be lost forever.
|
|
482
|
+
|
|
483
|
+
# License
|
|
484
|
+
|
|
485
|
+
See `LICENSE.md` in this repository.
|