├── .gitignore ├── AUTHORS ├── mixin.js ├── mixOwn.js ├── getProp.js ├── .travis.yml ├── delay.js ├── setProp.js ├── tests ├── puppeteer-chrome.js ├── puppeteer-firefox.js ├── tests.html ├── phantom.js ├── server.js └── tests.js ├── debounce.js ├── clone.js ├── throttle.js ├── mixDeep.js ├── package.json ├── README.md ├── sprintf.js └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | *.iml 3 | .idea 4 | *.sublime-* 5 | report/* 6 | coverage/* 7 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Eugene Lazutkin (http://lazutkin.com/) 2 | Max Motovilov 3 | -------------------------------------------------------------------------------- /mixin.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | ([], function(){ 3 | "use strict"; 4 | 5 | //TODO: consider defineProperty() & Co. on capable JS engines. 6 | 7 | return function mixin(t, s){ 8 | for(var k in s){ 9 | t[k] = s[k]; 10 | } 11 | return t; 12 | }; 13 | }); 14 | -------------------------------------------------------------------------------- /mixOwn.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | ([], function(){ 3 | "use strict"; 4 | 5 | //TODO: consider defineProperty() & Co. on capable JS engines. 6 | 7 | return function mixOwn(t, s){ 8 | for(var k in s){ 9 | if(s.hasOwnProperty(k)){ 10 | t[k] = s[k]; 11 | } 12 | } 13 | return t; 14 | }; 15 | }); 16 | -------------------------------------------------------------------------------- /getProp.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | ([], function(){ 3 | "use strict"; 4 | 5 | return function getProp(o, name){ 6 | if(typeof name == "string"){ 7 | name = name.split(/[^\w]+/); 8 | } 9 | for(var i = 0; i < name.length; ++i){ 10 | if(!o){ return; } 11 | o = o[name[i]]; 12 | } 13 | return o; 14 | }; 15 | }); 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | addon: 4 | chrome: stable 5 | 6 | language: node_js 7 | 8 | node_js: 9 | - "8" 10 | - "10" 11 | - "12" 12 | - "13" 13 | 14 | env: 15 | - BROWSER=true 16 | - BROWSER=false 17 | 18 | matrix: 19 | exclude: 20 | - node_js: "8" 21 | env: BROWSER=true 22 | - node_js: "10" 23 | env: BROWSER=true 24 | - node_js: "12" 25 | env: BROWSER=true 26 | 27 | before_script: 28 | - npm start & 29 | - sleep 5 30 | - if [ "$BROWSER" == "true" ]; then export RUN_TEST="npm run test-browser"; else export RUN_TEST="npm test"; fi 31 | 32 | script: $RUN_TEST 33 | -------------------------------------------------------------------------------- /delay.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | ([], function(){ 3 | "use strict"; 4 | 5 | return function delay(f, ms){ 6 | return function(){ 7 | var self = this, args = arguments, 8 | handle = setTimeout(function(){ 9 | handle = null; 10 | f.apply(self, args); 11 | }, ms); 12 | return { 13 | isWaiting: function(){ return !!handle; }, 14 | destroy: function(){ 15 | if(handle){ 16 | clearTimeout(handle); 17 | handle = null; 18 | } 19 | } 20 | }; 21 | }; 22 | }; 23 | }); 24 | -------------------------------------------------------------------------------- /setProp.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | ([], function(){ 3 | "use strict"; 4 | 5 | return function setProp(o, name, value, strict){ 6 | if(typeof name == "string"){ 7 | name = name.split(/[^\w]+/); 8 | } 9 | var last = name.pop(); 10 | for(var i = 0; i < name.length; ++i){ 11 | var key = name[i]; 12 | if(key in o){ 13 | o = o[key]; 14 | }else{ 15 | if(strict){ 16 | throw new Error("setProp: wrong path"); 17 | } 18 | o = o[key] = {}; 19 | } 20 | } 21 | o[last] = value; 22 | }; 23 | }); 24 | -------------------------------------------------------------------------------- /tests/puppeteer-chrome.js: -------------------------------------------------------------------------------- 1 | const puppeteer = require('puppeteer'); 2 | 3 | 4 | (async () => { 5 | const browser = await puppeteer.launch(); 6 | const page = await browser.newPage(); 7 | 8 | page.on('console', msg => console[typeof console[msg.type()] == 'function' ? msg.type() : 'log'](msg.text())); 9 | page.on('error', e => console.error(e)); 10 | 11 | await page.exposeFunction('callPhantom', async text => { 12 | await browser.close(); 13 | switch (text) { 14 | case "success": 15 | process.exit(0); 16 | break; 17 | case "failure": 18 | process.exit(1); 19 | break; 20 | } 21 | }); 22 | 23 | await page.goto('http://localhost:3000/tests/tests.html'); 24 | })(); 25 | -------------------------------------------------------------------------------- /tests/puppeteer-firefox.js: -------------------------------------------------------------------------------- 1 | const puppeteer = require('puppeteer-firefox'); 2 | 3 | 4 | (async () => { 5 | const browser = await puppeteer.launch(); 6 | const page = await browser.newPage(); 7 | 8 | page.on('console', msg => console[typeof console[msg.type()] == 'function' ? msg.type() : 'log'](msg.text())); 9 | page.on('error', e => console.error(e)); 10 | 11 | await page.exposeFunction('callPhantom', async text => { 12 | await browser.close(); 13 | switch (text) { 14 | case "success": 15 | process.exit(0); 16 | break; 17 | case "failure": 18 | process.exit(1); 19 | break; 20 | } 21 | }); 22 | 23 | await page.goto('http://localhost:3000/tests/tests.html'); 24 | })(); 25 | -------------------------------------------------------------------------------- /debounce.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | ([], function(){ 3 | "use strict"; 4 | 5 | return function debounce(f, ms){ 6 | var handle; 7 | return function(){ 8 | if(handle){ 9 | clearTimeout(handle); 10 | } 11 | var self = this, args = arguments; 12 | handle = setTimeout(function(){ 13 | handle = null; 14 | f.apply(self, args); 15 | }, ms); 16 | return { 17 | isWaiting: function(){ return !!handle; }, 18 | destroy: function(){ 19 | if(handle){ 20 | clearTimeout(handle); 21 | handle = null; 22 | } 23 | } 24 | }; 25 | }; 26 | }; 27 | }); 28 | -------------------------------------------------------------------------------- /clone.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | ([], function(){ 3 | "use strict"; 4 | 5 | //TODO: consider defineProperty() & Co. on capable JS engines. 6 | 7 | return function clone(s){ 8 | if(!s || typeof s != "object" || typeof s == "function"){ 9 | return s; 10 | } 11 | if(s instanceof Date){ 12 | return new Date(s.getTime()); 13 | } 14 | if(s instanceof RegExp){ 15 | return new RegExp(s.source, 16 | (s.global ? "g" : "") + 17 | (s.multiline ? "m" : "") + 18 | (s.ignoreCase ? "i" : "") 19 | ); 20 | } 21 | var t = s instanceof Array ? [] : {}; 22 | for(var i in s){ 23 | t[i] = clone(s[i]); 24 | } 25 | return t; 26 | }; 27 | }); 28 | -------------------------------------------------------------------------------- /tests/tests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | heya-utils test runner 5 | 6 | 7 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /throttle.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | ([], function(){ 3 | "use strict"; 4 | 5 | return function throttle(f, threshold){ 6 | var handle, last; 7 | return function(){ 8 | var now = new Date().getTime(); 9 | if(last && now < last + threshold){ 10 | if(handle){ 11 | clearTimeout(handle); 12 | } 13 | var self = this, args = arguments; 14 | handle = setTimeout(function(){ 15 | handle = null; 16 | last = new Date().getTime(); 17 | f.apply(self, args); 18 | }, last + threshold - now); 19 | }else{ 20 | last = now; 21 | f.apply(this, arguments); 22 | } 23 | return { 24 | isWaiting: function(){ return !!handle; }, 25 | destroy: function(){ 26 | if(handle){ 27 | clearTimeout(handle); 28 | handle = null; 29 | } 30 | } 31 | }; 32 | }; 33 | }; 34 | }); 35 | -------------------------------------------------------------------------------- /mixDeep.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | ([], function(){ 3 | "use strict"; 4 | 5 | //TODO: consider defineProperty() & Co. on capable JS engines. 6 | 7 | function identity(x){ return x; } 8 | 9 | return function mixDeep(t, s, dontCreate, cloneFn){ 10 | cloneFn = cloneFn || identity; 11 | main: { 12 | if(!s || typeof s != "object" || s instanceof Date || s instanceof RegExp){ 13 | return cloneFn(s); 14 | } 15 | if(s instanceof Array){ 16 | if(!(t instanceof Array)){ 17 | return cloneFn(s); 18 | } 19 | } 20 | // copy members 21 | for(var k in s){ 22 | if(k in t){ 23 | t[k] = mixDeep(t[k], s[k], dontCreate, cloneFn); 24 | }else if(!dontCreate){ 25 | t[k] = cloneFn(s[k]); 26 | } 27 | } 28 | return t; 29 | } 30 | throw new Error("mixDeep: Structural mismatch"); 31 | }; 32 | }); 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "heya-utils", 3 | "version": "0.1.4", 4 | "description": "General purpose utilities, frequently used functions.", 5 | "directories": { 6 | "test": "tests" 7 | }, 8 | "dependencies": {}, 9 | "devDependencies": { 10 | "body-parser": "^1.19.0", 11 | "express": "^4.17.1", 12 | "heya-unit": "^0.3.0", 13 | "puppeteer": "^2.0.0", 14 | "puppeteer-firefox": "^0.5.0" 15 | }, 16 | "scripts": { 17 | "start": "node tests/server.js", 18 | "test": "node tests/tests.js", 19 | "test-chrome": "node tests/puppeteer-chrome.js", 20 | "test-firefox": "node tests/puppeteer-firefox.js", 21 | "test-browser": "npm run test-chrome && npm run test-firefox" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "git://github.com/heya/utils.git" 26 | }, 27 | "keywords": [ 28 | "general", 29 | "general purpose", 30 | "utilities" 31 | ], 32 | "author": "Eugene Lazutkin (http://lazutkin.com/)", 33 | "license": "BSD" 34 | } 35 | -------------------------------------------------------------------------------- /tests/phantom.js: -------------------------------------------------------------------------------- 1 | phantom.onError = function(msg, trace){ 2 | var msgStack = ["PHANTOM ERROR: " + msg]; 3 | if(trace){ 4 | msgStack.push("TRACE:"); 5 | trace.forEach(function(t){ 6 | msgStack.push(" -> " + (t.file || t.sourceURL) + ": " + t.line + 7 | (t.function ? " (in function " + t.function + ")" : "")); 8 | }); 9 | } 10 | console.error(msgStack.join('\n')); 11 | phantom.exit(1); 12 | }; 13 | 14 | var page = require("webpage").create(); 15 | 16 | page.onError = function(msg){ 17 | console.error("ERROR: " + msg); 18 | phantom.exit(1); 19 | }; 20 | 21 | page.onAlert = function(msg){ 22 | console.log("ALERT: " + msg); 23 | }; 24 | page.onConsoleMessage = function(msg){ 25 | console.log(msg); 26 | }; 27 | page.onCallback = function(msg){ 28 | switch(msg){ 29 | case "success": 30 | phantom.exit(0); 31 | break; 32 | case "failure": 33 | phantom.exit(1); 34 | break; 35 | } 36 | } 37 | 38 | var scriptPath = require("system").args[0], 39 | path = require("fs").absolute( 40 | (scriptPath.length && scriptPath.charAt(0) == "/" ? "" : "./") + scriptPath).split("/"); 41 | 42 | path.pop(); 43 | path.push("tests.html"); 44 | 45 | page.open(path.join("/"), function(status){ 46 | if(status !== "success"){ 47 | console.error("ERROR: Can't load a web page."); 48 | phantom.exit(1); 49 | } 50 | }); 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # heya-utils 2 | 3 | [![Build status][travis-image]][travis-url] 4 | [![NPM version][npm-image]][npm-url] 5 | 6 | [![Greenkeeper][greenkeeper-image]][greenkeeper-url] 7 | [![Dependencies][deps-image]][deps-url] 8 | [![devDependencies][dev-deps-image]][dev-deps-url] 9 | 10 | General purpose utilitites, frequently used functions. 11 | 12 | ## How to install 13 | 14 | If you plan to use it in your [node.js](http://nodejs.org) project install it 15 | like this: 16 | 17 | ``` 18 | npm install heya-utils 19 | ``` 20 | 21 | For your browser-based projects I suggest to use [volo.js](http://volojs.org): 22 | 23 | ``` 24 | volo install heya/utils heya-utils 25 | ``` 26 | 27 | ## Versions 28 | 29 | - 0.1.4 *Refreshed dev deps.* 30 | 31 | [npm-image]: https://img.shields.io/npm/v/heya-utils.svg 32 | [npm-url]: https://npmjs.org/package/heya-utils 33 | [deps-image]: https://img.shields.io/david/heya/utils.svg 34 | [deps-url]: https://david-dm.org/heya/utils 35 | [dev-deps-image]: https://img.shields.io/david/dev/heya/utils.svg 36 | [dev-deps-url]: https://david-dm.org/heya/utils#info=devDependencies 37 | [travis-image]: https://img.shields.io/travis/heya/utils.svg 38 | [travis-url]: https://travis-ci.org/heya/utils 39 | [greenkeeper-image]: https://badges.greenkeeper.io/heya/utils.svg 40 | [greenkeeper-url]: https://greenkeeper.io/ 41 | -------------------------------------------------------------------------------- /sprintf.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | ([], function(){ 3 | "use strict"; 4 | 5 | function identity(value){ return value; } 6 | 7 | function pad(init){ 8 | var filler = init; 9 | return function(value, length, right){ 10 | var len = length - value.length; 11 | while(filler.length < len){ filler += filler; } 12 | var padding = filler.substr(0, len); 13 | return right ? value + padding : padding + value; 14 | }; 15 | } 16 | var padZero = pad("00000000"), padSpace = pad(" "); 17 | 18 | // alternative prefix 19 | var a = {o: "0", O: "0", x: "0x", X: "0X", b: "b", B: "B"}; 20 | 21 | function integer(radix){ 22 | return function(value, precision, flags, format){ 23 | var result = Math.round(value).toString(radix), first = result.charAt(0), isNum = "0" <= first && first <= "9"; 24 | if(isNum){ 25 | if(flags.indexOf("+") >= 0){ 26 | result = "+" + result; first = "+"; isNum = false; 27 | }else if(flags.indexOf(" ") >= 0){ 28 | result = " " + result; first = " "; isNum = false; 29 | } 30 | } 31 | if(!isNaN(precision) && result.length < precision){ 32 | result = isNum ? padZero(result, precision) : first + padZero(result.substr(1), precision - 1); 33 | } 34 | if(flags.indexOf("#") >= 0 && result !== "0"){ 35 | result = isNum ? (a[format] || "") + result : first + (a[format] || "") + result.substr(1); 36 | } 37 | return result; 38 | }; 39 | } 40 | 41 | var c = { // conversion routines 42 | d: integer(10), 43 | b: integer(2), 44 | o: integer(8), 45 | x: integer(16), 46 | f: function fixed(value, precision){ return isNaN(precision) ? value.toFixed() : value.toFixed(precision); }, 47 | e: function scientific(value, precision){ return isNaN(precision) ? value.toExponential() : value.toExponential(precision); }, 48 | g: function automatic(value, precision){ return isNaN(precision) ? value.toPrecision() : value.toPrecision(precision); }, 49 | j: function json(value){ return JSON.stringify(value); }, 50 | s: function string(value, precision){ return isNaN(precision) ? value.toString() : value.toString().slice(0, precision); }, 51 | "%": function percent(){ return "%"; } 52 | }; 53 | c.i = c.u = c.d; c.B = c.b; c.X = c.x; c.E = c.e; c.G = c.g; c.S = c.t = c.s; 54 | 55 | var p = { // post-processing 56 | S: function upper(value){ return value.toUpperCase(); }, 57 | t: function lower(value){ return value.toLowerCase(); } 58 | }; 59 | p.E = p.G = p.X = p.S; 60 | 61 | return function sprintf(fmt){ 62 | var args = arguments, argc = 1; 63 | return fmt.replace(/%([\-\+\ #]*)(\d*)(\.\d+|)([diobBuxXeEfgGjsSt%])/g, 64 | function(match, flags, width, precision, format){ 65 | var result = (p[format] || identity)(c[format](args[argc], parseInt(precision.substr(1), 10), flags, format)), 66 | w = parseInt(width, 10); 67 | if(!isNaN(w) && result.length < w){ 68 | result = padSpace(result, w, flags.indexOf("-") >= 0); 69 | } 70 | if(format != "%"){ ++argc; } 71 | return result; 72 | }); 73 | }; 74 | }); 75 | -------------------------------------------------------------------------------- /tests/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var http = require('http'); 4 | var path = require('path'); 5 | var debug = require('debug')('heya-io:server'); 6 | var express = require('express'); 7 | var bodyParser = require('body-parser'); 8 | 9 | // The APP 10 | 11 | var app = express(); 12 | 13 | app.use(bodyParser.raw({type: '*/*'})); 14 | 15 | var counter = 0; 16 | 17 | app.all('/api*', function (req, res) { 18 | if (req.query.status) { 19 | var status = parseInt(req.query.status, 10); 20 | if (isNaN(status) || status < 100 || status >= 600) { 21 | status = 200; 22 | } 23 | res.status(status); 24 | } 25 | switch (req.query.payloadType) { 26 | case 'txt': 27 | res.set('Content-Type', 'text/plain'); 28 | res.send('Hello, world!'); 29 | return; 30 | case 'xml': 31 | res.set('Content-Type', 'application/xml'); 32 | res.send('
Hello, world!
'); 33 | return; 34 | } 35 | var data = { 36 | method: req.method, 37 | protocol: req.protocol, 38 | hostname: req.hostname, 39 | url: req.url, 40 | originalUrl: req.originalUrl, 41 | headers: req.headers, 42 | body: req.body && req.body.length && req.body.toString() || null, 43 | query: req.query, 44 | now: Date.now(), 45 | counter: counter++ 46 | }; 47 | var timeout = 0; 48 | if (req.query.timeout) { 49 | var timeout = parseInt(req.query.timeout, 10); 50 | if (isNaN(timeout) || timeout < 0 || timeout > 60000) { 51 | timeout = 0; 52 | } 53 | } 54 | if (timeout) { 55 | setTimeout(function () { 56 | res.jsonp(data); 57 | }, timeout); 58 | } else { 59 | res.jsonp(data); 60 | } 61 | }); 62 | 63 | app.use(express.static(path.join(__dirname, '..'))); 64 | 65 | // catch 404 and forward to error handler 66 | app.use(function(req, res, next) { 67 | var err = new Error('Not Found'); 68 | err.status = 404; 69 | next(err); 70 | }); 71 | 72 | // error handlers 73 | 74 | app.use(function(err, req, res, next) { 75 | // for simplicity we don't use fancy HTML formatting opting for a plain text 76 | res.status(err.status || 500); 77 | res.set('Content-Type', 'text/plain'); 78 | res.send('Error (' + err.status + '): ' + err.message + '\n' + err.stack); 79 | debug('Error: ' + err.message + ' (' + err.status + ')'); 80 | }); 81 | 82 | // The SERVER 83 | 84 | /** 85 | * Get port from environment and store in Express. 86 | */ 87 | 88 | var host = process.env.HOST || 'localhost', 89 | port = normalizePort(process.env.PORT || '3000'); 90 | app.set('port', port); 91 | 92 | /** 93 | * Create HTTP server. 94 | */ 95 | 96 | var server = http.createServer(app); 97 | 98 | /** 99 | * Listen on provided port, on provided host, or all network interfaces. 100 | */ 101 | 102 | server.listen(port, host); 103 | server.on('error', onError); 104 | server.on('listening', onListening); 105 | 106 | /** 107 | * Normalize a port into a number, string, or false. 108 | */ 109 | 110 | function normalizePort(val) { 111 | var port = parseInt(val, 10); 112 | 113 | if (isNaN(port)) { 114 | // named pipe 115 | return val; 116 | } 117 | 118 | if (port >= 0) { 119 | // port number 120 | return port; 121 | } 122 | 123 | return false; 124 | } 125 | 126 | /** 127 | * Human-readable port description. 128 | */ 129 | 130 | function portToString (port) { 131 | return typeof port === 'string' ? 'pipe ' + port : 'port ' + port; 132 | } 133 | 134 | 135 | /** 136 | * Event listener for HTTP server "error" event. 137 | */ 138 | 139 | function onError(error) { 140 | if (error.syscall !== 'listen') { 141 | throw error; 142 | } 143 | 144 | var bind = portToString(port); 145 | 146 | // handle specific listen errors with friendly messages 147 | switch (error.code) { 148 | case 'EACCES': 149 | console.error('Error: ' + bind + ' requires elevated privileges'); 150 | process.exit(1); 151 | break; 152 | case 'EADDRINUSE': 153 | console.error('Error: ' + bind + ' is already in use'); 154 | process.exit(1); 155 | break; 156 | default: 157 | throw error; 158 | } 159 | } 160 | 161 | /** 162 | * Event listener for HTTP server "listening" event. 163 | */ 164 | 165 | function onListening() { 166 | //var addr = server.address(); 167 | var bind = portToString(port); 168 | debug('Listening on ' + (host || 'all network interfaces') + ' ' + bind); 169 | } 170 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This library is available under *either* the terms of the modified BSD license 2 | *or* the Academic Free License version 2.1. As a recipient of this work, you 3 | may choose which license to receive this code under. No external contributions 4 | are allowed under licenses which are fundamentally incompatible with the AFL 5 | or BSD licenses that this library is distributed under. 6 | 7 | The text of the AFL and BSD licenses is reproduced below. 8 | 9 | ------------------------------------------------------------------------------- 10 | The "New" BSD License: 11 | ********************** 12 | 13 | Copyright (c) 2005-2012, Eugene Lazutkin 14 | All rights reserved. 15 | 16 | Redistribution and use in source and binary forms, with or without 17 | modification, are permitted provided that the following conditions are met: 18 | 19 | * Redistributions of source code must retain the above copyright notice, this 20 | list of conditions and the following disclaimer. 21 | * Redistributions in binary form must reproduce the above copyright notice, 22 | this list of conditions and the following disclaimer in the documentation 23 | and/or other materials provided with the distribution. 24 | * Neither the name of Eugene Lazutkin nor the names of other contributors 25 | may be used to endorse or promote products derived from this software 26 | without specific prior written permission. 27 | 28 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 29 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 30 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 31 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 32 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 34 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 35 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 36 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 37 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 38 | 39 | ------------------------------------------------------------------------------- 40 | The Academic Free License, v. 2.1: 41 | ********************************** 42 | 43 | This Academic Free License (the "License") applies to any original work of 44 | authorship (the "Original Work") whose owner (the "Licensor") has placed the 45 | following notice immediately following the copyright notice for the Original 46 | Work: 47 | 48 | Licensed under the Academic Free License version 2.1 49 | 50 | 1) Grant of Copyright License. Licensor hereby grants You a world-wide, 51 | royalty-free, non-exclusive, perpetual, sublicenseable license to do the 52 | following: 53 | 54 | a) to reproduce the Original Work in copies; 55 | 56 | b) to prepare derivative works ("Derivative Works") based upon the Original 57 | Work; 58 | 59 | c) to distribute copies of the Original Work and Derivative Works to the 60 | public; 61 | 62 | d) to perform the Original Work publicly; and 63 | 64 | e) to display the Original Work publicly. 65 | 66 | 2) Grant of Patent License. Licensor hereby grants You a world-wide, 67 | royalty-free, non-exclusive, perpetual, sublicenseable license, under patent 68 | claims owned or controlled by the Licensor that are embodied in the Original 69 | Work as furnished by the Licensor, to make, use, sell and offer for sale the 70 | Original Work and Derivative Works. 71 | 72 | 3) Grant of Source Code License. The term "Source Code" means the preferred 73 | form of the Original Work for making modifications to it and all available 74 | documentation describing how to modify the Original Work. Licensor hereby 75 | agrees to provide a machine-readable copy of the Source Code of the Original 76 | Work along with each copy of the Original Work that Licensor distributes. 77 | Licensor reserves the right to satisfy this obligation by placing a 78 | machine-readable copy of the Source Code in an information repository 79 | reasonably calculated to permit inexpensive and convenient access by You for as 80 | long as Licensor continues to distribute the Original Work, and by publishing 81 | the address of that information repository in a notice immediately following 82 | the copyright notice that applies to the Original Work. 83 | 84 | 4) Exclusions From License Grant. Neither the names of Licensor, nor the names 85 | of any contributors to the Original Work, nor any of their trademarks or 86 | service marks, may be used to endorse or promote products derived from this 87 | Original Work without express prior written permission of the Licensor. Nothing 88 | in this License shall be deemed to grant any rights to trademarks, copyrights, 89 | patents, trade secrets or any other intellectual property of Licensor except as 90 | expressly stated herein. No patent license is granted to make, use, sell or 91 | offer to sell embodiments of any patent claims other than the licensed claims 92 | defined in Section 2. No right is granted to the trademarks of Licensor even if 93 | such marks are included in the Original Work. Nothing in this License shall be 94 | interpreted to prohibit Licensor from licensing under different terms from this 95 | License any Original Work that Licensor otherwise would have a right to 96 | license. 97 | 98 | 5) This section intentionally omitted. 99 | 100 | 6) Attribution Rights. You must retain, in the Source Code of any Derivative 101 | Works that You create, all copyright, patent or trademark notices from the 102 | Source Code of the Original Work, as well as any notices of licensing and any 103 | descriptive text identified therein as an "Attribution Notice." You must cause 104 | the Source Code for any Derivative Works that You create to carry a prominent 105 | Attribution Notice reasonably calculated to inform recipients that You have 106 | modified the Original Work. 107 | 108 | 7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that 109 | the copyright in and to the Original Work and the patent rights granted herein 110 | by Licensor are owned by the Licensor or are sublicensed to You under the terms 111 | of this License with the permission of the contributor(s) of those copyrights 112 | and patent rights. Except as expressly stated in the immediately proceeding 113 | sentence, the Original Work is provided under this License on an "AS IS" BASIS 114 | and WITHOUT WARRANTY, either express or implied, including, without limitation, 115 | the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR 116 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. 117 | This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No 118 | license to Original Work is granted hereunder except under this disclaimer. 119 | 120 | 8) Limitation of Liability. Under no circumstances and under no legal theory, 121 | whether in tort (including negligence), contract, or otherwise, shall the 122 | Licensor be liable to any person for any direct, indirect, special, incidental, 123 | or consequential damages of any character arising as a result of this License 124 | or the use of the Original Work including, without limitation, damages for loss 125 | of goodwill, work stoppage, computer failure or malfunction, or any and all 126 | other commercial damages or losses. This limitation of liability shall not 127 | apply to liability for death or personal injury resulting from Licensor's 128 | negligence to the extent applicable law prohibits such limitation. Some 129 | jurisdictions do not allow the exclusion or limitation of incidental or 130 | consequential damages, so this exclusion and limitation may not apply to You. 131 | 132 | 9) Acceptance and Termination. If You distribute copies of the Original Work or 133 | a Derivative Work, You must make a reasonable effort under the circumstances to 134 | obtain the express assent of recipients to the terms of this License. Nothing 135 | else but this License (or another written agreement between Licensor and You) 136 | grants You permission to create Derivative Works based upon the Original Work 137 | or to exercise any of the rights granted in Section 1 herein, and any attempt 138 | to do so except under the terms of this License (or another written agreement 139 | between Licensor and You) is expressly prohibited by U.S. copyright law, the 140 | equivalent laws of other countries, and by international treaty. Therefore, by 141 | exercising any of the rights granted to You in Section 1 herein, You indicate 142 | Your acceptance of this License and all of its terms and conditions. 143 | 144 | 10) Termination for Patent Action. This License shall terminate automatically 145 | and You may no longer exercise any of the rights granted to You by this License 146 | as of the date You commence an action, including a cross-claim or counterclaim, 147 | against Licensor or any licensee alleging that the Original Work infringes a 148 | patent. This termination provision shall not apply for an action alleging 149 | patent infringement by combinations of the Original Work with other software or 150 | hardware. 151 | 152 | 11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this 153 | License may be brought only in the courts of a jurisdiction wherein the 154 | Licensor resides or in which Licensor conducts its primary business, and under 155 | the laws of that jurisdiction excluding its conflict-of-law provisions. The 156 | application of the United Nations Convention on Contracts for the International 157 | Sale of Goods is expressly excluded. Any use of the Original Work outside the 158 | scope of this License or after its termination shall be subject to the 159 | requirements and penalties of the U.S. Copyright Act, 17 U.S.C. § 101 et 160 | seq., the equivalent laws of other countries, and international treaty. This 161 | section shall survive the termination of this License. 162 | 163 | 12) Attorneys Fees. In any action to enforce the terms of this License or 164 | seeking damages relating thereto, the prevailing party shall be entitled to 165 | recover its costs and expenses, including, without limitation, reasonable 166 | attorneys' fees and costs incurred in connection with such action, including 167 | any appeal of such action. This section shall survive the termination of this 168 | License. 169 | 170 | 13) Miscellaneous. This License represents the complete agreement concerning 171 | the subject matter hereof. If any provision of this License is held to be 172 | unenforceable, such provision shall be reformed only to the extent necessary to 173 | make it enforceable. 174 | 175 | 14) Definition of "You" in This License. "You" throughout this License, whether 176 | in upper or lower case, means an individual or a legal entity exercising rights 177 | under, and complying with all of the terms of, this License. For legal 178 | entities, "You" includes any entity that controls, is controlled by, or is 179 | under common control with you. For purposes of this definition, "control" means 180 | (i) the power, direct or indirect, to cause the direction or management of such 181 | entity, whether by contract or otherwise, or (ii) ownership of fifty percent 182 | (50%) or more of the outstanding shares, or (iii) beneficial ownership of such 183 | entity. 184 | 185 | 15) Right to Use. You may use the Original Work in all ways not otherwise 186 | restricted or conditioned by this License or by law, and Licensor promises not 187 | to interfere with or be responsible for such uses by You. 188 | 189 | This license is Copyright (C) 2003-2004 Lawrence E. Rosen. All rights reserved. 190 | Permission is hereby granted to copy and distribute this license without 191 | modification. This license may not be modified without the express written 192 | permission of its copyright owner. 193 | -------------------------------------------------------------------------------- /tests/tests.js: -------------------------------------------------------------------------------- 1 | /* UMD.define */ (typeof define=="function"&&define||function(d,f,m){m={module:module,require:require};module.exports=f.apply(null,d.map(function(n){return m[n]||require(n)}))}) 2 | (["module", "heya-unit", "../mixin", "../mixOwn", "../mixDeep", "../clone", 3 | "../getProp", "../setProp", "../sprintf", "../delay", "../debounce", "../throttle"], 4 | function(module, unit, mixin, mixOwn, mixDeep, clone, 5 | getProp, setProp, sprintf, delay, debounce, throttle){ 6 | 7 | "use strict"; 8 | 9 | // tests 10 | 11 | unit.add(module, [ 12 | // mixin 13 | function test_mixin(t){ 14 | var p1 = {a: 1, b: "Hi!", c: [1, 2, 3]}, 15 | r1 = mixin({}, p1); 16 | eval(t.TEST("t.unify(r1, p1)")); 17 | eval(t.TEST("r1.c === p1.c")); 18 | 19 | var r2 = mixin({a: null, b: {}, d: 3}, p1); 20 | eval(t.TEST("t.unify(r2, {a: 1, b: 'Hi!', c: [1, 2, 3], d: 3})")); 21 | 22 | var p2 = Object.create(p1); 23 | p2.b = 42; 24 | p2.d = 55; 25 | var r3 = mixin({}, p2); 26 | eval(t.TEST("t.unify(r3, {a: 1, b: 42, c: [1, 2, 3], d: 55})")); 27 | 28 | eval(t.TEST("r3.hasOwnProperty('a')")); 29 | eval(t.TEST("r3.hasOwnProperty('b')")); 30 | eval(t.TEST("r3.hasOwnProperty('c')")); 31 | eval(t.TEST("r3.hasOwnProperty('d')")); 32 | 33 | eval(t.TEST("!p2.hasOwnProperty('a')")); 34 | eval(t.TEST("p2.hasOwnProperty('b')")); 35 | eval(t.TEST("!p2.hasOwnProperty('c')")); 36 | eval(t.TEST("p2.hasOwnProperty('d')")); 37 | }, 38 | // mixOwn 39 | function test_mixOwn(t){ 40 | var p1 = {a: 1, b: "Hi!", c: [1, 2, 3]}, 41 | r1 = mixOwn({}, p1); 42 | eval(t.TEST("t.unify(r1, p1)")); 43 | eval(t.TEST("r1.c === p1.c")); 44 | 45 | var r2 = mixOwn({a: null, b: {}, d: 3}, p1); 46 | eval(t.TEST("t.unify(r2, {a: 1, b: 'Hi!', c: [1, 2, 3], d: 3})")); 47 | 48 | var p2 = Object.create(p1); 49 | p2.b = 42; 50 | p2.d = 55; 51 | var r3 = mixOwn({}, p2); 52 | eval(t.TEST("t.unify(r3, {b: 42, d: 55})")); 53 | 54 | eval(t.TEST("!r3.hasOwnProperty('a')")); 55 | eval(t.TEST("r3.hasOwnProperty('b')")); 56 | eval(t.TEST("!r3.hasOwnProperty('c')")); 57 | eval(t.TEST("r3.hasOwnProperty('d')")); 58 | 59 | eval(t.TEST("!p2.hasOwnProperty('a')")); 60 | eval(t.TEST("p2.hasOwnProperty('b')")); 61 | eval(t.TEST("!p2.hasOwnProperty('c')")); 62 | eval(t.TEST("p2.hasOwnProperty('d')")); 63 | }, 64 | // clone 65 | function test_clone(t){ 66 | var p1 = {a: 1, b: "Hi!", c: [1, 2, 3]}, 67 | r1 = clone(p1); 68 | eval(t.TEST("t.unify(r1, p1)")); 69 | eval(t.TEST("r1 !== p1")); 70 | eval(t.TEST("r1.c !== p1.c")); 71 | 72 | var p2 = Object.create(p1); 73 | p2.b = 42; 74 | p2.d = 55; 75 | var r3 = clone(p2); 76 | eval(t.TEST("t.unify(r3, {a: 1, b: 42, c: [1, 2, 3], d: 55})")); 77 | 78 | eval(t.TEST("r3.hasOwnProperty('a')")); 79 | eval(t.TEST("r3.hasOwnProperty('b')")); 80 | eval(t.TEST("r3.hasOwnProperty('c')")); 81 | eval(t.TEST("r3.hasOwnProperty('d')")); 82 | 83 | eval(t.TEST("!p2.hasOwnProperty('a')")); 84 | eval(t.TEST("p2.hasOwnProperty('b')")); 85 | eval(t.TEST("!p2.hasOwnProperty('c')")); 86 | eval(t.TEST("p2.hasOwnProperty('d')")); 87 | 88 | var p3 = {a: [new Date(), /abc/g]}, 89 | r2 = clone(p3); 90 | eval(t.TEST("t.unify(r2, p3)")); 91 | eval(t.TEST("r2 !== p3")); 92 | eval(t.TEST("r2.a !== p3.a")); 93 | eval(t.TEST("r2.a[0] !== p3.a[0]")); 94 | eval(t.TEST("r2.a[0].getTime() === p3.a[0].getTime()")); 95 | eval(t.TEST("r2.a[1] !== p3.a[1]")); 96 | eval(t.TEST("r2.a[1].source === p3.a[1].source")); 97 | eval(t.TEST("r2.a[1].global === p3.a[1].global")); 98 | eval(t.TEST("r2.a[1].ignoreCase === p3.a[1].ignoreCase")); 99 | eval(t.TEST("r2.a[1].multiline === p3.a[1].multiline")); 100 | }, 101 | // mixDeep 102 | function test_mixDeep(t){ 103 | eval(t.TEST("t.unify(mixDeep({}, {}), {})")); 104 | eval(t.TEST("t.unify(mixDeep({a: {b: 1}}, {a: {c: 2}}), {a: {b: 1, c: 2}})")); 105 | eval(t.TEST("t.unify(mixDeep({a: {b: 1}}, {a: [1, 2]}), {a: [1, 2]})")); 106 | eval(t.TEST("t.unify(mixDeep({a: [1, , 3]}, {a: [, 2, , 4]}), {a: [1, 2, 3, 4]})")); 107 | eval(t.TEST("t.unify(mixDeep({a: {b: new Date(2013, 3, 15)}}, {a: {c: /.+/mig}}), {a: {b: new Date(2013, 3, 15), c: /.+/mig}})")); 108 | eval(t.TEST("t.unify(mixDeep({}, {a: {c: 2}}), {a: {c: 2}})")); 109 | 110 | eval(t.TEST("t.unify(mixDeep({a: {b: 1}}, {a: {c: 2}}, true), {a: {b: 1}})")); 111 | eval(t.TEST("t.unify(mixDeep({a: {b: 1}}, {a: {b: 42, c: 2}, d: 13}, true), {a: {b: 42}})")); 112 | 113 | eval(t.TEST("t.unify(mixDeep({a: {b: 1}}, {a: {c: 2}}, false, clone), {a: {b: 1, c: 2}})")); 114 | eval(t.TEST("t.unify(mixDeep({a: {b: 1}}, {a: [1, 2]}, false, clone), {a: [1, 2]})")); 115 | eval(t.TEST("t.unify(mixDeep({a: [1, , 3]}, {a: [, 2, , 4]}, false, clone), {a: [1, 2, 3, 4]})")); 116 | eval(t.TEST("t.unify(mixDeep({a: {b: new Date(2013, 3, 15)}}, {a: {c: /.+/mig}}, false, clone), {a: {b: new Date(2013, 3, 15), c: /.+/mig}})")); 117 | eval(t.TEST("t.unify(mixDeep({}, {a: {c: 2}}, false, clone), {a: {c: 2}})")); 118 | }, 119 | // getProp/setProp 120 | function test_prop(t){ 121 | var p = {a: 1, " ": "Hi!", c: [new Date(2013, 3, 15), /.+/mig], d: { 122 | e: [1, 2, 3], f: { 123 | g: "Hello", h: null 124 | } 125 | }}; 126 | 127 | eval(t.TEST("getProp(p, 'a') === 1")); 128 | eval(t.TEST("getProp(p, 'c') === p.c")); 129 | eval(t.TEST("getProp(p, 'c.length') === 2")); 130 | eval(t.TEST("getProp(p, 'c.0').getFullYear() === 2013")); 131 | eval(t.TEST("getProp(p, 'c.1.source') === '.+'")); 132 | eval(t.TEST("getProp(p, 'd.e.length') === 3")); 133 | eval(t.TEST("getProp(p, 'e') === undefined")); 134 | eval(t.TEST("getProp(p, 'd.f.g') === 'Hello'")); 135 | 136 | eval(t.TEST("getProp(p, [' ']) === 'Hi!'")); 137 | eval(t.TEST("getProp(p, ['d', 'f', 'g']) === 'Hello'")); 138 | 139 | var r = {}; 140 | setProp(r, "a", 1); 141 | setProp(r, "d.f.g", "Hello"); 142 | setProp(r, ["d", "f", "h"], null); 143 | setProp(r, [" "], "Hi!"); 144 | setProp(r, "d.e", [1, 2, 3]); 145 | setProp(r, "c", [, /.+/mig]); 146 | setProp(r, "c.0", new Date(2013, 3, 15)); 147 | 148 | eval(t.TEST("t.unify(p, r)")); 149 | }, 150 | // sprintf 151 | function test_sprintf(t){ 152 | eval(t.TEST("sprintf('a %% b') === 'a % b'")); 153 | eval(t.TEST("sprintf('a %% %d b', 123) === 'a % 123 b'")); 154 | eval(t.TEST("sprintf('a %d b', 123.456) === 'a 123 b'")); 155 | eval(t.TEST("sprintf('a %d b', 456.789) === 'a 457 b'")); 156 | eval(t.TEST("sprintf('a %+d b', 123.456) === 'a +123 b'")); 157 | eval(t.TEST("sprintf('a %+d b', -123.456) === 'a -123 b'")); 158 | eval(t.TEST("sprintf('a % d b', 123.456) === 'a 123 b'")); 159 | eval(t.TEST("sprintf('a % d b', -123.456) === 'a -123 b'")); 160 | 161 | eval(t.TEST("sprintf('a %u b', 123.456) === 'a 123 b'")); 162 | eval(t.TEST("sprintf('a %i b', 123.456) === 'a 123 b'")); 163 | eval(t.TEST("sprintf('a %o b', 123.456) === 'a 173 b'")); 164 | eval(t.TEST("sprintf('a %b b', 123.456) === 'a 1111011 b'")); 165 | eval(t.TEST("sprintf('a %B b', 123.456) === 'a 1111011 b'")); 166 | eval(t.TEST("sprintf('a %x b', 123.456) === 'a 7b b'")); 167 | eval(t.TEST("sprintf('a %X b', 123.456) === 'a 7B b'")); 168 | 169 | eval(t.TEST("sprintf('a %#d b', 123.456) === 'a 123 b'")); 170 | eval(t.TEST("sprintf('a %#u b', 123.456) === 'a 123 b'")); 171 | eval(t.TEST("sprintf('a %#i b', 123.456) === 'a 123 b'")); 172 | eval(t.TEST("sprintf('a %#o b', 123.456) === 'a 0173 b'")); 173 | eval(t.TEST("sprintf('a %#b b', 123.456) === 'a b1111011 b'")); 174 | eval(t.TEST("sprintf('a %#B b', 123.456) === 'a B1111011 b'")); 175 | eval(t.TEST("sprintf('a %#x b', 123.456) === 'a 0x7b b'")); 176 | eval(t.TEST("sprintf('a %#X b', 123.456) === 'a 0X7B b'")); 177 | 178 | eval(t.TEST("sprintf('a %d b', 0) === 'a 0 b'")); 179 | eval(t.TEST("sprintf('a %u b', 0) === 'a 0 b'")); 180 | eval(t.TEST("sprintf('a %i b', 0) === 'a 0 b'")); 181 | eval(t.TEST("sprintf('a %o b', 0) === 'a 0 b'")); 182 | eval(t.TEST("sprintf('a %b b', 0) === 'a 0 b'")); 183 | eval(t.TEST("sprintf('a %B b', 0) === 'a 0 b'")); 184 | eval(t.TEST("sprintf('a %x b', 0) === 'a 0 b'")); 185 | eval(t.TEST("sprintf('a %X b', 0) === 'a 0 b'")); 186 | 187 | eval(t.TEST("sprintf('a %#d b', 0) === 'a 0 b'")); 188 | eval(t.TEST("sprintf('a %#u b', 0) === 'a 0 b'")); 189 | eval(t.TEST("sprintf('a %#i b', 0) === 'a 0 b'")); 190 | eval(t.TEST("sprintf('a %#o b', 0) === 'a 0 b'")); 191 | eval(t.TEST("sprintf('a %#b b', 0) === 'a 0 b'")); 192 | eval(t.TEST("sprintf('a %#B b', 0) === 'a 0 b'")); 193 | eval(t.TEST("sprintf('a %#x b', 0) === 'a 0 b'")); 194 | eval(t.TEST("sprintf('a %#X b', 0) === 'a 0 b'")); 195 | 196 | eval(t.TEST("sprintf('a %f b', 123.456) === 'a 123 b'")); 197 | eval(t.TEST("sprintf('a %.1f b', 123.456) === 'a 123.5 b'")); 198 | eval(t.TEST("sprintf('a %.2f b', 123.456) === 'a 123.46 b'")); 199 | eval(t.TEST("sprintf('a %.3f b', 123.456) === 'a 123.456 b'")); 200 | eval(t.TEST("sprintf('a %.4f b', 123.456) === 'a 123.4560 b'")); 201 | 202 | eval(t.TEST("sprintf('a %e b', 123.456) === 'a 1.23456e+2 b'")); 203 | eval(t.TEST("sprintf('a %.1e b', 123.456) === 'a 1.2e+2 b'")); 204 | eval(t.TEST("sprintf('a %.2e b', 123.456) === 'a 1.23e+2 b'")); 205 | eval(t.TEST("sprintf('a %.3e b', 123.456) === 'a 1.235e+2 b'")); 206 | eval(t.TEST("sprintf('a %.4e b', 123.456) === 'a 1.2346e+2 b'")); 207 | eval(t.TEST("sprintf('a %.5e b', 123.456) === 'a 1.23456e+2 b'")); 208 | eval(t.TEST("sprintf('a %.6e b', 123.456) === 'a 1.234560e+2 b'")); 209 | 210 | eval(t.TEST("sprintf('a %E b', 123.456) === 'a 1.23456E+2 b'")); 211 | eval(t.TEST("sprintf('a %.1E b', 123.456) === 'a 1.2E+2 b'")); 212 | eval(t.TEST("sprintf('a %.2E b', 123.456) === 'a 1.23E+2 b'")); 213 | eval(t.TEST("sprintf('a %.3E b', 123.456) === 'a 1.235E+2 b'")); 214 | eval(t.TEST("sprintf('a %.4E b', 123.456) === 'a 1.2346E+2 b'")); 215 | eval(t.TEST("sprintf('a %.5E b', 123.456) === 'a 1.23456E+2 b'")); 216 | eval(t.TEST("sprintf('a %.6E b', 123.456) === 'a 1.234560E+2 b'")); 217 | 218 | eval(t.TEST("sprintf('a %g b', 123.456) === 'a 123.456 b'")); 219 | eval(t.TEST("sprintf('a %.1g b', 123.456) === 'a 1e+2 b'")); 220 | eval(t.TEST("sprintf('a %.2g b', 123.456) === 'a 1.2e+2 b'")); 221 | eval(t.TEST("sprintf('a %.3g b', 123.456) === 'a 123 b'")); 222 | eval(t.TEST("sprintf('a %.4g b', 123.456) === 'a 123.5 b'")); 223 | eval(t.TEST("sprintf('a %.5g b', 123.456) === 'a 123.46 b'")); 224 | eval(t.TEST("sprintf('a %.6g b', 123.456) === 'a 123.456 b'")); 225 | 226 | eval(t.TEST("sprintf('a %G b', 123.456) === 'a 123.456 b'")); 227 | eval(t.TEST("sprintf('a %.1G b', 123.456) === 'a 1E+2 b'")); 228 | eval(t.TEST("sprintf('a %.2G b', 123.456) === 'a 1.2E+2 b'")); 229 | eval(t.TEST("sprintf('a %.3G b', 123.456) === 'a 123 b'")); 230 | eval(t.TEST("sprintf('a %.4G b', 123.456) === 'a 123.5 b'")); 231 | eval(t.TEST("sprintf('a %.5G b', 123.456) === 'a 123.46 b'")); 232 | eval(t.TEST("sprintf('a %.6G b', 123.456) === 'a 123.456 b'")); 233 | 234 | eval(t.TEST("sprintf('a %d b', 123.456) === 'a 123 b'")); 235 | eval(t.TEST("sprintf('a %.1d b', 123.456) === 'a 123 b'")); 236 | eval(t.TEST("sprintf('a %.2d b', 123.456) === 'a 123 b'")); 237 | eval(t.TEST("sprintf('a %.3d b', 123.456) === 'a 123 b'")); 238 | eval(t.TEST("sprintf('a %.4d b', 123.456) === 'a 0123 b'")); 239 | eval(t.TEST("sprintf('a %.5d b', 123.456) === 'a 00123 b'")); 240 | eval(t.TEST("sprintf('a %.6d b', 123.456) === 'a 000123 b'")); 241 | 242 | eval(t.TEST("sprintf('a %.3o b', 123) === 'a 173 b'")); 243 | eval(t.TEST("sprintf('a %.8b b', 123) === 'a 01111011 b'")); 244 | eval(t.TEST("sprintf('a %.4x b', 123) === 'a 007b b'")); 245 | 246 | eval(t.TEST("sprintf('a %#.3o b', 123) === 'a 0173 b'")); 247 | eval(t.TEST("sprintf('a %#.8b b', 123) === 'a b01111011 b'")); 248 | eval(t.TEST("sprintf('a %#.4x b', 123) === 'a 0x007b b'")); 249 | 250 | eval(t.TEST("sprintf('a %j b', {a: 1, b: true, c: 'Hi!'}) === 'a {\"a\":1,\"b\":true,\"c\":\"Hi!\"} b'")); 251 | 252 | eval(t.TEST("sprintf('a %s b', 'Hello') === 'a Hello b'")); 253 | eval(t.TEST("sprintf('a %S b', 'Hello') === 'a HELLO b'")); 254 | eval(t.TEST("sprintf('a %t b', 'Hello') === 'a hello b'")); 255 | 256 | eval(t.TEST("sprintf('a %.4s b', 'Hello') === 'a Hell b'")); 257 | eval(t.TEST("sprintf('a %6.4s b', 'Hello') === 'a Hell b'")); 258 | eval(t.TEST("sprintf('a %-6.4s b', 'Hello') === 'a Hell b'")); 259 | eval(t.TEST("sprintf('a %6s b', 'Hello') === 'a Hello b'")); 260 | eval(t.TEST("sprintf('a %-6s b', 'Hello') === 'a Hello b'")); 261 | }, 262 | // delay 263 | { 264 | timeout: 1000, 265 | test: function test_delay(t){ 266 | var a = t.startAsync("delay"); 267 | t.info("Creating delayed function"); 268 | var f = delay(function(v){ 269 | t.info("callback #1: " + v); 270 | a.done(); 271 | }, 200); 272 | t.info("Calling delayed function"); 273 | var h = f(1); 274 | t.info("Canceling delayed function"); 275 | h.destroy(); 276 | t.info("Calling delayed function again"); 277 | f(2); 278 | t.info("Waiting for result"); 279 | }, 280 | logs: [ 281 | {text: "Creating delayed function"}, 282 | {text: "Calling delayed function"}, 283 | {text: "Canceling delayed function"}, 284 | {text: "Calling delayed function again"}, 285 | {text: "Waiting for result"}, 286 | {text: "callback #1: 2"} 287 | ] 288 | }, 289 | // debounce 290 | { 291 | timeout: 1000, 292 | test: function test_debounce(t){ 293 | var a = t.startAsync("debounce"); 294 | t.info("Creating debounced function"); 295 | var f = debounce(function(v){ 296 | t.info("callback #1: " + v); 297 | a.done(); 298 | }, 200); 299 | t.info("Calling debounced function"); 300 | var h = f(1); 301 | t.info("Canceling debounced function"); 302 | h.destroy(); 303 | t.info("Calling debounced function again"); 304 | f(2); 305 | t.info("Running timeline"); 306 | for(var i = 20; i < 100; i += 20){ 307 | (function(ms){ 308 | setTimeout(function(){ 309 | t.info("Calling with: " + ms); 310 | f(ms); 311 | }, ms); 312 | })(i); 313 | } 314 | t.info("Waiting for result"); 315 | }, 316 | logs: [ 317 | {text: "Creating debounced function"}, 318 | {text: "Calling debounced function"}, 319 | {text: "Canceling debounced function"}, 320 | {text: "Calling debounced function again"}, 321 | {text: "Running timeline"}, 322 | {text: "Waiting for result"}, 323 | {text: "Calling with: 20"}, 324 | {text: "Calling with: 40"}, 325 | {text: "Calling with: 60"}, 326 | {text: "Calling with: 80"}, 327 | {text: "callback #1: 80"} 328 | ] 329 | }, 330 | // throttle 331 | { 332 | timeout: 1000, 333 | test: function test_throttle(t){ 334 | var a = t.startAsync("throttle"); 335 | t.info("Creating throttled function"); 336 | var flag = false, f = throttle(function(v){ 337 | t.info("callback #1: " + v); 338 | if(flag){ 339 | a.done(); 340 | } 341 | }, 200); 342 | t.info("Calling throttled function"); 343 | f(1); 344 | t.info("Calling throttled function again"); 345 | var h = f(2); 346 | t.info("Canceling throttled function"); 347 | h.destroy(); 348 | t.info("Running timeline"); 349 | for(var i = 20; i < 100; i += 20){ 350 | (function(ms){ 351 | setTimeout(function(){ 352 | t.info("Calling with: " + ms); 353 | flag = ms == 80; 354 | f(ms); 355 | }, ms); 356 | })(i); 357 | } 358 | t.info("Waiting for result"); 359 | }, 360 | logs: [ 361 | {text: "Creating throttled function"}, 362 | {text: "Calling throttled function"}, 363 | {text: "callback #1: 1"}, 364 | {text: "Calling throttled function again"}, 365 | {text: "Canceling throttled function"}, 366 | {text: "Running timeline"}, 367 | {text: "Waiting for result"}, 368 | {text: "Calling with: 20"}, 369 | {text: "Calling with: 40"}, 370 | {text: "Calling with: 60"}, 371 | {text: "Calling with: 80"}, 372 | {text: "callback #1: 80"} 373 | ] 374 | } 375 | ]); 376 | 377 | unit.run(); 378 | }); 379 | --------------------------------------------------------------------------------