UNPKG

jquery.fancytree

Version:

jQuery tree view / tree grid plugin with support for keyboard, inline editing, filtering, checkboxes, drag'n'drop, and lazy loading

405 lines (345 loc) 10.9 kB
;(function($, window, document, undefined) { /*globals QUnit */ var TOOLS = {}, log = [], FIXTURE_SELECTOR = "#tree"; window.TEST_TOOLS = TOOLS; // TOOLS.EVENT_SEQUENCE = []; TOOLS.EVENT_SEQUENCE = "<deprecated: use assert.EVENT_SEQUENCE instead>"; TOOLS.TOTAL_ELAP = 0; /******************************************************************************* * QUnit setup */ TOOLS.initQUnit = function() { // See https://github.com/axemclion/grunt-saucelabs QUnit.done(function (testResults) { var details, i, len, tests = []; for(i = 0, len = log.length; i < len; i++) { details = log[i]; tests.push({ name: details.name, result: details.result, expected: details.expected, actual: details.actual, source: details.source }); } testResults.tests = tests; // Expand first section when all tests are run $("ol#qunit-tests > li:first > ol").show("slow"); /*jshint camelcase:false*/ // jscs: disable window.global_test_results = testResults; // used by saucelabs /*jshint camelcase:true*/ // jscs: enable }); // See https://github.com/axemclion/grunt-saucelabs QUnit.testStart(function(testDetails){ QUnit.log(function(details){ if (!details.result) { details.name = testDetails.name; log.push(details); } }); }); // Silence, please $.ui.fancytree.debugLevel = 1; }; TOOLS.createInfoSection = function() { // Create the first informational section QUnit.module("Configuration and Summary"); QUnit.test("Version info", function(assert) { TOOLS.setup(assert); assert.expect(5); assert.ok(true, "Fancytree v" + $.ui.fancytree.version + ", buildType='" + $.ui.fancytree.buildType + "'"); assert.ok(true, "jQuery UI " + jQuery.ui.version + " (uiBackCompat=" + $.uiBackCompat + ")"); assert.ok(true, "jQuery " + jQuery.fn.jquery); assert.ok(true, "Browser: " + TOOLS.getBrowserInfo()); assert.ok(true, "Cumulated test time: " + TOOLS.TOTAL_ELAP + " milliseconds"); }); }; /******************************************************************************* * Tool functions */ //function simulateClick(selector) { // var event = document.createEvent("MouseEvents"); // event.initEvent("click", true, true); // $(selector).each(function(){ // this.dispatchEvent(event); // }); //} /** Helper to reset environment for asynchronous Fancytree tests. */ TOOLS.appendEvent = function(assert, msg) { if( !assert || !assert.deepEqual ) { $.error("assert must be passed"); } if( typeof msg !== "string" ) { $.error("msg must be a string"); } if( !assert.EVENT_SEQUENCE ) { $.error("TOOLS.setup() was not called"); } assert.EVENT_SEQUENCE.push(msg); }; /** Helper to reset environment for asynchronous Fancytree tests. */ TOOLS.setup = function(assert) { if( !assert ) { $.error("Need assert arg"); } if( assert.EVENT_SEQUENCE ) { $.error("Duplicate setup()"); } assert.EVENT_SEQUENCE = []; if( $(FIXTURE_SELECTOR).is(":ui-fancytree") ){ $(FIXTURE_SELECTOR).fancytree("destroy"); } }; /** Return an info string of current browser. */ TOOLS.getBrowserInfo = function() { var n = navigator.appName, ua = navigator.userAgent, tem, m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i); if(m && (tem = ua.match(/version\/([\.\d]+)/i)) !== null){ m[2]= tem[1]; } m = m ? [m[1], m[2]] : [n, navigator.appVersion, "-?"]; return m.join(", "); }; /** Get FancytreeNode from current tree. */ TOOLS.getNode = function(key){ return TOOLS.getTree().getNodeByKey(key); }; /** Get first node with matching title. */ TOOLS.getNodeByTitle = function(title){ var tree = $("#tree").fancytree("getTree"); return tree.findFirst(function(n){ return n.title === title; }); }; /** Get current Fancytree. */ TOOLS.getTree = function(){ return $(FIXTURE_SELECTOR).fancytree("getTree"); }; /** Get node title as rendered in the DOM. */ TOOLS.getNodeTitle = function(key){ var node = TOOLS.getNode(key); if(!node){ return undefined; } return $(node.span).find(".fancytree-title").html(); }; /** Convert array of nodes to array to array of node keys. */ TOOLS.getNodeKeyArray = function(nodeArray){ if(!$.isArray(nodeArray)){ return nodeArray; } return $.map(nodeArray, function(n){ return n.key; }); }; /** Generate a large hierarchy of nodes */ TOOLS.addGenericNodes = function(node, options, callback) { var d, f, i, j, k, key, opts = $.extend({ level1: 1, level2: 0, level3: 0, disableUpdate: true }, options); function _cb(parentNode, data, i, j, k) { if( !callback || callback(data, i, j, k) !== false ) { return parentNode.addChildren(data); } } if( opts.disableUpdate ) { node.tree.enableUpdate(false); } for(i=0; i<opts.level1; i++) { key = "" + (i+1); f = _cb(node, {title: "Folder_" + key, key: key, folder: true}, i, 0, 0); for (j=0; j<opts.level2; j++) { key = "" + (i+1) + "." + (j+1); d = _cb(f, {title: "Node_" + key, key: key}, i, j, 0); for (k=0; k<opts.level3; k++) { key = "" + (i+1) + "." + (j+1) + "." + (k+1); _cb(d, {title: "Node_" + key, key: key}, i, j, k); } } } if( opts.disableUpdate ) { node.tree.enableUpdate(true); } }; /** Fake an Ajax request, return a $.Promise. */ TOOLS.fakeAjaxLoad = function(node, count, delay){ delay = delay || 0; if($.isArray(delay)){ // random delay range [min..max] delay = Math.round(delay[0] + Math.random() * (delay[1] - delay[0])); } var dfd = new $.Deferred(); setTimeout(function(){ var i, children = []; for(i=0; i<count; i++){ children.push({ key: node.key + "_" + (i+1), title: node.title + "_" + (i+1), lazy: true }); } // emulate ajax deferred: done(data, textStatus, jqXHR) dfd.resolveWith(this, [children, null, null]); }, delay); return dfd.promise(); }; /** Format a number as string with thousands-separator. */ TOOLS.formatNumber = function(num) { var parts = num.toFixed(0).toString().split("."); parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","); return parts.join("."); }; TOOLS.makeBenchWrapper = function(assert, testName, count, callback) { return function() { var elap, start = Date.now(); // callback.apply(this, arguments); callback.call(); elap = Date.now() - start; if( count && elap ){ assert.ok(true, testName + " took " + elap + " milliseconds, " + TOOLS.formatNumber(1000 * count / elap) + " items/sec"); }else{ assert.ok(true, testName + " took " + elap + " milliseconds"); } TOOLS.TOTAL_ELAP += elap; }; }; /* Execute callback immediately and log timing as test result. * This function should be called inside a QUnit.test() function. */ TOOLS.benchmark = function(assert, testName, count, callback) { TOOLS.makeBenchWrapper(assert, testName, count, callback).call(); }; /* Execute callback, then immediately and log timing as test result. * * Example: * tools.benchmarkWithReflowAsync(assert, tree, "Add 500x500 nodes", null, function(){ // Add benchark code: tools.addGenericNodes(node, {level1: 500, level2: 500}); }).done(function(){ // Reflow and Redraw finished and have beem logged // ... }); * * This function should be called inside a QUnit.test() function. */ TOOLS.benchmarkWithReflowAsync = function(assert, tree, testName, count, callback) { var elap1, elap2, elap3, msg, dfd = new $.Deferred(), start = Date.now(); callback.call(); elap1 = Date.now() - start; // raw execution time // Query div size to trigger a layout reflow // As a call to a dummy function to prevent optimizations (cargo-cult?) // $.noop(window.innerHeight); $.noop(tree.$div[0].offsetHeight); elap2 = Date.now() - start; // execution time incl. reflow // Yield to interpreter -- Hopefully this will cause the browser to redraw, // so we can capture the timings: setTimeout(function(){ elap3 = Date.now() - start; // execution time incl. reflow & redraw msg = testName + " took " + elap3 + " ms (reflow w/o redraw: " + elap2 + " ms, raw: " + elap1 + " ms)"; if( count && elap1 ){ msg += ", " + TOOLS.formatNumber(1000 * count / elap3) + " items/sec"; } assert.ok(true, msg); TOOLS.TOTAL_ELAP += elap3; dfd.resolve(); }, 0); return dfd.promise(); }; /** * AsyncTimer */ function AsyncTimer(assert, name, count, start){ this.assert = assert; this.done = null; this.name = "AsyncTimer(" + name + ")"; this.stamp = null; this.count = count; if(start !== false){ this.start(); } } TOOLS.AsyncTimer = AsyncTimer; AsyncTimer.prototype = { toString: function(){ return this.name; }, start: function(){ /*jshint expr:true */ window.console && window.console.time && window.console.time(this.name); // halt QUnit // this.done = this.assert.async(); this.stamp = Date.now(); this.lastStamp = this.stamp; }, stop: function(){ /*jshint expr:true */ window.console && window.console.timeEnd && window.console.timeEnd(this.name); var elap = Date.now() - this.stamp; if( this.count && elap ){ this.assert.ok(true, this.name + " took " + elap + " milliseconds, " + TOOLS.formatNumber(1000.0 * this.count / elap) + " items/sec"); }else{ this.assert.ok(true, this.name + " took " + elap + " milliseconds"); } TOOLS.TOTAL_ELAP += elap; // Continue QUnit // this.done(); }, subtime: function(info){ var now = Date.now(), elap = now - this.lastStamp; this.lastStamp = now; this.assert.ok(true, "... " + this.name + " until '" + info + "' took " + elap + " milliseconds"); } }; /** Create a profile wrapper. * */ /* function profileWrapper(fn, flag, opts){ if( flag === false ){ return fn; } opts = $.extend({printTime: true}, opts); var start, elap, stats = { count: 0, countDeep: 0, maxLevel: 0, min: Math.pow(2, 32) - 1, max: 0, sum: 0 }, name = fn.name, level = 0, // wrapper = function(){ level += 1; stats.countDeep += 1; stats.maxLevel = Math.max(stats.maxLevel, level); if( level === 1 ){ stats.count += 1; if( opts.printTime ){ console.time(name); } start = new Date().getTime(); fn.apply(this, arguments); elap = new Date().getTime() - start; if(opts.printTime){ console.timeEnd(name); } stats.min = Math.min(stats.min, elap); stats.max = Math.max(stats.max, elap); stats.sum += elap; }else{ // We don't collect stats for recursive calls fn.apply(this, arguments); } level -= 1; }; wrapper.stats = function(){ return stats; }; return wrapper; } */ }(jQuery, window, document));