'use strict'; /*@ Ox.DocPanel Documentation Panel options Options object collapsible If true, the list can be collabsed element Default content expanded If true, modules and sections are expanded in the list files Files to parse for docs (alternative to items option) getModule Returns module for given item getSection Returns section for given item items Array of doc items (alternative to files option) path Path prefix references RegExp to find references replace <[[]]|[]> See Ox.SyntaxHighlighter resizable If true, list is resizable resize List resize positions runTests If true, run tests selected Id of the selected item showLoading If true, show loading icon when parsing files showTests If true, show test results in list showTooltips If true, show test result tooltips in list size Default list size stripComments If true, strip comments in source code self Shared private variable ([options[, self]]) -> Documentation Panel load Fires once all docs are loaded items [o] Array of doc items tests Fires once all tests finished results [o] Array of results select select @*/ Ox.DocPanel = function(options, self) { self = self || {}; var that = Ox.Element({}, self) .defaults({ collapsible: false, element: null, examples: [], examplesPath: '', expanded: false, files: [], getModule: function(item) { return item.file.replace(self.options.path, ''); }, getSection: function(item) { return item.section; }, items: [], path: '', references: null, replace: [], resizable: false, resize: [128, 256, 384], results: null, runTests: false, selected: '', showLoading: false, showTests: false, showTooltips: false, size: 256, stripComments: false }) .options(options || {}) .update({ selected: function() { selectItem(self.options.selected); } }); self.$list = Ox.Element(); self.$toolbar = Ox.Bar({size: 24}).css({textAlign: 'center'}); self.$sidebar = Ox.SplitPanel({ elements: [ {element: Ox.Element()}, {element: Ox.Element(), size: 24} ], orientation: 'vertical' }); self.$page = Ox.Element(); self.$testsStatus = $('
') .css({marginTop: '5px', textAlign: 'center'}) .appendTo(self.$toolbar); if (!self.options.results) { self.options.results = {}; self.$testsButton = Ox.Button({title: Ox._('Run Tests')}) .css({margin: '4px auto'}) .bindEvent({click: runTests}) .appendTo(self.$toolbar); self.$testsStatus.hide(); } else { self.$testsStatus.html(formatResults()); } that.setElement( self.$panel = Ox.SplitPanel({ elements: [ { collapsible: self.options.collapsible, element: self.$sidebar, resizable: self.options.resizable, resize: self.options.resize, size: self.options.size }, { element: self.$page } ], orientation: 'horizontal' }) ); if (self.options.files && self.options.files.length) { setTimeout(function() { self.options.showLoading && showLoadingScreen(); self.options.files = Ox.makeArray(self.options.files); self.options.items = Ox.doc(self.options.files.map(function(file) { return self.options.path + file; }), function(docItems) { self.options.items = docItems; getExamples(function() { self.options.showLoading && hideLoadingScreen(); self.$sidebar.replaceElement(1, self.$toolbar); renderList(); self.options.runTests && runTests(); that.triggerEvent('load', {items: self.options.items}); }); }); }); } else { getExamples(function() { self.$sidebar.replaceElement(1, self.$toolbar); renderList(); self.options.runTests && runTests(); }); } function formatResults() { var results = self.options.results[''], tests = results.passed + results.failed; return tests + ' test' + (tests == 1 ? '' : 's') + ', ' + results.passed + ' passed, ' + results.failed + ' failed'; } function getExamples(callback) { var i = 0; if (self.options.examples && self.options.examples.length) { self.options.examples.forEach(function(example) { var path = self.options.examplesPath + example; Ox.get(path + '/index.html', function(html) { var match = html.match(/(.+)<\/title>/), title = match ? match[1] : Ox._('Untitled'); Ox.get(path + '/js/example.js', function(js) { var references = js.match(self.options.references); if (references) { Ox.unique(references).forEach(function(reference) { var item = getItemByName(reference); if (item) { item.examples = ( item.examples || [] ).concat({ id: example.split('/').pop(), title: title }); } }); } if (++i == self.options.examples.length) { self.options.items.forEach(function(item) { if (item.examples) { item.examples.sort(function(a, b) { a = a.title.toLowerCase(); b = b.title.toLowerCase(); return a < b ? -1 : a > b ? 1 : 0; }); } }); callback(); } }); }); }); } else { callback(); } } function getIcon(id, expanded) { var $icon = null, results = self.options.results[id]; if (!Ox.isEmpty(self.options.results)) { $icon = Ox.Theme.getColorImage( 'symbol' + (expanded === true ? 'Down' : expanded === false ? 'Right' : 'Center'), !results ? '' : results.failed === 0 ? 'passed' : 'failed', self.options.showTooltips ? getTooltip(results) : null ); } else if (!Ox.endsWith(id, '/')) { $icon = $('<img>').attr({src: Ox.UI.getImageURL('symbolCenter')}); } return $icon; } function getItemByName(name) { var item = null; Ox.forEach(self.options.items, function(v) { if (v.name == name) { item = v; return false; // break } }); return item; } function getTooltip(results) { return results ? results.passed + ' test' + (results.passed == 1 ? '' : 's') + ' passed' + (results.failed ? ', ' + results.failed + ' test' + (results.failed == 1 ? '' : 's') + ' failed' : '' ) : 'No tests'; } function hideLoadingScreen() { self.$loadingIcon.stop().remove(); self.$loadingText.remove(); } function parseFiles(callback) { var counter = 0, docItems = [], length = self.options.files.length; self.options.files.forEach(function(file) { Ox.doc(self.options.path + file, function(fileItems) { docItems = docItems.concat(fileItems); ++counter == length && callback(docItems); }); }); } function renderList() { var treeItems = []; self.options.items.forEach(function(docItem) { var moduleIndex, results, sectionIndex; docItem.module = self.options.getModule(docItem); moduleIndex = Ox.getIndexById(treeItems, docItem.module + '/'); if (moduleIndex == -1) { treeItems.push({ id: docItem.module + '/', items: [], title: docItem.module }); moduleIndex = treeItems.length - 1; } docItem.section = self.options.getSection(docItem); if (docItem.section) { sectionIndex = Ox.getIndexById( treeItems[moduleIndex].items, docItem.module + '/' + docItem.section + '/' ); if (sectionIndex == -1) { treeItems[moduleIndex].items.push({ id: docItem.module + '/' + docItem.section + '/', items: [], title: docItem.section }); sectionIndex = treeItems[moduleIndex].items.length - 1; } } ( docItem.section ? treeItems[moduleIndex].items[sectionIndex] : treeItems[moduleIndex] ).items.push({ id: docItem.module + '/' + ( docItem.section ? docItem.section + '/' : '' ) + docItem.name, title: docItem.name }); }); treeItems.sort(sortByTitle); treeItems.forEach(function(item) { item.items.sort(sortByTitle); item.items.forEach(function(subitem) { subitem.items.sort(sortByTitle); }); }); self.$list = Ox.TreeList({ expanded: self.options.expanded, icon: self.options.showTests ? getIcon : Ox.UI.getImageURL('symbolCenter'), items: treeItems, selected: self.options.selected ? [self.options.selected] : '', width: self.options.size }) .bindEvent({ select: function(data) { if (!data.ids[0] || !Ox.endsWith(data.ids[0], '/')) { selectItem( data.ids[0] ? data.ids[0].split('/').pop() : '' ); } } }); self.$sidebar.replaceElement(0, self.$list); selectItem(self.options.selected); } function runTests() { self.$testsButton.remove(); self.$testsStatus.html('Running Tests...').show(); Ox.load({Geo: {}, Image: {}, Unicode: {}}, function() { Ox.test(self.options.items, function(results) { results.forEach(function(result) { var item = getItemByName(result.name), passed = result.passed ? 'passed' : 'failed'; item.tests[Ox.indexOf(item.tests, function(test) { return test.statement == result.statement; })] = result; ['', item.module + '/'].concat( item.section ? item.module + '/' + item.section + '/' : [], item.module + '/' + (item.section ? item.section + '/' : '') + item.name ).forEach(function(key) { self.options.results[key] = self.options.results[key] || {passed: 0, failed: 0}; self.options.results[key][passed]++; }); }); self.$testsStatus.html(formatResults()); renderList(); that.triggerEvent('tests', {results: self.options.results}); }); }); } function selectItem(id) { var item = id ? getItemByName(id) : null; if (item) { self.options.selected = id; self.$list.options({ selected: [item.module + '/' + ( item.section ? item.section + '/' : '' ) + item.name] }); self.$page = Ox.DocPage({ item: item, replace: self.options.replace, stripComments: self.options.stripComments }) .bindEvent({ close: function() { selectItem(); }, example: function(data) { that.triggerEvent('example', data); } }); self.$panel.replaceElement(1, self.$page); } else { self.options.selected = ''; self.$list.options({selected: []}); self.$page.empty().append(self.options.element || $('<div>')); } that.triggerEvent('select', {id: self.options.selected}); } function showLoadingScreen() { self.$loadingIcon = Ox.LoadingIcon({size: 16}) .css({ position: 'absolute', left: (self.$page.width() - self.options.size) / 2 - 8, top: self.$page.height() / 2 - 20 }) .appendTo(self.$page) .start(); self.$loadingText = $('<div>') .addClass('OxLight') .css({ position: 'absolute', left: (self.$page.width() - self.options.size) / 2 - 128, top: self.$page.height() / 2 + 4, width: 256, textAlign: 'center' }) .html(Ox._('Generating Documentation...')) .appendTo(self.$page); } function sortByTitle(a, b) { var a = Ox.stripTags(a.title).toLowerCase(), b = Ox.stripTags(b.title).toLowerCase(); return a < b ? -1 : a > b ? 1 : 0; } return that; };