├── .gitignore ├── tags-parser ├── requirements.txt ├── sample-tags ├── parser.py └── Flexigrid │ └── js │ └── flexigrid.js ├── chrome-extension ├── movie.mp4 ├── img │ ├── icon.png │ ├── logo128.png │ ├── logo16.png │ ├── logo19.png │ ├── spinner.gif │ └── logo19-loading.png ├── shared.js ├── utils │ └── browser-action.js ├── context-menu.js ├── manifest.json ├── background.js ├── popup.js ├── popup.html ├── injected.js └── jquery-2.1.4.min.js ├── scripts ├── rundevserver ├── runctags ├── runctagsfile └── runserver ├── gitCloner ├── logging.js ├── config.js ├── package.json ├── utils.js ├── ctags.js ├── cache.js ├── getGit.js └── server.js ├── CHANGELOG.mdown ├── Vagrantfile ├── README.md └── sample-tags /.gitignore: -------------------------------------------------------------------------------- 1 | repos/ 2 | **/node_modules/ 3 | error.log -------------------------------------------------------------------------------- /tags-parser/requirements.txt: -------------------------------------------------------------------------------- 1 | logentries 2 | click 3 | -------------------------------------------------------------------------------- /chrome-extension/movie.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhereIsItDev/whereisit/HEAD/chrome-extension/movie.mp4 -------------------------------------------------------------------------------- /chrome-extension/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhereIsItDev/whereisit/HEAD/chrome-extension/img/icon.png -------------------------------------------------------------------------------- /chrome-extension/img/logo128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhereIsItDev/whereisit/HEAD/chrome-extension/img/logo128.png -------------------------------------------------------------------------------- /chrome-extension/img/logo16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhereIsItDev/whereisit/HEAD/chrome-extension/img/logo16.png -------------------------------------------------------------------------------- /chrome-extension/img/logo19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhereIsItDev/whereisit/HEAD/chrome-extension/img/logo19.png -------------------------------------------------------------------------------- /chrome-extension/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhereIsItDev/whereisit/HEAD/chrome-extension/img/spinner.gif -------------------------------------------------------------------------------- /chrome-extension/img/logo19-loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhereIsItDev/whereisit/HEAD/chrome-extension/img/logo19-loading.png -------------------------------------------------------------------------------- /scripts/rundevserver: -------------------------------------------------------------------------------- 1 | export NODE_ENV=dev 2 | PATH=$PATH:./gitCloner/node_modules/.bin/ nodemon -w ./chrome-extension -w ./gitCloner gitCloner/server.js 3 | -------------------------------------------------------------------------------- /scripts/runctags: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | # scripts are relative to the root dir of this repo 3 | # so it can find the tags-parser directory no problem 4 | 5 | python tags-parser/parser.py --tag_name $1 --repo_path $2 6 | -------------------------------------------------------------------------------- /scripts/runctagsfile: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | # scripts are relative to the root dir of this repo 3 | # so it can find the tags-parser directory no problem 4 | 5 | python tags-parser/parser.py --code_file $1 --repo_path $2 6 | -------------------------------------------------------------------------------- /gitCloner/logging.js: -------------------------------------------------------------------------------- 1 | var Logger = require('le_node'); 2 | var config = require('./config'); 3 | 4 | var log = new Logger({ 5 | token: config.LE_TOKEN, 6 | console: true 7 | }); 8 | 9 | module.exports = log; 10 | -------------------------------------------------------------------------------- /scripts/runserver: -------------------------------------------------------------------------------- 1 | export NODE_ENV=prod FOREVER_UID=production 2 | PATH=$PATH:./gitCloner/node_modules/.bin/ forever stop $FOREVER_UID 3 | PATH=$PATH:./gitCloner/node_modules/.bin/ forever start -l forever.log -o out.log -e err.log --append --uid "$FOREVER_UID" gitCloner/server.js 4 | -------------------------------------------------------------------------------- /gitCloner/config.js: -------------------------------------------------------------------------------- 1 | var config = {}; 2 | 3 | var isProd = process.env.NODE_ENV === 'prod'; 4 | var isDev = !isProd; 5 | 6 | if (isProd) { 7 | config.LE_TOKEN = 'b9d62caf-ba48-4151-84b8-a1a2f957f51a' 8 | } else { 9 | config.LE_TOKEN = '681b893c-144f-479f-8442-4354db192afd'; 10 | } 11 | 12 | module.exports = config 13 | -------------------------------------------------------------------------------- /CHANGELOG.mdown: -------------------------------------------------------------------------------- 1 | 2015-08-09 2 | ========== 3 | 4 | * Support GitHub's pjax loading, previously navigating to other code files will result in the extension not loading, user has to refresh the page for extension to work. This fix adds a couple of event listeners to automatically insert links when the pjax calls end 5 | * Bump to v0.2.3 6 | -------------------------------------------------------------------------------- /chrome-extension/shared.js: -------------------------------------------------------------------------------- 1 | function makeLink(path, location, line) { 2 | var splits = location.split('/'); 3 | var github = 'https://github.com'; 4 | var user = splits[3]; 5 | var repo = splits[4]; 6 | var blob = splits[5] || 'blob'; 7 | var branch = splits[6] || 'master'; 8 | url = [github, user, repo, blob, branch, path].join('/'); 9 | url += '#l' + line; 10 | return url; 11 | } 12 | -------------------------------------------------------------------------------- /chrome-extension/utils/browser-action.js: -------------------------------------------------------------------------------- 1 | 2 | function setLoadingState(tabId) { 3 | chrome.pageAction.setIcon({ 4 | tabId: tabId, 5 | path: 'img/logo19-loading.png', 6 | }) 7 | chrome.pageAction.show(tabId) 8 | } 9 | 10 | function setNormalState(tabId) { 11 | chrome.pageAction.setIcon({ 12 | tabId: tabId, 13 | path: 'img/logo19.png', 14 | }) 15 | chrome.pageAction.show(tabId) 16 | } 17 | -------------------------------------------------------------------------------- /chrome-extension/context-menu.js: -------------------------------------------------------------------------------- 1 | chrome.contextMenus.create({ 2 | title: 'Jump to definition', 3 | contexts: ['selection'], 4 | onclick: getClickHandler() 5 | }); 6 | 7 | function getClickHandler() { 8 | return function(info ,tab) { 9 | var url = chrome.extension.getURL('popup.html'); 10 | url += '?selection=' + info.selectionText + '&location=' + info.pageUrl; 11 | chrome.tabs.create({url: url}); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /gitCloner/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gitCloner", 3 | "version": "0.1.0", 4 | "description": "clones git repo", 5 | "main": "server.js", 6 | "dependencies": { 7 | "body-parser": "~1.12.4", 8 | "express": "~4.12.4", 9 | "multer": "~0.1.8", 10 | "shelljs": "~0.5.1", 11 | "forever": "~0.14.1", 12 | "le_node": "~1.0.12" 13 | }, 14 | "devDependencies": { 15 | "nodemon": "~1.3.7" 16 | }, 17 | "scripts": { 18 | "test": "echo \"Error: no test specified\" && exit 1", 19 | "start": "node server.js" 20 | }, 21 | "author": "", 22 | "license": "BSD-2-Clause" 23 | } 24 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure(2) do |config| 5 | config.vm.box = "ubuntu/trusty64" 6 | 7 | # config.vm.network "forwarded_port", guest: 80, host: 8080 8 | 9 | config.vm.provider "virtualbox" do |vb| 10 | # Customize the amount of memory on the VM: 11 | vb.memory = "2048" 12 | end 13 | 14 | config.vm.provision "shell", inline: <<-SHELL 15 | sudo apt-get update 16 | sudo apt-get install -y git nodejs npm exuberant-ctags python-pip 17 | sudo pip install virtualenvwrapper 18 | source /usr/local/bin/virtualenvwrapper.sh 19 | mkvirtualenv whereisit 20 | workon whereisit 21 | pip install -r tags-parser/requirements.txt 22 | SHELL 23 | end 24 | -------------------------------------------------------------------------------- /gitCloner/utils.js: -------------------------------------------------------------------------------- 1 | var log = require('./logging'); 2 | var shell = require('shelljs'); 3 | 4 | function defaultFailure() { 5 | return null; 6 | } 7 | 8 | function run_cmd(cmd, success, failure) { 9 | failure = failure || defaultFailure; 10 | 11 | var start = new Date(); 12 | var ret = shell.exec(cmd, {silent:true}); 13 | 14 | if (ret.code !== 0) { 15 | var end = new Date(); 16 | log.debug('event=run_command_fail message=' + ret.output); 17 | log.debug('command ' + cmd + ' took ' + ((end - start) / 1000) + 's') 18 | return failure(); 19 | } 20 | 21 | var end = new Date(); 22 | log.debug('event=run_command_success result=' + ret.output); 23 | log.debug('command ' + cmd + ' took ' + ((end - start) / 1000) + 's') 24 | return success(ret.output); 25 | } 26 | 27 | exports.run_cmd = run_cmd 28 | -------------------------------------------------------------------------------- /gitCloner/ctags.js: -------------------------------------------------------------------------------- 1 | var utils = require('./utils'); 2 | var emptyResult = []; 3 | 4 | function failure() { 5 | return []; 6 | } 7 | 8 | function success(commandOutput) { 9 | return JSON.parse(commandOutput); 10 | } 11 | 12 | exports.run = function(tagname, repopath) { 13 | script = './scripts/runctags'; 14 | 15 | // new lines cause the script to break 16 | // we'll just read until the first newline encountered 17 | tagname += '\n'; 18 | tagname = tagname.replace(/\n.*/, ''); 19 | 20 | cmd = [script, tagname, repopath].join(' '); 21 | return utils.run_cmd(cmd, success, failure) 22 | } 23 | 24 | exports.tag_file = function(filePath, repopath) { 25 | var script = './scripts/runctagsfile'; 26 | var codePath = [repopath, filePath].join('/'); 27 | var cmd = [script, codePath, repopath].join(' '); 28 | 29 | return utils.run_cmd(cmd, success, failure) 30 | } 31 | -------------------------------------------------------------------------------- /chrome-extension/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Where is it?", 4 | "version": "0.2.3", 5 | 6 | "description": "Whereisit is an extension that helps you to easily find the references on repositories in an online fashion.", 7 | "icons":{ 8 | "128": "img/logo128.png", 9 | "19": "img/logo19.png", 10 | "16": "img/logo16.png" 11 | }, 12 | 13 | "page_action": { 14 | "default_icon": "img/logo19.png", 15 | "default_popup": "popup.html" 16 | }, 17 | 18 | "background": { 19 | "scripts": [ 20 | "jquery-2.1.4.min.js", 21 | "shared.js", 22 | "utils/browser-action.js", 23 | "background.js", 24 | "context-menu.js"] 25 | }, 26 | 27 | "content_scripts":[ 28 | { 29 | "matches":["*://github.com/*"], 30 | "js": [ 31 | "jquery-2.1.4.min.js", 32 | "shared.js", 33 | "injected.js" 34 | ], 35 | "run_at": "document_end" 36 | } 37 | ], 38 | "homepage_url": "http://whereisit.io", 39 | "permissions": [ 40 | "contextMenus", 41 | "activeTab", 42 | "*://github.com/*", 43 | "http://localhost/", 44 | "http://45.33.82.241/" 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /gitCloner/cache.js: -------------------------------------------------------------------------------- 1 | var crypto = require('crypto'); 2 | var cache = {}; 3 | var timers = {}; 4 | 5 | var hashFn = function(url, snippet) { 6 | var shasum = crypto.createHash('sha1'); 7 | shasum.update(url); 8 | shasum.update(snippet); 9 | return shasum.digest('hex'); 10 | } 11 | 12 | exports.storeUrlSnippet = function(url, snippet, results) { 13 | var cacheKey = hashFn(url, snippet); 14 | cache[cacheKey] = results; 15 | timers[cacheKey] = new Date(); 16 | return results; 17 | } 18 | 19 | exports.getUrlSnippet = function(url, snippet) { 20 | var cacheKey = hashFn(url, snippet); 21 | inCache = cache[cacheKey]; 22 | return inCache === undefined ? null : isValid(cacheKey) ? inCache : null; 23 | } 24 | 25 | exports.storeFileTags = function(filePath, repoPath, results) { 26 | var cacheKey = hashFn(filePath, repoPath); 27 | cache[cacheKey] = results; 28 | timers[cacheKey] = new Date(); 29 | return results; 30 | } 31 | 32 | exports.getFileTags = function(filePath, repoPath, results) { 33 | var cacheKey = hashFn(filePath, repoPath); 34 | inCache = cache[cacheKey]; 35 | return inCache === undefined ? null : isValid(cacheKey) ? inCache : null; 36 | } 37 | 38 | FIVE_MINS_IN_MS = 5 * 60 * 1000; 39 | function isValid(cacheKey) { 40 | var timeSet = timers[cacheKey]; 41 | if (timeSet === undefined) return false; 42 | var now = new Date(); 43 | var msPassed = now - timeSet; 44 | return msPassed < FIVE_MINS_IN_MS; 45 | } 46 | -------------------------------------------------------------------------------- /gitCloner/getGit.js: -------------------------------------------------------------------------------- 1 | var shell = require('shelljs'); 2 | var log = require('./logging'); 3 | var utils = require('./utils'); 4 | 5 | // file should be run using scripts/runserver from root dir 6 | var reposDir = 'repos'; 7 | var lastCheck = {}; 8 | 9 | function wasRecentlyPulled(repoUrl) { 10 | var timeNow = Date.now() / 1000 | 0; 11 | if (lastCheck.hasOwnProperty(repoUrl)) { 12 | if ((timeNow - lastCheck[repoUrl]) < 30) { 13 | log.debug('event=git_cached'); 14 | return true; 15 | } 16 | } 17 | lastCheck[repoUrl] = timeNow; 18 | return false; 19 | } 20 | 21 | exports.cloneFromGit = function(url) { 22 | var splits = url.split('/'); 23 | var user = splits[3]; 24 | var repo = splits[4]; 25 | var repoUrl = 'git:/' + splits.splice(1, 4).join('/'); 26 | 27 | repoPath = [reposDir, user, repo].join('/'); 28 | 29 | if (wasRecentlyPulled(repoUrl)) { 30 | return repoPath; 31 | } 32 | 33 | var dirExists = shell.test('-e', repoPath); 34 | var result; 35 | 36 | if (dirExists) { 37 | log.debug('event=git_directory_exists') 38 | oldDir = shell.pwd(); 39 | shell.cd(repoPath); 40 | 41 | // in case of previously failed pull, reset it 42 | var cmd = 'git reset --hard'; 43 | utils.run_cmd(cmd, function() { return; }) 44 | 45 | var cmd = 'git pull -s recursive --rebase=preserve'; 46 | result = utils.run_cmd(cmd, function() { return repoPath;}) 47 | shell.cd(oldDir); 48 | } else { 49 | log.debug('event=git_need_to_clone') 50 | shell.mkdir('-p', repoPath); 51 | var cmd = 'git clone --depth 1 ' + repoUrl + ' ' + repoPath; 52 | result = utils.run_cmd(cmd, function() { return repoPath;}) 53 | } 54 | return result; 55 | } 56 | 57 | 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # whereisit 2 | Sometimes finding references on GitHub is frustrating. Not anymore. whereisit is an extension that helps you to easily find the references on repositories in an online fashion. Just select the function or declaration and whereisit is going to help finding the right stuff. Piece of cake. 3 | 4 | whereisit is a hack developed during the Angelhack Brooklyn 2015, by @celsopalmeiraneto, @danielcodes, @filfrey, @Anomalous1, @ngzhian 5 | 6 | ## How to get it 7 | This has been released as a [chrome extension](https://chrome.google.com/webstore/detail/where-is-it/cdgnplmebagbialenimejpokfcodlkdm). Things may be slow at times or really buggy, especially if you are using the extension on a repo for the first time. 8 | 9 | If it takes too long, please try the extension on these repositories: 10 | 11 | 1. [django/django](https://github.com/django/django/) 12 | 2. [danielcodes/Algorithms](https://github.com/danielcodes/Algorithms/) 13 | 3. [cocos2d/cocos2d-x](https://github.com/cocos2d/cocos2d-x/) 14 | 4. [jquery/jquery](https://github.com/jquery/jquery) 15 | 5. [jekyll/jekyll](https://github.com/jekyll/jekyll) 16 | 17 | ## Running this locally 18 | You'll need `nodejs`, `python`, [`ctags`](http://ctags.sourceforge.net/) installed. 19 | 20 | 1. `cd` into gitCloner, and run `npm install`, this should get the backend node server running 21 | 2. install `ctags` on your machine, usually it's `sudo apt-get install exuberant-ctags` 22 | 3. inside the main `whereisit` folder, start the server using `./scripts/runserver` 23 | 4. install the chrome extension (you need to be in developer mode for chrome) 24 | 5. navigate to your favourite repository on GitHub and try it out! 25 | 26 | [![Join the chat at https://gitter.im/ngzhian/whereisit](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ngzhian/whereisit?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 27 | where is it? 28 | -------------------------------------------------------------------------------- /chrome-extension/background.js: -------------------------------------------------------------------------------- 1 | PROD = "http://45.33.82.241:8880"; 2 | DEV = "http://localhost:8080"; 3 | SERVER_URL = PROD; 4 | 5 | function findstuff(data) { 6 | var userSelection = data.text; 7 | var location = data.location; 8 | var success = data.callback; 9 | 10 | var url = SERVER_URL + (data.ff ? '/file' : ''); 11 | console.log(url); 12 | 13 | sendRequest({ 14 | url: url, 15 | snippet: userSelection, 16 | location: location, 17 | }, success, function() {}); 18 | } 19 | 20 | /* Make the ajax call to server */ 21 | function sendRequest(data, success, failure) { 22 | $.ajax({ 23 | url: data.url, 24 | method: 'POST', 25 | data: JSON.stringify({'snippet': data.snippet, 'url': data.location}), 26 | contentType: 'application/json; charset=utf-8' 27 | }).done(function(resp) { 28 | success(resp); 29 | }).fail(function(resp) { 30 | failure(resp); 31 | }) 32 | } 33 | 34 | chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) { 35 | if (message.type === 'FINDIT' && message.location) { 36 | // called from page load (injected script) 37 | var tabId = sender.tab.id; 38 | setLoadingState(tabId); 39 | message.tabId = tabId; 40 | pageFindIt(message, sendResponse); 41 | setNormalState(tabId); 42 | } else if (message.type === 'CONTEXT_MENU') { 43 | // called from user right click on context menu 44 | contextMenu(message, sendResponse); 45 | } else if (message.type === 'PAGE_ACTION') { 46 | // called from user click on page action 47 | pageAction(message, sendResponse); 48 | } 49 | 50 | // This function becomes invalid when the event listener returns, 51 | // unless you return true from the event listener to indicate you 52 | // wish to send a response asynchronously (this will keep the 53 | // message channel open to the other end until sendResponse is called). 54 | // https://developer.chrome.com/extensions/runtime#event-onMessage 55 | return true; 56 | }); 57 | 58 | function pageAction(message, callback) { 59 | message.callback = callback; 60 | findstuff(message) 61 | } 62 | 63 | function contextMenu(message, callback) { 64 | message.callback = callback; 65 | findstuff(message) 66 | } 67 | 68 | function pageFindIt(message, callback) { 69 | message.ff = true; 70 | message.callback = callback; 71 | findstuff(message); 72 | } 73 | -------------------------------------------------------------------------------- /gitCloner/server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var bodyParser = require('body-parser'); 3 | var app = express(); 4 | 5 | app.use(bodyParser.urlencoded({ extended: false })) 6 | app.use(bodyParser.json()) 7 | 8 | var getGit = require("./getGit"); 9 | var ctags = require("./ctags"); 10 | var cache = require('./cache'); 11 | 12 | var log = require('./logging'); 13 | 14 | function urlParam(req, res, next) { 15 | url = req.body.url; 16 | if (url === null) return res.sendStatus(404); 17 | else next(); 18 | } 19 | 20 | function snippetParam(req, res, next) { 21 | url = req.body.url; 22 | snippet = req.body.snippet; 23 | if (snippet === null) return res.sendStatus(404); 24 | else next(); 25 | } 26 | 27 | app.post('/file', urlParam, function(req,res){ 28 | log.debug('event=connected'); 29 | url = req.body.url; 30 | var repoPath = getGit.cloneFromGit(url); 31 | if (repoPath == null) { 32 | return res.sendStatus(404); 33 | } 34 | 35 | var p = url.split('/'); 36 | // this will fail if we have a branch like "stable/1.8" 37 | filePath = p.slice(7).join('/'); 38 | 39 | var results = cache.getFileTags(filePath, repoPath); 40 | if (results) { 41 | log.debug('event=web_server_cache'); 42 | } else { 43 | results = ctags.tag_file(filePath, repoPath); 44 | cache.storeFileTags(filePath, repoPath, results); 45 | } 46 | 47 | log.debug('server responsed with ' + results.length + ' results.'); 48 | return res.json(results); 49 | }); 50 | 51 | app.post('/', urlParam, snippetParam, function(req,res){ 52 | log.debug('event=connected'); 53 | url = req.body.url; 54 | snippet = req.body.snippet; 55 | log.debug('event=query snippet=' + snippet + ' url=' + url); 56 | 57 | var repoPath = getGit.cloneFromGit(url); 58 | if (repoPath == null) { 59 | return res.sendStatus(404); 60 | } 61 | 62 | var results; 63 | if (url && snippet) { 64 | results = cache.getUrlSnippet(url, snippet); 65 | if (results) { 66 | log.debug('event=web_server_cache'); 67 | } 68 | results = ctags.run(snippet, repoPath); 69 | cache.storeUrlSnippet(url, snippet, results); 70 | } else { 71 | return res.json(); 72 | } 73 | 74 | return res.json(results); 75 | }); 76 | 77 | app.post('/clone', function(req, res) { 78 | var url = req.body.url; 79 | 80 | if (url == null) { 81 | return res.sendStatus(404); 82 | } 83 | 84 | log.debug('event=clone url=' + url); 85 | 86 | var repoPath = getGit.cloneFromGit(url); 87 | 88 | if (repoPath == null) { 89 | return res.sendStatus(404); 90 | } 91 | 92 | return res.sendStatus(200); 93 | }) 94 | 95 | 96 | var server = app.listen(8880, function(){ 97 | }); 98 | -------------------------------------------------------------------------------- /chrome-extension/popup.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Triggered when user clicks on the action button 3 | */ 4 | document.addEventListener('DOMContentLoaded', function() { 5 | // the background page can be loaded in 2 ways, 6 | // 1. when the user right clicks on a selection, 7 | // 2. when the user clicks on the toolbar button 8 | // the way we differentiate is that in case 1. 9 | // we set the search string to the url where the user right clicked. 10 | var search = window.location.search.substring(1); 11 | if (search) { 12 | // a result of a right click from user 13 | contextMenuAction(search) 14 | } else { 15 | // user clicks on the action in the toolbar 16 | toolbarButtonAction() 17 | } 18 | }); 19 | 20 | /** 21 | * Called when the user right clicks a selection on the page 22 | * and clicks on the link to jump straight to the definition. 23 | * We store the user selection snippet and the url of the window 24 | * in the search parameters. 25 | */ 26 | function contextMenuAction(search) { 27 | var splits = search.split('&', 2); 28 | var selection = splits[0].substring('selection'.length + 1); 29 | var location = splits[1].substring('location'.length + 1); 30 | 31 | chrome.runtime.sendMessage({ 32 | type: 'CONTEXT_MENU', 33 | text: selection, 34 | location: location 35 | }, function(results) { 36 | var result = results[0]; 37 | var url = makeLink(result.filepath, location, result.linenum); 38 | window.location.href = url; 39 | }); 40 | } 41 | 42 | /** 43 | * Called when the user clicks on the toolbar button. 44 | * This sends a message to the current tab to 45 | * trigger a series of callbacks which basically 46 | * 1. gets the user selected text 47 | * 2. queries the server for the results 48 | * 3. updates the background page of the toolbar button 49 | */ 50 | function toolbarButtonAction() { 51 | chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { 52 | var tab = tabs[0]; 53 | // send message to get user selection 54 | // expect to get back an object with the user selection 55 | // and the location of the page 56 | chrome.tabs.sendMessage( 57 | tab.id, {type: 'SELECTION_AND_LOCATION'}, triggerPageAction); 58 | }); 59 | } 60 | 61 | function triggerPageAction(message) { 62 | var userSelection = message.text; 63 | var location = message.location; 64 | 65 | if (!userSelection || ! location) return; 66 | 67 | $("#spinner").show(); 68 | chrome.runtime.sendMessage({ 69 | type: 'PAGE_ACTION', 70 | text: userSelection, 71 | location: location 72 | }, function(results) { 73 | addToDom(results, location, userSelection); 74 | }); 75 | } 76 | 77 | function addToDom(results, location, userSelection) { 78 | hideInfo(); 79 | var $status = $('#status'); 80 | $status.find('#search').text(userSelection); 81 | html = results.forEach(function(v) { 82 | $status.append(makeResultHtml(v, location)); 83 | }); 84 | $("#title").show(); 85 | } 86 | 87 | function makeResultHtml(v, location) { 88 | var $template = $(resultTemplate); 89 | var link = makeLink(v.filepath, location, v.linenum); 90 | $template.find('.link') 91 | .attr('href', link) 92 | .text(v.filepath + '(line ' + v.linenum + ')'); 93 | $template.find('.snippet pre') 94 | .text(v.exerpt); 95 | return $template; 96 | } 97 | 98 | var resultTemplate = '
' + 99 | '
' + 100 | '' + 101 | '
' + 102 | '
' + 103 | '
' +
104 |     '
' + 105 | '
'; 106 | 107 | function hideInfo() { 108 | $("#title").hide(); 109 | $("#selSom").hide(); 110 | $("#spinner").hide(); 111 | } 112 | -------------------------------------------------------------------------------- /chrome-extension/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Where is it 5 | 69 | 70 | 71 |
72 | 73 | Loading... 74 |
75 |
76 |
77 |

78 | whereisit 79 |

80 |

Looks up code definition so you don't have to!

81 |

Highlight a piece of code you would like to lookup the definition for, and click this button again

82 | 83 | 87 | 88 |

User reviews (may be fake)

89 |
90 | Works like magic... 91 |
92 |
93 | I didn't know where this method was defined, now I do! 94 |
95 |
96 | It's like IntelliSense, but dumber. 97 |
98 |
99 |
A hack for AngelHack Brooklyn 2015 by
100 |

101 |

102 |
    103 |
  • 104 | 105 |
    106 | Celso 107 |
    108 |
  • 109 |
  • 110 | 111 |
    112 | Daniel 113 |
    114 |
  • 115 |
  • 116 | 117 |
    118 | Jeff 119 |
    120 |
  • 121 |
  • 122 | 123 |
    124 | Vincent 125 |
    126 |
  • 127 |
  • 128 | 129 |
    130 | Zhi An 131 |
    132 |
  • 133 |
134 |
135 |
136 | 139 |
results for 140 | 141 |
142 |
143 | 144 | 145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /tags-parser/sample-tags: -------------------------------------------------------------------------------- 1 | !_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ 2 | !_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ 3 | !_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/ 4 | !_TAG_PROGRAM_NAME Exuberant Ctags // 5 | !_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ 6 | !_TAG_PROGRAM_VERSION 5.9~svn20110310 // 7 | $.addFlex Flexigrid/js/flexigrid.js 69;" f 8 | $.fn.disableSelection Flexigrid/js/flexigrid.js 62;" f 9 | $.fn.flexAddData Flexigrid/js/flexigrid.js 1516;" f 10 | $.fn.flexOptions Flexigrid/js/flexigrid.js 1506;" f 11 | $.fn.flexReload Flexigrid/js/flexigrid.js 1501;" f 12 | $.fn.flexSearch Flexigrid/js/flexigrid.js 1545;" f 13 | $.fn.flexToggleCol Flexigrid/js/flexigrid.js 1511;" f 14 | $.fn.flexigrid Flexigrid/js/flexigrid.js 1488;" f 15 | $.fn.noSelect Flexigrid/js/flexigrid.js 1521;" f 16 | $.fn.selectedRows Flexigrid/js/flexigrid.js 1548;" f 17 | Example4 Flexigrid/demo/index.html 425;" f 18 | Example4 Flexigrid/demo/index.html 647;" f 19 | Tag tags-parser/parser.py 10;" c 20 | __init__ tags-parser/parser.py 11;" m class:Tag 21 | __repr__ tags-parser/parser.py 21;" m class:Tag file: 22 | __str__ tags-parser/parser.py 18;" m class:Tag file: 23 | active_group Flexigrid/demo/post-json.php 23;" v 24 | active_group Flexigrid/demo/post-xml.php 23;" v 25 | addFormData Flexigrid/demo/sample.html 62;" f 26 | base_url Flexigrid/demo/post-json.php 25;" v 27 | base_url Flexigrid/demo/post-xml.php 25;" v 28 | cb Flexigrid/js/flexigrid.js 347;" v 29 | connect Flexigrid/demo/post-json.php 28;" v 30 | connect Flexigrid/demo/post-xml.php 28;" v 31 | countRec Flexigrid/demo/post-json.php 36;" f 32 | countRec Flexigrid/demo/post-xml.php 36;" f 33 | db Flexigrid/demo/post-json.php 29;" v 34 | db Flexigrid/demo/post-xml.php 29;" v 35 | diff Flexigrid/js/flexigrid.js 253;" v 36 | docloaded Flexigrid/js/flexigrid.js 1485;" v 37 | entry Flexigrid/demo/example4.php 65;" v 38 | entry Flexigrid/demo/post-json.php 85;" v 39 | get_snippet tags-parser/parser.py 52;" f 40 | gh Flexigrid/js/flexigrid.js 1380;" v 41 | gtop Flexigrid/js/flexigrid.js 1381;" v 42 | h Flexigrid/js/flexigrid.js 215;" p class:this.vresize 43 | hrH Flexigrid/js/flexigrid.js 187;" v 44 | hset Flexigrid/js/flexigrid.js 145;" p class:$.addFlex.g 45 | jsonData Flexigrid/demo/example4.php 61;" v 46 | jsonData Flexigrid/demo/post-json.php 81;" v 47 | left Flexigrid/js/flexigrid.js 762;" v 48 | limit Flexigrid/demo/post-json.php 47;" v 49 | limit Flexigrid/demo/post-xml.php 58;" v 50 | lookup_tagname tags-parser/parser.py 41;" f 51 | main tags-parser/parser.py 58;" f 52 | make_tags tags-parser/parser.py 27;" f 53 | matched Flexigrid/js/flexigrid.js 32;" v 54 | n Flexigrid/js/flexigrid.js 197;" v 55 | n Flexigrid/js/flexigrid.js 346;" v 56 | n Flexigrid/js/flexigrid.js 863;" v 57 | n Flexigrid/js/flexigrid.js 891;" p class:colresize 58 | nd Flexigrid/js/flexigrid.js 180;" v 59 | nw Flexigrid/js/flexigrid.js 890;" p class:colresize 60 | ow Flexigrid/js/flexigrid.js 198;" v 61 | page Flexigrid/demo/example4.php 5;" v 62 | page Flexigrid/demo/post-json.php 2;" v 63 | page Flexigrid/demo/post-xml.php 2;" v 64 | page Flexigrid/demo/post-xml.php 43;" v 65 | pdl Flexigrid/js/flexigrid.js 763;" v 66 | pdt Flexigrid/js/flexigrid.js 764;" v 67 | pht Flexigrid/js/flexigrid.js 758;" v 68 | pwt Flexigrid/js/flexigrid.js 760;" v 69 | qtype Flexigrid/demo/example4.php 10;" v 70 | qtype Flexigrid/demo/post-json.php 7;" v 71 | qtype Flexigrid/demo/post-xml.php 7;" v 72 | query Flexigrid/demo/example4.php 9;" v 73 | query Flexigrid/demo/post-json.php 60;" v 74 | query Flexigrid/demo/post-json.php 6;" v 75 | query Flexigrid/demo/post-xml.php 6;" v 76 | query Flexigrid/demo/post-xml.php 74;" v 77 | r1 Flexigrid/js/flexigrid.js 589;" v 78 | r2 Flexigrid/js/flexigrid.js 590;" v 79 | result Flexigrid/demo/post-json.php 31;" v 80 | result Flexigrid/demo/post-json.php 38;" v 81 | result Flexigrid/demo/post-json.php 53;" v 82 | result Flexigrid/demo/post-xml.php 31;" v 83 | result Flexigrid/demo/post-xml.php 38;" v 84 | result Flexigrid/demo/post-xml.php 63;" v 85 | rows Flexigrid/demo/countryArray.inc.php 2;" v 86 | rows Flexigrid/demo/example4.php 15;" v 87 | rows Flexigrid/demo/example4.php 27;" v 88 | rows Flexigrid/demo/example4.php 41;" v 89 | rows Flexigrid/demo/example4.php 48;" v 90 | rows Flexigrid/demo/post-json.php 78;" v 91 | rows Flexigrid/demo/post-xml.php 66;" v 92 | rows Flexigrid/demo/post-xml.php 92;" v 93 | rp Flexigrid/demo/example4.php 6;" v 94 | rp Flexigrid/demo/post-json.php 3;" v 95 | rp Flexigrid/demo/post-xml.php 3;" v 96 | rp Flexigrid/demo/post-xml.php 44;" v 97 | runSQL Flexigrid/demo/post-json.php 11;" f 98 | runSQL Flexigrid/demo/post-xml.php 11;" f 99 | sample_output tags-parser/parser.py 24;" f 100 | sort Flexigrid/demo/post-json.php 44;" v 101 | sort Flexigrid/demo/post-xml.php 51;" v 102 | sortArray Flexigrid/demo/post-json.php 68;" v 103 | sortArray Flexigrid/demo/post-xml.php 82;" v 104 | sortMethod Flexigrid/demo/post-json.php 72;" v 105 | sortMethod Flexigrid/demo/post-json.php 74;" v 106 | sortMethod Flexigrid/demo/post-xml.php 86;" v 107 | sortMethod Flexigrid/demo/post-xml.php 88;" v 108 | sortname Flexigrid/demo/example4.php 7;" v 109 | sortname Flexigrid/demo/post-json.php 4;" v 110 | sortname Flexigrid/demo/post-xml.php 45;" v 111 | sortname Flexigrid/demo/post-xml.php 4;" v 112 | sortorder Flexigrid/demo/example4.php 8;" v 113 | sortorder Flexigrid/demo/post-json.php 5;" v 114 | sortorder Flexigrid/demo/post-xml.php 46;" v 115 | sortorder Flexigrid/demo/post-xml.php 5;" v 116 | sql Flexigrid/demo/post-json.php 37;" v 117 | sql Flexigrid/demo/post-json.php 52;" v 118 | sql Flexigrid/demo/post-xml.php 37;" v 119 | sql Flexigrid/demo/post-xml.php 62;" v 120 | start Flexigrid/demo/post-json.php 45;" v 121 | start Flexigrid/demo/post-xml.php 56;" v 122 | startX Flexigrid/js/flexigrid.js 201;" p class:colresize 123 | stat Flexigrid/js/flexigrid.js 594;" v 124 | tbody Flexigrid/js/flexigrid.js 437;" v 125 | test Flexigrid/demo/index.html 330;" f 126 | test Flexigrid/demo/index.html 563;" f 127 | thead Flexigrid/js/flexigrid.js 1076;" v 128 | top Flexigrid/js/flexigrid.js 761;" v 129 | total Flexigrid/demo/post-json.php 55;" v 130 | total Flexigrid/demo/post-json.php 77;" v 131 | total Flexigrid/demo/post-xml.php 65;" v 132 | total Flexigrid/demo/post-xml.php 91;" v 133 | uaMatch Flexigrid/js/flexigrid.js 16;" f function:function 134 | usingSQL Flexigrid/demo/post-json.php 10;" v 135 | usingSQL Flexigrid/demo/post-xml.php 10;" v 136 | where Flexigrid/demo/post-json.php 49;" v 137 | where Flexigrid/demo/post-xml.php 59;" v 138 | wt Flexigrid/js/flexigrid.js 759;" v 139 | xml Flexigrid/demo/post-xml.php 96;" v 140 | -------------------------------------------------------------------------------- /tags-parser/parser.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # AngelHack Brooklyn 2015 3 | import os 4 | import json 5 | import re 6 | import subprocess 7 | import sys 8 | 9 | from textwrap import dedent 10 | 11 | import click 12 | 13 | 14 | SAMPLE_CTAGS_OUTPUT = dedent("""\ 15 | !_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ 16 | !_TAG_PROGRAM_VERSION 5.9~svn20110310 // 17 | $.addFlex Flexigrid/js/flexigrid.js 69;" f 18 | $.addFlex Flexigrid/js/flexigrid.js 75;" f 19 | """).split('\n') 20 | 21 | 22 | class Tag(object): 23 | def __init__(self, *args): 24 | """ 25 | >>> name = 'repos/danielcodes/Algorithms/Seven/Graph.java' 26 | >>> tag = Tag(name, name, '4;"', 'm', 'class:Graph') 27 | >>> tag.kind 28 | 'method' 29 | >>> tag.member_of 30 | 'class:Graph' 31 | >>> tag.member_of_kind 32 | 'class' 33 | >>> tag.member_of_name 34 | 'Graph' 35 | """ 36 | if len(args) < 3: 37 | raise Exception('bad args: %s' % list(args)) 38 | self.tagname = args[0] 39 | self.tagfile = args[1] 40 | self.tagaddress = args[2] 41 | self.filepath = '/'.join(self.tagfile.split('/')[3:]) 42 | self.linenum = int(self.tagaddress.split(';')[0]) 43 | self.kind = None 44 | self.member_of = None 45 | self.member_of_kind = None 46 | self.member_of_name = None 47 | self.parse_tagfields(*args[3:]) 48 | 49 | def parse_tagfields(self, *args): 50 | for arg in args: 51 | if not arg: 52 | continue 53 | arg = arg.strip() 54 | if arg == 'm': 55 | self.kind = 'method' 56 | elif arg == 'c': 57 | self.kind = 'class' 58 | elif arg.startswith('class:'): 59 | self.member_of = arg 60 | kind, name = arg.split(':', 1) 61 | self.member_of_kind = kind 62 | self.member_of_name = name 63 | 64 | def read_snippet(self): 65 | """A tag has information on the filename, 66 | and also the line where this tag can be found. 67 | 68 | >>> name = 'repos/danielcodes/Algorithms/Seven/Graph.java' 69 | >>> tag = Tag(name, name, '4;"') 70 | """ 71 | path = self.tagfile 72 | exerpt = [] 73 | line = '' 74 | with open(path, 'r') as f: 75 | for i in range(self.linenum): 76 | line = f.readline() 77 | exerpt.append(line) 78 | for i in range(5): 79 | l = f.readline() 80 | if l == '': 81 | break 82 | exerpt.append(l) 83 | 84 | # this snippet is only set after tag has been populated 85 | self.snippet = line[:].strip() 86 | self.exerpt = ''.join(exerpt) 87 | 88 | def to_json(self): 89 | """ 90 | >>> name = 'repos/danielcodes/Algorithms/Seven/Graph.java' 91 | >>> tag = Tag(name, name, '4;"') 92 | """ 93 | # impt, ensure that file is read 94 | if not hasattr(self, 'snippet'): 95 | self.read_snippet() 96 | 97 | obj = {} 98 | for field in ['tagname', 'filepath', 'linenum', 'kind', 'snippet', 99 | 'member_of', 'member_of_kind', 'member_of_name', 'exerpt']: 100 | obj[field] = getattr(self, field) 101 | return obj 102 | 103 | def is_of_filetype(self, filetype): 104 | if filetype is None: 105 | return True 106 | return self.filepath and self.filepath.endswith(filetype) 107 | 108 | def __str__(self): 109 | return '(%s)(%s)(%s)' % (self.tagname, self.tagfile, self.tagaddress) 110 | 111 | def __repr__(self): 112 | return str(self) 113 | 114 | 115 | class TagEncoder(json.JSONEncoder): 116 | def default(self, obj): 117 | if isinstance(obj, Tag): 118 | return obj.to_json() 119 | return json.JSONEncoder.default(self, obj) 120 | 121 | 122 | def make_tags(lines): 123 | """Takes in lines of tags file output, 124 | returns list of Tag objects 125 | 126 | >>> lines = SAMPLE_CTAGS_OUTPUT 127 | >>> results = list(make_tags(lines)) 128 | >>> results[0] 129 | ($.addFlex)(Flexigrid/js/flexigrid.js)(69;") 130 | >>> results[1] 131 | ($.addFlex)(Flexigrid/js/flexigrid.js)(75;") 132 | """ 133 | for line in lines: 134 | if not line or line[0] == '!': 135 | continue 136 | parts = line.split('\t') 137 | yield Tag(*parts) 138 | 139 | 140 | def lookup_tagname(tagname, tags, filetype=None): 141 | """Looks up a tag within a list of Tags via the tagname 142 | 143 | >>> tags = make_tags(SAMPLE_CTAGS_OUTPUT) 144 | >>> results = list(lookup_tagname('$.addFlex', tags)) 145 | >>> results[0] 146 | ($.addFlex)(Flexigrid/js/flexigrid.js)(69;") 147 | >>> results[1] 148 | ($.addFlex)(Flexigrid/js/flexigrid.js)(75;") 149 | >>> results = list(lookup_tagname('$.addFlex', tags, 'java')) 150 | >>> results 151 | [] 152 | """ 153 | for tag in tags: 154 | if tag.tagname == tagname and tag.is_of_filetype(filetype): 155 | yield tag 156 | 157 | 158 | def gen_ctags(repo_path): 159 | import time 160 | tags_path = os.path.join(repo_path, 'tags') 161 | if os.path.exists(tags_path): 162 | mtime = os.path.getmtime(tags_path) 163 | time_now = time.time() 164 | if time_now - mtime < (60 * 10): 165 | # was modified in the last 10 mins 166 | return tags_path 167 | with open('error.log', 'a') as f: 168 | subprocess.check_output( 169 | ['ctags', '-f', tags_path, '-R', '--excmd=number', repo_path], 170 | stderr=f) 171 | return tags_path 172 | 173 | 174 | @click.command() 175 | @click.option('--tag_name', default=None, help='Tag name to lookup') 176 | @click.option('--code_file', default=None, help='Tag names of this file') 177 | @click.option('--repo_path', default=None, help='Path to repo') 178 | def main(tag_name, code_file, repo_path): 179 | if not repo_path: 180 | return 181 | 182 | tags_path = gen_ctags(repo_path) 183 | tags = make_tags(open(tags_path)) 184 | 185 | # looking for a single tag name 186 | if tag_name: 187 | candidates = list(lookup_tagname(tag_name, tags)) 188 | print json.dumps(candidates, cls=TagEncoder) 189 | 190 | # looking for a all tags in a particular file 191 | if code_file: 192 | names = get_names_from_file(code_file) 193 | tags_dict = {tag.tagname: tag for tag in tags} 194 | tags_in_file = [] 195 | for name in names: 196 | tag = tags_dict.get(name['name'], None) 197 | if tag and tag_and_file_same_extension(tag, code_file): 198 | tags_in_file.append(tag) 199 | print json.dumps(tags_in_file, cls=TagEncoder) 200 | 201 | 202 | def tag_and_file_same_extension(tag, code_file): 203 | return extension(code_file) == extension(tag.filepath) 204 | 205 | def extension(name): 206 | splits = name.rsplit('.') 207 | if len(splits) >= 2: 208 | return splits[-1] 209 | return name 210 | 211 | def get_names_from_file(code_file): 212 | results = [] 213 | with open(code_file) as f: 214 | for i, line in enumerate(f): 215 | parts = filter(bool, re.split(r'[\W\(\,)\.,]+', line)) 216 | for name in parts: 217 | index = line.index(name) 218 | results.append({ 219 | 'name': name, 220 | 'line': line, 221 | 'start': index, 222 | 'end': index - 1 + len(name) 223 | }) 224 | return results 225 | 226 | 227 | if __name__ == '__main__': 228 | # import doctest 229 | # doctest.testmod() 230 | main() 231 | -------------------------------------------------------------------------------- /chrome-extension/injected.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Generic traversal function for the DOM 3 | * Starts at startNode and goes depth first 4 | * matches is a check before calling callback 5 | */ 6 | var traverseText = function(startNode, matches, callback) { 7 | matches = matches || function() { return true; }; 8 | var nextSibling; 9 | for (var child = startNode.firstChild; child !== null; ) { 10 | // need this because mutating the dom causes this to enter infinite loop 11 | nextSibling = child.nextSibling; 12 | if (child.nodeType === 3) { 13 | if (matches(child)) { 14 | callback(child); 15 | } 16 | } else { 17 | traverseText(child, matches, callback); 18 | } 19 | child = nextSibling; 20 | } 21 | return startNode; 22 | } 23 | 24 | var traverseMethodCalls = function(startNode, isMethod, callback) { 25 | traverseText(startNode, isMethod, callback); 26 | } 27 | 28 | function Python() { 29 | var methodCallRegex = /\.?\w+\(/g; 30 | var methodReplaceRegex = /[.(]/g; 31 | 32 | function matchMethodCalls(str) { 33 | return str.match(methodCallRegex) || []; 34 | } 35 | 36 | function stripMethodCall(str) { 37 | return str.replace(methodReplaceRegex, '') 38 | } 39 | 40 | function isMethod(node) { 41 | match = node.data.match(/\.?\w+\(/); 42 | hasClass = node.parentElement.classList.contains('pl-c1'); 43 | return (match && match.length > 0) || hasClass; 44 | } 45 | 46 | return { 47 | name: 'Python', 48 | matchMethodCalls: matchMethodCalls, 49 | stripMethodCall: stripMethodCall, 50 | isMethod: isMethod 51 | } 52 | } 53 | 54 | function JavaScript() { 55 | var methodCallRegex = /\.?\w+\(/g; 56 | var methodReplaceRegex = /[.(]/g; 57 | 58 | function matchMethodCalls(str) { 59 | return str.match(methodCallRegex) || [str]; 60 | } 61 | 62 | function stripMethodCall(str) { 63 | return str.replace(methodReplaceRegex, '') 64 | } 65 | 66 | function isMethod(node) { 67 | match = node.data.match(/\.?\w+\(/); 68 | hasClass = node.parentElement.classList.contains('pl-c1'); 69 | return hasClass || match; 70 | } 71 | 72 | return { 73 | name: 'JavaScript', 74 | matchMethodCalls: matchMethodCalls, 75 | stripMethodCall: stripMethodCall, 76 | isMethod: isMethod 77 | } 78 | } 79 | 80 | function Java() { 81 | var methodCallRegex = /\.?\w+\(/g; 82 | var methodReplaceRegex = /[.(]/g; 83 | 84 | function matchMethodCalls(str) { 85 | // method calls are usually a text node with x.method() 86 | // or when there is no parenthesis, it's a class constructor 87 | return str.match(methodCallRegex) || [str]; 88 | } 89 | 90 | function stripMethodCall(str) { 91 | return str.replace(methodReplaceRegex, '') 92 | } 93 | 94 | function isMethod(node) { 95 | index = node.data.search(methodCallRegex); 96 | hasClass = node.parentElement.classList.contains('pl-smi'); 97 | return hasClass || (index >= 0); 98 | } 99 | 100 | return { 101 | name: 'Java', 102 | matchMethodCalls: matchMethodCalls, 103 | stripMethodCall: stripMethodCall, 104 | isMethod: isMethod 105 | } 106 | } 107 | 108 | function guessFileExtension(filename) { 109 | // to add a new language, 110 | // add a new key-value map to mapping 111 | var splits = filename.split('.'); 112 | var defaultLanguage = JavaScript; 113 | var mapping = { 114 | 'py': Python, 115 | 'js': JavaScript, 116 | 'java': Java 117 | } 118 | 119 | if (splits.length > 0) { 120 | var ext = splits[splits.length - 1]; 121 | if (mapping.hasOwnProperty(ext)) { 122 | return mapping[ext]; 123 | } 124 | } 125 | return defaultLanguage; 126 | } 127 | 128 | function linkUpMethods(msg_tags) { 129 | /* Breaks the textNode into 3, before-regex, regex-matched, after-regex. 130 | * the regex-matched portion will be wrapped in a span of class 'wii' 131 | * returns the span that was created to wrap the matched word 132 | */ 133 | function breakTextAt(textNode, regex) { 134 | var span; 135 | textNode.data.replace(regex, function(matched) { 136 | var args = [].slice.call(arguments); 137 | var offset = args[args.length - 2]; 138 | var newTextNode = textNode.splitText(offset); 139 | newTextNode.data = newTextNode.data.substr(matched.length); 140 | span = document.createElement('span'); 141 | span.className = 'wii'; 142 | span.textContent = matched; 143 | textNode.parentNode.insertBefore(span, newTextNode); 144 | }) 145 | return span; 146 | } 147 | 148 | function linkTag(tag, textNode) { 149 | var url = makeLink(tag.filepath, window.location.href, tag.linenum); 150 | var span = breakTextAt( 151 | textNode, new RegExp("\\b" + tag.tagname + '\\b')); 152 | if (!span) return; // TODO(ngzhian) method call in method call on same line 153 | var a = makeAhref(url, span.firstChild.data); 154 | span.replaceChild(a, span.firstChild); 155 | } 156 | 157 | function makeAhref(href, textContent) { 158 | var a = document.createElement('a'); 159 | a.href = href; 160 | a.style.textDecoration = 'underline'; 161 | a.textContent = textContent; 162 | return a; 163 | } 164 | 165 | var lang = guessFileExtension(window.location.pathname)(); 166 | console.log('loaded ' + lang.name); 167 | 168 | function linkTextNodeToName(textNode, maybeName) { 169 | maybeName = lang.stripMethodCall(maybeName); 170 | tag = msg_tags[maybeName]; 171 | if (tag) { linkTag(tag, textNode); } 172 | } 173 | 174 | function cleanNameAndLink(textNode) { 175 | matches = lang.matchMethodCalls(textNode.data); 176 | matches.forEach(function(match) { linkTextNodeToName(textNode, match); }) 177 | } 178 | 179 | var codeContainer = document.getElementsByClassName('blob-wrapper')[0]; 180 | 181 | traverseMethodCalls( 182 | codeContainer, 183 | lang.isMethod, 184 | cleanNameAndLink 185 | ) 186 | } 187 | 188 | function getSelectedText() { 189 | var text = ""; 190 | if (window.getSelection) { 191 | text = window.getSelection().toString(); 192 | } else if (document.selection && document.selection.type != "Control") { 193 | text = document.selection.createRange().text; 194 | } 195 | return text.trim(); 196 | } 197 | 198 | function isGitHubUrl(url) { 199 | var ORIGIN_RE = /https?:\/\/github.com/; 200 | return url.search(ORIGIN_RE) !== -1; 201 | } 202 | 203 | function hasCodeBlock(document) { 204 | var codeBlocks = document.getElementsByClassName('blob-wrapper'); 205 | return codeBlocks.length > 0; 206 | } 207 | 208 | chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) { 209 | // called by the page action to get user selection and location 210 | if (msg.type == 'SELECTION_AND_LOCATION') { 211 | if (!isGitHubUrl(window.location.origin)) return; 212 | if (!hasCodeBlock(document)) return; 213 | 214 | var text = getSelectedText(); 215 | var location = window.location.origin + window.location.pathname; 216 | if (!text || !location) return; 217 | 218 | sendResponse({text:text, location: location}); 219 | } 220 | 221 | // important to return true to keep the async communications open 222 | return true; 223 | }); 224 | 225 | function checkAndTriggerLinkInsertion() { 226 | // this script will be injected on all github pages (based on manifest) 227 | // do a check that this page contains code blocks 228 | if (!isGitHubUrl(window.location.origin)) return; 229 | if (!hasCodeBlock(document)) return; 230 | 231 | // Sends a message to the background script: 232 | // { 233 | // 'type': 'FINDIT', 234 | // 'location': current url 235 | // } 236 | var location = window.location.origin + window.location.pathname; 237 | chrome.runtime.sendMessage({ 238 | type: 'FINDIT', 239 | location: location 240 | }, insertLinks); 241 | } 242 | 243 | $(window).on('load', function() { 244 | checkAndTriggerLinkInsertion(); 245 | 246 | // dynamic script injection 247 | // http://stackoverflow.com/questions/9515704/building-a-chrome-extension-inject-code-in-a-page-using-a-content-script/9517879 248 | var script = document.createElement('script'); 249 | script.textContent = '(' + function() { 250 | $(document).on('pjax:complete', function() { 251 | window.postMessage({type: 'PJAX'}, '*'); 252 | });; 253 | } + ')();'; 254 | (document.head||document.documentElement).appendChild(script); 255 | script.parentNode.removeChild(script); 256 | }); 257 | 258 | window.addEventListener('message', function(event) { 259 | if (event.origin !== 'https://github.com') 260 | return; 261 | if (event.source !== window) 262 | return; 263 | if (event.data.type === 'PJAX') { 264 | checkAndTriggerLinkInsertion(); 265 | } 266 | }) 267 | 268 | function insertLinks(response) { 269 | if (!response) console.log(chrome.runtime.lastError); 270 | tags = {} 271 | response.forEach(function(v) { tags[v.tagname] = v; }) 272 | linkUpMethods(tags); 273 | } 274 | -------------------------------------------------------------------------------- /sample-tags: -------------------------------------------------------------------------------- 1 | !_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ 2 | !_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ 3 | !_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/ 4 | !_TAG_PROGRAM_NAME Exuberant Ctags // 5 | !_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ 6 | !_TAG_PROGRAM_VERSION 5.9~svn20110310 // 7 | $.addFlex Flexigrid/js/flexigrid.js /^ }$/;" f 8 | $.fn.disableSelection Flexigrid/js/flexigrid.js /^ if(typeof $.fn.disableSelection != 'function') {$/;" f 9 | $.fn.flexAddData Flexigrid/js/flexigrid.js /^ }; \/\/end flexToggleCol$/;" f 10 | $.fn.flexOptions Flexigrid/js/flexigrid.js /^ }; \/\/end flexReload$/;" f 11 | $.fn.flexReload Flexigrid/js/flexigrid.js /^ }; \/\/end flexigrid$/;" f 12 | $.fn.flexSearch Flexigrid/js/flexigrid.js /^ }; \/\/end noSelect$/;" f 13 | $.fn.flexToggleCol Flexigrid/js/flexigrid.js /^ }; \/\/end flexOptions$/;" f 14 | $.fn.flexigrid Flexigrid/js/flexigrid.js /^ });$/;" f 15 | $.fn.noSelect Flexigrid/js/flexigrid.js /^ };$/;" f 16 | $.fn.selectedRows Flexigrid/js/flexigrid.js /^ }; \/\/end flexSearch$/;" f 17 | Example4 Flexigrid/demo/index.html /^ function Example4(com, grid) {$/;" f 18 | active_group Flexigrid/demo/post-json.php /^ $active_group = 'default';$/;" v 19 | active_group Flexigrid/demo/post-xml.php /^ $active_group = 'default';$/;" v 20 | addFormData Flexigrid/demo/sample.html /^function addFormData(){$/;" f 21 | base_url Flexigrid/demo/post-json.php /^ $base_url = "http:\/\/".$_SERVER['HTTP_HOST'];$/;" v 22 | base_url Flexigrid/demo/post-xml.php /^ $base_url = "http:\/\/".$_SERVER['HTTP_HOST'];$/;" v 23 | cb Flexigrid/js/flexigrid.js /^ var cb = $('input[value=' + cid + ']', g.nDiv)[0];$/;" v 24 | connect Flexigrid/demo/post-json.php /^ $connect = mysql_connect($db[$active_group]['hostname'],$db[$active_group]['username'],$db[$active_group]['password']) or die ("Error: could not connect to database");$/;" v 25 | connect Flexigrid/demo/post-xml.php /^ $connect = mysql_connect($db[$active_group]['hostname'],$db[$active_group]['username'],$db[$active_group]['password']) or die ("Error: could not connect to database");$/;" v 26 | countRec Flexigrid/demo/post-json.php /^function countRec($fname,$tname) {$/;" f 27 | countRec Flexigrid/demo/post-xml.php /^function countRec($fname,$tname) {$/;" f 28 | db Flexigrid/demo/post-json.php /^ $db = mysql_select_db($db[$active_group]['database']);$/;" v 29 | db Flexigrid/demo/post-xml.php /^ $db = mysql_select_db($db[$active_group]['database']);$/;" v 30 | diff Flexigrid/js/flexigrid.js /^ var diff = e.pageX - this.colresize.startX;$/;" v 31 | docloaded Flexigrid/js/flexigrid.js /^ var docloaded = false;$/;" v 32 | entry Flexigrid/demo/example4.php /^ $entry = array('id' => $rowNum,$/;" v 33 | entry Flexigrid/demo/post-json.php /^ $entry = array('id'=>$row['iso'],$/;" v 34 | gh Flexigrid/js/flexigrid.js /^ var gh = $(g.bDiv).height();$/;" v 35 | gtop Flexigrid/js/flexigrid.js /^ var gtop = g.bDiv.offsetTop;$/;" v 36 | h Flexigrid/js/flexigrid.js /^ this.vresize = {$/;" p class:this.vresize 37 | hrH Flexigrid/js/flexigrid.js /^ var hrH = g.bDiv.offsetTop + newH;$/;" v 38 | hset Flexigrid/js/flexigrid.js /^ var g = {$/;" p class:$.addFlex.g 39 | jsonData Flexigrid/demo/example4.php /^ $jsonData = array('page'=>$page,'total'=>0,'rows'=>array());$/;" v 40 | jsonData Flexigrid/demo/post-json.php /^$jsonData = array('page'=>$page,'total'=>$total,'rows'=>array());$/;" v 41 | left Flexigrid/js/flexigrid.js /^ var left = obj.offsetParent.offsetLeft;$/;" v 42 | limit Flexigrid/demo/post-json.php /^$limit = "LIMIT $start, $rp";$/;" v 43 | limit Flexigrid/demo/post-xml.php /^$limit = "LIMIT $start, $rp";$/;" v 44 | matched Flexigrid/js/flexigrid.js /^ var matched = uaMatch( navigator.userAgent );$/;" v 45 | n Flexigrid/js/flexigrid.js /^ nw: nw,$/;" p class:colresize 46 | n Flexigrid/js/flexigrid.js /^ var n = $('div', this.cDrag).index(obj);$/;" v 47 | n Flexigrid/js/flexigrid.js /^ var n = $('div', this.cDrag).index(obj),$/;" v 48 | n Flexigrid/js/flexigrid.js /^ var n = $('thead th', g.hDiv).index(ncol);$/;" v 49 | nd Flexigrid/js/flexigrid.js /^ var nd = parseInt($(g.nDiv).height(), 10);$/;" v 50 | nw Flexigrid/js/flexigrid.js /^ this.colresize = {$/;" p class:colresize 51 | ow Flexigrid/js/flexigrid.js /^ var ow = $('th:visible div:eq(' + n + ')', this.hDiv).width();$/;" v 52 | page Flexigrid/demo/example4.php /^ $page = isset($_POST['page']) ? $_POST['page'] : 1;$/;" v 53 | page Flexigrid/demo/post-json.php /^$page = isset($_POST['page']) ? $_POST['page'] : 1;$/;" v 54 | page Flexigrid/demo/post-xml.php /^$page = $_POST['page'];$/;" v 55 | page Flexigrid/demo/post-xml.php /^$page = isset($_POST['page']) ? $_POST['page'] : 1;$/;" v 56 | pdl Flexigrid/js/flexigrid.js /^ var pdl = parseInt($(obj).css('paddingLeft'), 10);$/;" v 57 | pdt Flexigrid/js/flexigrid.js /^ var pdt = parseInt($(obj).css('paddingTop'), 10);$/;" v 58 | pht Flexigrid/js/flexigrid.js /^ var pht = parseInt($(obj).parent().height(), 10);$/;" v 59 | pwt Flexigrid/js/flexigrid.js /^ var pwt = parseInt($(obj).parent().width(), 10);$/;" v 60 | qtype Flexigrid/demo/example4.php /^ $qtype = isset($_POST['qtype']) ? $_POST['qtype'] : false;$/;" v 61 | qtype Flexigrid/demo/post-json.php /^$qtype = isset($_POST['qtype']) ? $_POST['qtype'] : false;$/;" v 62 | qtype Flexigrid/demo/post-xml.php /^$qtype = isset($_POST['qtype']) ? $_POST['qtype'] : false;$/;" v 63 | query Flexigrid/demo/example4.php /^ $query = isset($_POST['query']) ? $_POST['query'] : false;$/;" v 64 | query Flexigrid/demo/post-json.php /^ $query = strtolower(trim($query));$/;" v 65 | query Flexigrid/demo/post-json.php /^$query = isset($_POST['query']) ? $_POST['query'] : false;$/;" v 66 | query Flexigrid/demo/post-xml.php /^ $query = strtolower(trim($query));$/;" v 67 | query Flexigrid/demo/post-xml.php /^$query = isset($_POST['query']) ? $_POST['query'] : false;$/;" v 68 | r1 Flexigrid/js/flexigrid.js /^ var r1 = p.total == 0 ? 0 : (p.page - 1) * p.rp + 1;$/;" v 69 | r2 Flexigrid/js/flexigrid.js /^ var r2 = r1 + p.rp - 1;$/;" v 70 | result Flexigrid/demo/post-json.php /^ $result = mysql_query($rsql) or die ($rsql);$/;" v 71 | result Flexigrid/demo/post-json.php /^ $result = runSQL($sql);$/;" v 72 | result Flexigrid/demo/post-json.php /^$result = runSQL($sql);$/;" v 73 | result Flexigrid/demo/post-xml.php /^ $result = mysql_query($rsql) or die ('test');$/;" v 74 | result Flexigrid/demo/post-xml.php /^ $result = runSQL($sql);$/;" v 75 | result Flexigrid/demo/post-xml.php /^$result = runSQL($sql);$/;" v 76 | rows Flexigrid/demo/countryArray.inc.php /^$rows = array($/;" v 77 | rows Flexigrid/demo/example4.php /^ $rows = $_SESSION['Example4'];$/;" v 78 | rows Flexigrid/demo/example4.php /^ $rows = $_SESSION['Example4'];$/;" v 79 | rows Flexigrid/demo/post-json.php /^ $rows = array_slice($rows,($page-1)*$rp,$rp);$/;" v 80 | rows Flexigrid/demo/post-xml.php /^ $rows = array_slice($rows,($page-1)*$rp,$rp);$/;" v 81 | rows Flexigrid/demo/post-xml.php /^$rows = array();$/;" v 82 | rp Flexigrid/demo/example4.php /^ $rp = isset($_POST['rp']) ? $_POST['rp'] : 10;$/;" v 83 | rp Flexigrid/demo/post-json.php /^$rp = isset($_POST['rp']) ? $_POST['rp'] : 10;$/;" v 84 | rp Flexigrid/demo/post-xml.php /^$rp = $_POST['rp'];$/;" v 85 | rp Flexigrid/demo/post-xml.php /^$rp = isset($_POST['rp']) ? $_POST['rp'] : 10;$/;" v 86 | runSQL Flexigrid/demo/post-json.php /^function runSQL($rsql) {$/;" f 87 | runSQL Flexigrid/demo/post-xml.php /^function runSQL($rsql) {$/;" f 88 | sort Flexigrid/demo/post-json.php /^$sort = "ORDER BY $sortname $sortorder";$/;" v 89 | sort Flexigrid/demo/post-xml.php /^$sort = "ORDER BY $sortname $sortorder";$/;" v 90 | sortArray Flexigrid/demo/post-json.php /^ $sortArray = array();$/;" v 91 | sortArray Flexigrid/demo/post-xml.php /^ $sortArray = array();$/;" v 92 | sortMethod Flexigrid/demo/post-json.php /^ $sortMethod = SORT_DESC;$/;" v 93 | sortMethod Flexigrid/demo/post-json.php /^ $sortMethod = SORT_ASC;$/;" v 94 | sortMethod Flexigrid/demo/post-xml.php /^ $sortMethod = SORT_DESC;$/;" v 95 | sortMethod Flexigrid/demo/post-xml.php /^ $sortMethod = SORT_ASC;$/;" v 96 | sortname Flexigrid/demo/example4.php /^ $sortname = isset($_POST['sortname']) ? $_POST['sortname'] : 'name';$/;" v 97 | sortname Flexigrid/demo/post-json.php /^$sortname = isset($_POST['sortname']) ? $_POST['sortname'] : 'name';$/;" v 98 | sortname Flexigrid/demo/post-xml.php /^$sortname = $_POST['sortname'];$/;" v 99 | sortname Flexigrid/demo/post-xml.php /^$sortname = isset($_POST['sortname']) ? $_POST['sortname'] : 'name';$/;" v 100 | sortorder Flexigrid/demo/example4.php /^ $sortorder = isset($_POST['sortorder']) ? $_POST['sortorder'] : 'desc';$/;" v 101 | sortorder Flexigrid/demo/post-json.php /^$sortorder = isset($_POST['sortorder']) ? $_POST['sortorder'] : 'desc';$/;" v 102 | sortorder Flexigrid/demo/post-xml.php /^$sortorder = $_POST['sortorder'];$/;" v 103 | sortorder Flexigrid/demo/post-xml.php /^$sortorder = isset($_POST['sortorder']) ? $_POST['sortorder'] : 'desc';$/;" v 104 | sql Flexigrid/demo/post-json.php /^ $sql = "SELECT count($fname) FROM $tname ";$/;" v 105 | sql Flexigrid/demo/post-json.php /^$sql = "SELECT iso,name,printable_name,iso3,numcode FROM country $where $sort $limit";$/;" v 106 | sql Flexigrid/demo/post-xml.php /^ $sql = "SELECT count($fname) FROM $tname ";$/;" v 107 | sql Flexigrid/demo/post-xml.php /^$sql = "SELECT iso,name,printable_name,iso3,numcode FROM country $where $sort $limit";$/;" v 108 | start Flexigrid/demo/post-json.php /^$start = (($page-1) * $rp);$/;" v 109 | start Flexigrid/demo/post-xml.php /^$start = (($page-1) * $rp);$/;" v 110 | startX Flexigrid/js/flexigrid.js /^ this.colresize = {$/;" p class:colresize 111 | stat Flexigrid/js/flexigrid.js /^ var stat = p.pagestat;$/;" v 112 | tbody Flexigrid/js/flexigrid.js /^ var tbody = document.createElement('tbody');$/;" v 113 | test Flexigrid/demo/index.html /^ function test(com, grid) {$/;" f 114 | thead Flexigrid/js/flexigrid.js /^ var thead = $("thead:first", t).get(0);$/;" v 115 | top Flexigrid/js/flexigrid.js /^ var top = obj.offsetParent.offsetTop;$/;" v 116 | total Flexigrid/demo/post-json.php /^ $total = count($rows);$/;" v 117 | total Flexigrid/demo/post-json.php /^$total = countRec("iso","country $where");$/;" v 118 | total Flexigrid/demo/post-xml.php /^ $total = count($rows);$/;" v 119 | total Flexigrid/demo/post-xml.php /^$total = countRec('iso','country');$/;" v 120 | uaMatch Flexigrid/js/flexigrid.js /^ function uaMatch( ua ) {$/;" f function:function 121 | usingSQL Flexigrid/demo/post-json.php /^$usingSQL = true;$/;" v 122 | usingSQL Flexigrid/demo/post-xml.php /^$usingSQL = true;$/;" v 123 | where Flexigrid/demo/post-json.php /^$where = "";$/;" v 124 | where Flexigrid/demo/post-xml.php /^$where = "";$/;" v 125 | wt Flexigrid/js/flexigrid.js /^ var wt = parseInt(obj.style.width, 10);$/;" v 126 | xml Flexigrid/demo/post-xml.php /^$xml = "\\n";$/;" v 127 | -------------------------------------------------------------------------------- /tags-parser/Flexigrid/js/flexigrid.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Flexigrid for jQuery - v1.1 3 | * 4 | * Copyright (c) 2008 Paulo P. Marinas (code.google.com/p/flexigrid/) 5 | * Dual licensed under the MIT or GPL Version 2 licenses. 6 | * http://jquery.org/license 7 | * 8 | */ 9 | (function ($) { 10 | /* 11 | * jQuery 1.9 support. browser object has been removed in 1.9 12 | */ 13 | var browser = $.browser 14 | 15 | if (!browser) { 16 | function uaMatch( ua ) { 17 | ua = ua.toLowerCase(); 18 | 19 | var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) || 20 | /(webkit)[ \/]([\w.]+)/.exec( ua ) || 21 | /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) || 22 | /(msie) ([\w.]+)/.exec( ua ) || 23 | ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) || 24 | []; 25 | 26 | return { 27 | browser: match[ 1 ] || "", 28 | version: match[ 2 ] || "0" 29 | }; 30 | }; 31 | 32 | var matched = uaMatch( navigator.userAgent ); 33 | browser = {}; 34 | 35 | if ( matched.browser ) { 36 | browser[ matched.browser ] = true; 37 | browser.version = matched.version; 38 | } 39 | 40 | // Chrome is Webkit, but Webkit is also Safari. 41 | if ( browser.chrome ) { 42 | browser.webkit = true; 43 | } else if ( browser.webkit ) { 44 | browser.safari = true; 45 | } 46 | } 47 | 48 | /*! 49 | * START code from jQuery UI 50 | * 51 | * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) 52 | * Dual licensed under the MIT or GPL Version 2 licenses. 53 | * http://jquery.org/license 54 | * 55 | * http://docs.jquery.com/UI 56 | */ 57 | 58 | if(typeof $.support.selectstart != 'function') { 59 | $.support.selectstart = "onselectstart" in document.createElement("div"); 60 | } 61 | 62 | if(typeof $.fn.disableSelection != 'function') { 63 | $.fn.disableSelection = function() { 64 | return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) + 65 | ".ui-disableSelection", function( event ) { 66 | event.preventDefault(); 67 | }); 68 | }; 69 | } 70 | 71 | /* END code from jQuery UI */ 72 | 73 | $.addFlex = function (t, p) { 74 | if (t.grid) return false; //return if already exist 75 | p = $.extend({ //apply default properties 76 | height: 200, //default height 77 | width: 'auto', //auto width 78 | striped: true, //apply odd even stripes 79 | novstripe: false, 80 | minwidth: 30, //min width of columns 81 | minheight: 80, //min height of columns 82 | resizable: true, //allow table resizing 83 | url: false, //URL if using data from AJAX 84 | method: 'POST', //data sending method 85 | dataType: 'xml', //type of data for AJAX, either xml or json 86 | errormsg: 'Connection Error', 87 | usepager: false, 88 | nowrap: true, 89 | page: 1, //current page 90 | total: 1, //total pages 91 | useRp: true, //use the results per page select box 92 | rp: 15, //results per page 93 | rpOptions: [10, 15, 20, 30, 50], //allowed per-page values 94 | title: false, 95 | idProperty: 'id', 96 | pagestat: 'Displaying {from} to {to} of {total} items', 97 | pagetext: 'Page', 98 | outof: 'of', 99 | findtext: 'Find', 100 | params: [], //allow optional parameters to be passed around 101 | procmsg: 'Processing, please wait ...', 102 | query: '', 103 | qtype: '', 104 | nomsg: 'No items', 105 | minColToggle: 1, //minimum allowed column to be hidden 106 | showToggleBtn: true, //show or hide column toggle popup 107 | hideOnSubmit: true, 108 | autoload: true, 109 | blockOpacity: 0.5, 110 | preProcess: false, 111 | addTitleToCell: false, // add a title attr to cells with truncated contents 112 | dblClickResize: false, //auto resize column by double clicking 113 | onDragCol: false, 114 | onToggleCol: false, 115 | onChangeSort: false, 116 | onDoubleClick: false, 117 | onSuccess: false, 118 | onError: false, 119 | onSubmit: false, //using a custom populate function 120 | __mw: { //extendable middleware function holding object 121 | datacol: function(p, col, val) { //middleware for formatting data columns 122 | var _col = (typeof p.datacol[col] == 'function') ? p.datacol[col](val) : val; //format column using function 123 | if(typeof p.datacol['*'] == 'function') { //if wildcard function exists 124 | return p.datacol['*'](_col); //run wildcard function 125 | } else { 126 | return _col; //return column without wildcard 127 | } 128 | } 129 | }, 130 | getGridClass: function(g) { //get the grid class, always returns g 131 | return g; 132 | }, 133 | datacol: {}, //datacol middleware object 'colkey': function(colval) {} 134 | colResize: true, //from: http://stackoverflow.com/a/10615589 135 | colMove: true 136 | }, p); 137 | $(t).show() //show if hidden 138 | .attr({ 139 | cellPadding: 0, 140 | cellSpacing: 0, 141 | border: 0 142 | }) //remove padding and spacing 143 | .removeAttr('width'); //remove width properties 144 | //create grid class 145 | var g = { 146 | hset: {}, 147 | rePosDrag: function () { 148 | var cdleft = 0 - this.hDiv.scrollLeft; 149 | if (this.hDiv.scrollLeft > 0) cdleft -= Math.floor(p.cgwidth / 2); 150 | $(g.cDrag).css({ 151 | top: g.hDiv.offsetTop + 1 152 | }); 153 | var cdpad = this.cdpad; 154 | var cdcounter=0; 155 | $('div', g.cDrag).hide(); 156 | $('thead tr:first th:visible', this.hDiv).each(function () { 157 | var n = $('thead tr:first th:visible', g.hDiv).index(this); 158 | var cdpos = parseInt($('div', this).width()); 159 | if (cdleft == 0) cdleft -= Math.floor(p.cgwidth / 2); 160 | cdpos = cdpos + cdleft + cdpad; 161 | if (isNaN(cdpos)) { 162 | cdpos = 0; 163 | } 164 | $('div:eq(' + n + ')', g.cDrag).css({ 165 | 'left': (!(browser.mozilla) ? cdpos - cdcounter : cdpos) + 'px' 166 | }).show(); 167 | cdleft = cdpos; 168 | cdcounter++; 169 | }); 170 | }, 171 | fixHeight: function (newH) { 172 | newH = false; 173 | if (!newH) newH = $(g.bDiv).height(); 174 | var hdHeight = $(this.hDiv).height(); 175 | $('div', this.cDrag).each( 176 | function () { 177 | $(this).height(newH + hdHeight); 178 | } 179 | ); 180 | var nd = parseInt($(g.nDiv).height(), 10); 181 | if (nd > newH) $(g.nDiv).height(newH).width(200); 182 | else $(g.nDiv).height('auto').width('auto'); 183 | $(g.block).css({ 184 | height: newH, 185 | marginBottom: (newH * -1) 186 | }); 187 | var hrH = g.bDiv.offsetTop + newH; 188 | if (p.height != 'auto' && p.resizable) hrH = g.vDiv.offsetTop; 189 | $(g.rDiv).css({ 190 | height: hrH 191 | }); 192 | }, 193 | dragStart: function (dragtype, e, obj) { //default drag function start 194 | if (dragtype == 'colresize' && p.colResize === true) {//column resize 195 | $(g.nDiv).hide(); 196 | $(g.nBtn).hide(); 197 | var n = $('div', this.cDrag).index(obj); 198 | var ow = $('th:visible div:eq(' + n + ')', this.hDiv).width(); 199 | $(obj).addClass('dragging').siblings().hide(); 200 | $(obj).prev().addClass('dragging').show(); 201 | this.colresize = { 202 | startX: e.pageX, 203 | ol: parseInt(obj.style.left, 10), 204 | ow: ow, 205 | n: n 206 | }; 207 | $('body').css('cursor', 'col-resize'); 208 | } else if (dragtype == 'vresize') {//table resize 209 | var hgo = false; 210 | $('body').css('cursor', 'row-resize'); 211 | if (obj) { 212 | hgo = true; 213 | $('body').css('cursor', 'col-resize'); 214 | } 215 | this.vresize = { 216 | h: p.height, 217 | sy: e.pageY, 218 | w: p.width, 219 | sx: e.pageX, 220 | hgo: hgo 221 | }; 222 | } else if (dragtype == 'colMove') {//column header drag 223 | $(e.target).disableSelection(); //disable selecting the column header 224 | if((p.colMove === true)) { 225 | $(g.nDiv).hide(); 226 | $(g.nBtn).hide(); 227 | this.hset = $(this.hDiv).offset(); 228 | this.hset.right = this.hset.left + $('table', this.hDiv).width(); 229 | this.hset.bottom = this.hset.top + $('table', this.hDiv).height(); 230 | this.dcol = obj; 231 | this.dcoln = $('th', this.hDiv).index(obj); 232 | this.colCopy = document.createElement("div"); 233 | this.colCopy.className = "colCopy"; 234 | this.colCopy.innerHTML = obj.innerHTML; 235 | if (browser.msie) { 236 | this.colCopy.className = "colCopy ie"; 237 | } 238 | $(this.colCopy).css({ 239 | position: 'absolute', 240 | 'float': 'left', 241 | display: 'none', 242 | textAlign: obj.align 243 | }); 244 | $('body').append(this.colCopy); 245 | $(this.cDrag).hide(); 246 | } 247 | } 248 | $('body').noSelect(); 249 | }, 250 | dragMove: function (e) { 251 | if (this.colresize) {//column resize 252 | var n = this.colresize.n; 253 | var diff = e.pageX - this.colresize.startX; 254 | var nleft = this.colresize.ol + diff; 255 | var nw = this.colresize.ow + diff; 256 | if (nw > p.minwidth) { 257 | $('div:eq(' + n + ')', this.cDrag).css('left', nleft); 258 | this.colresize.nw = nw; 259 | } 260 | } else if (this.vresize) {//table resize 261 | var v = this.vresize; 262 | var y = e.pageY; 263 | var diff = y - v.sy; 264 | if (!p.defwidth) p.defwidth = p.width; 265 | if (p.width != 'auto' && !p.nohresize && v.hgo) { 266 | var x = e.pageX; 267 | var xdiff = x - v.sx; 268 | var newW = v.w + xdiff; 269 | if (newW > p.defwidth) { 270 | this.gDiv.style.width = newW + 'px'; 271 | p.width = newW; 272 | } 273 | } 274 | var newH = v.h + diff; 275 | if ((newH > p.minheight || p.height < p.minheight) && !v.hgo) { 276 | this.bDiv.style.height = newH + 'px'; 277 | p.height = newH; 278 | this.fixHeight(newH); 279 | } 280 | v = null; 281 | } else if (this.colCopy) { 282 | $(this.dcol).addClass('thMove').removeClass('thOver'); 283 | if (e.pageX > this.hset.right || e.pageX < this.hset.left || e.pageY > this.hset.bottom || e.pageY < this.hset.top) { 284 | //this.dragEnd(); 285 | $('body').css('cursor', 'move'); 286 | } else { 287 | $('body').css('cursor', 'pointer'); 288 | } 289 | $(this.colCopy).css({ 290 | top: e.pageY + 10, 291 | left: e.pageX + 20, 292 | display: 'block' 293 | }); 294 | } 295 | }, 296 | dragEnd: function () { 297 | if (this.colresize) { 298 | var n = this.colresize.n; 299 | var nw = this.colresize.nw; 300 | $('th:visible div:eq(' + n + ')', this.hDiv).css('width', nw); 301 | $('tr', this.bDiv).each( 302 | function () { 303 | var $tdDiv = $('td:visible div:eq(' + n + ')', this); 304 | $tdDiv.css('width', nw); 305 | g.addTitleToCell($tdDiv); 306 | } 307 | ); 308 | this.hDiv.scrollLeft = this.bDiv.scrollLeft; 309 | $('div:eq(' + n + ')', this.cDrag).siblings().show(); 310 | $('.dragging', this.cDrag).removeClass('dragging'); 311 | this.rePosDrag(); 312 | this.fixHeight(); 313 | this.colresize = false; 314 | if ($.cookies) { 315 | var name = p.colModel[n].name; // Store the widths in the cookies 316 | $.cookie('flexiwidths/'+name, nw); 317 | } 318 | } else if (this.vresize) { 319 | this.vresize = false; 320 | } else if (this.colCopy) { 321 | $(this.colCopy).remove(); 322 | if (this.dcolt !== null) { 323 | if (this.dcoln > this.dcolt) $('th:eq(' + this.dcolt + ')', this.hDiv).before(this.dcol); 324 | else $('th:eq(' + this.dcolt + ')', this.hDiv).after(this.dcol); 325 | this.switchCol(this.dcoln, this.dcolt); 326 | $(this.cdropleft).remove(); 327 | $(this.cdropright).remove(); 328 | this.rePosDrag(); 329 | if (p.onDragCol) { 330 | p.onDragCol(this.dcoln, this.dcolt); 331 | } 332 | } 333 | this.dcol = null; 334 | this.hset = null; 335 | this.dcoln = null; 336 | this.dcolt = null; 337 | this.colCopy = null; 338 | $('.thMove', this.hDiv).removeClass('thMove'); 339 | $(this.cDrag).show(); 340 | } 341 | $('body').css('cursor', 'default'); 342 | $('body').noSelect(false); 343 | }, 344 | toggleCol: function (cid, visible) { 345 | var ncol = $("th[axis='col" + cid + "']", this.hDiv)[0]; 346 | var n = $('thead th', g.hDiv).index(ncol); 347 | var cb = $('input[value=' + cid + ']', g.nDiv)[0]; 348 | if (visible == null) { 349 | visible = ncol.hidden; 350 | } 351 | if ($('input:checked', g.nDiv).length < p.minColToggle && !visible) { 352 | return false; 353 | } 354 | if (visible) { 355 | ncol.hidden = false; 356 | $(ncol).show(); 357 | cb.checked = true; 358 | } else { 359 | ncol.hidden = true; 360 | $(ncol).hide(); 361 | cb.checked = false; 362 | } 363 | $('tbody tr', t).each( 364 | function () { 365 | if (visible) { 366 | $('td:eq(' + n + ')', this).show(); 367 | } else { 368 | $('td:eq(' + n + ')', this).hide(); 369 | } 370 | } 371 | ); 372 | this.rePosDrag(); 373 | if (p.onToggleCol) { 374 | p.onToggleCol(cid, visible); 375 | } 376 | return visible; 377 | }, 378 | switchCol: function (cdrag, cdrop) { //switch columns 379 | $('tbody tr', t).each( 380 | function () { 381 | if (cdrag > cdrop) $('td:eq(' + cdrop + ')', this).before($('td:eq(' + cdrag + ')', this)); 382 | else $('td:eq(' + cdrop + ')', this).after($('td:eq(' + cdrag + ')', this)); 383 | } 384 | ); 385 | //switch order in nDiv 386 | if (cdrag > cdrop) { 387 | $('tr:eq(' + cdrop + ')', this.nDiv).before($('tr:eq(' + cdrag + ')', this.nDiv)); 388 | } else { 389 | $('tr:eq(' + cdrop + ')', this.nDiv).after($('tr:eq(' + cdrag + ')', this.nDiv)); 390 | } 391 | if (browser.msie && browser.version < 7.0) { 392 | $('tr:eq(' + cdrop + ') input', this.nDiv)[0].checked = true; 393 | } 394 | this.hDiv.scrollLeft = this.bDiv.scrollLeft; 395 | }, 396 | scroll: function () { 397 | this.hDiv.scrollLeft = this.bDiv.scrollLeft; 398 | this.rePosDrag(); 399 | }, 400 | addData: function (data) { //parse data 401 | if (p.dataType == 'json') { 402 | data = $.extend({rows: [], page: 0, total: 0}, data); 403 | } 404 | if (p.preProcess) { 405 | data = p.preProcess(data); 406 | } 407 | $('.pReload', this.pDiv).removeClass('loading'); 408 | this.loading = false; 409 | if (!data) { 410 | $('.pPageStat', this.pDiv).html(p.errormsg); 411 | if (p.onSuccess) p.onSuccess(this); 412 | return false; 413 | } 414 | if (p.dataType == 'xml') { 415 | p.total = +$('rows total', data).text(); 416 | } else { 417 | p.total = data.total; 418 | } 419 | if (p.total === 0) { 420 | $('tr, a, td, div', t).unbind(); 421 | $(t).empty(); 422 | p.pages = 1; 423 | p.page = 1; 424 | this.buildpager(); 425 | $('.pPageStat', this.pDiv).html(p.nomsg); 426 | if (p.onSuccess) p.onSuccess(this); 427 | return false; 428 | } 429 | p.pages = Math.ceil(p.total / p.rp); 430 | if (p.dataType == 'xml') { 431 | p.page = +$('rows page', data).text(); 432 | } else { 433 | p.page = data.page; 434 | } 435 | this.buildpager(); 436 | //build new body 437 | var tbody = document.createElement('tbody'); 438 | if (p.dataType == 'json') { 439 | $.each(data.rows, function (i, row) { 440 | var tr = document.createElement('tr'); 441 | var jtr = $(tr); 442 | if (row.name) tr.name = row.name; 443 | if (row.color) { 444 | jtr.css('background',row.color); 445 | } else { 446 | if (i % 2 && p.striped) tr.className = 'erow'; 447 | } 448 | if (row[p.idProperty]) { 449 | tr.id = 'row' + row[p.idProperty]; 450 | jtr.attr('data-id', row[p.idProperty]); 451 | } 452 | $('thead tr:first th', g.hDiv).each( //add cell 453 | function () { 454 | var td = document.createElement('td'); 455 | var idx = $(this).attr('axis').substr(3); 456 | td.align = this.align; 457 | // If each row is the object itself (no 'cell' key) 458 | if (typeof row.cell == 'undefined') { 459 | td.innerHTML = row[p.colModel[idx].name]; 460 | } else { 461 | // If the json elements aren't named (which is typical), use numeric order 462 | var iHTML = ''; 463 | if (typeof row.cell[idx] != "undefined") { 464 | iHTML = (row.cell[idx] !== null) ? row.cell[idx] : ''; //null-check for Opera-browser 465 | } else { 466 | iHTML = row.cell[p.colModel[idx].name]; 467 | } 468 | td.innerHTML = p.__mw.datacol(p, $(this).attr('abbr'), iHTML); //use middleware datacol to format cols 469 | } 470 | // If the content has a option, decode it. 471 | var offs = td.innerHTML.indexOf( '0 ) { 473 | $(td).css('background', text.substr(offs+7,7) ); 474 | } 475 | 476 | $(td).attr('abbr', $(this).attr('abbr')); 477 | $(tr).append(td); 478 | td = null; 479 | } 480 | ); 481 | if ($('thead', this.gDiv).length < 1) {//handle if grid has no headers 482 | for (idx = 0; idx < row.cell.length; idx++) { 483 | var td = document.createElement('td'); 484 | // If the json elements aren't named (which is typical), use numeric order 485 | if (typeof row.cell[idx] != "undefined") { 486 | td.innerHTML = (row.cell[idx] != null) ? row.cell[idx] : '';//null-check for Opera-browser 487 | } else { 488 | td.innerHTML = row.cell[p.colModel[idx].name]; 489 | } 490 | $(tr).append(td); 491 | td = null; 492 | } 493 | } 494 | $(tbody).append(tr); 495 | tr = null; 496 | }); 497 | } else if (p.dataType == 'xml') { 498 | var i = 1; 499 | $("rows row", data).each(function () { 500 | i++; 501 | var tr = document.createElement('tr'); 502 | if ($(this).attr('name')) tr.name = $(this).attr('name'); 503 | if ($(this).attr('color')) { 504 | $(tr).css('background',$(this).attr('id')); 505 | } else { 506 | if (i % 2 && p.striped) tr.className = 'erow'; 507 | } 508 | var nid = $(this).attr('id'); 509 | if (nid) { 510 | tr.id = 'row' + nid; 511 | } 512 | nid = null; 513 | var robj = this; 514 | $('thead tr:first th', g.hDiv).each(function () { 515 | var td = document.createElement('td'); 516 | var idx = $(this).attr('axis').substr(3); 517 | td.align = this.align; 518 | 519 | var text = $("cell:eq(" + idx + ")", robj).text(); 520 | var offs = text.indexOf( '0 ) { 522 | $(td).css('background', text.substr(offs+7,7) ); 523 | } 524 | td.innerHTML = p.__mw.datacol(p, $(this).attr('abbr'), text); //use middleware datacol to format cols 525 | $(td).attr('abbr', $(this).attr('abbr')); 526 | $(tr).append(td); 527 | td = null; 528 | }); 529 | if ($('thead', this.gDiv).length < 1) {//handle if grid has no headers 530 | $('cell', this).each(function () { 531 | var td = document.createElement('td'); 532 | td.innerHTML = $(this).text(); 533 | $(tr).append(td); 534 | td = null; 535 | }); 536 | } 537 | $(tbody).append(tr); 538 | tr = null; 539 | robj = null; 540 | }); 541 | } 542 | $('tr', t).unbind(); 543 | $(t).empty(); 544 | $(t).append(tbody); 545 | this.addCellProp(); 546 | this.addRowProp(); 547 | this.rePosDrag(); 548 | tbody = null; 549 | data = null; 550 | i = null; 551 | if (p.onSuccess) { 552 | p.onSuccess(this); 553 | } 554 | if (p.hideOnSubmit) { 555 | $(g.block).remove(); 556 | } 557 | this.hDiv.scrollLeft = this.bDiv.scrollLeft; 558 | if (browser.opera) { 559 | $(t).css('visibility', 'visible'); 560 | } 561 | }, 562 | changeSort: function (th) { //change sortorder 563 | if (this.loading) { 564 | return true; 565 | } 566 | $(g.nDiv).hide(); 567 | $(g.nBtn).hide(); 568 | if (p.sortname == $(th).attr('abbr')) { 569 | if (p.sortorder == 'asc') { 570 | p.sortorder = 'desc'; 571 | } else { 572 | p.sortorder = 'asc'; 573 | } 574 | } 575 | $(th).addClass('sorted').siblings().removeClass('sorted'); 576 | $('.sdesc', this.hDiv).removeClass('sdesc'); 577 | $('.sasc', this.hDiv).removeClass('sasc'); 578 | $('div', th).addClass('s' + p.sortorder); 579 | p.sortname = $(th).attr('abbr'); 580 | if (p.onChangeSort) { 581 | p.onChangeSort(p.sortname, p.sortorder); 582 | } else { 583 | this.populate(); 584 | } 585 | }, 586 | buildpager: function () { //rebuild pager based on new properties 587 | $('.pcontrol input', this.pDiv).val(p.page); 588 | $('.pcontrol span', this.pDiv).html(p.pages); 589 | var r1 = p.total == 0 ? 0 : (p.page - 1) * p.rp + 1; 590 | var r2 = r1 + p.rp - 1; 591 | if (p.total < r2) { 592 | r2 = p.total; 593 | } 594 | var stat = p.pagestat; 595 | stat = stat.replace(/{from}/, r1); 596 | stat = stat.replace(/{to}/, r2); 597 | stat = stat.replace(/{total}/, p.total); 598 | $('.pPageStat', this.pDiv).html(stat); 599 | }, 600 | populate: function () { //get latest data 601 | if (this.loading) { 602 | return true; 603 | } 604 | if (p.onSubmit) { 605 | var gh = p.onSubmit(); 606 | if (!gh) { 607 | return false; 608 | } 609 | } 610 | this.loading = true; 611 | if (!p.url) { 612 | return false; 613 | } 614 | $('.pPageStat', this.pDiv).html(p.procmsg); 615 | $('.pReload', this.pDiv).addClass('loading'); 616 | $(g.block).css({ 617 | top: g.bDiv.offsetTop 618 | }); 619 | if (p.hideOnSubmit) { 620 | $(this.gDiv).prepend(g.block); 621 | } 622 | if (browser.opera) { 623 | $(t).css('visibility', 'hidden'); 624 | } 625 | if (!p.newp) { 626 | p.newp = 1; 627 | } 628 | if (p.page > p.pages) { 629 | p.page = p.pages; 630 | } 631 | var param = [{ 632 | name: 'page', 633 | value: p.newp 634 | }, { 635 | name: 'rp', 636 | value: p.rp 637 | }, { 638 | name: 'sortname', 639 | value: p.sortname 640 | }, { 641 | name: 'sortorder', 642 | value: p.sortorder 643 | }, { 644 | name: 'query', 645 | value: p.query 646 | }, { 647 | name: 'qtype', 648 | value: p.qtype 649 | }]; 650 | if (p.params.length) { 651 | for (var pi = 0; pi < p.params.length; pi++) { 652 | param[param.length] = p.params[pi]; 653 | } 654 | } 655 | $.ajax({ 656 | type: p.method, 657 | url: p.url, 658 | data: param, 659 | dataType: p.dataType, 660 | success: function (data) { 661 | g.addData(data); 662 | }, 663 | error: function (XMLHttpRequest, textStatus, errorThrown) { 664 | try { 665 | if (p.onError) p.onError(XMLHttpRequest, textStatus, errorThrown); 666 | } catch (e) {} 667 | } 668 | }); 669 | }, 670 | doSearch: function () { 671 | p.query = $('input[name=q]', g.sDiv).val(); 672 | p.qtype = $('select[name=qtype]', g.sDiv).val(); 673 | p.newp = 1; 674 | this.populate(); 675 | }, 676 | changePage: function (ctype) { //change page 677 | if (this.loading) { 678 | return true; 679 | } 680 | switch (ctype) { 681 | case 'first': 682 | p.newp = 1; 683 | break; 684 | case 'prev': 685 | if (p.page > 1) { 686 | p.newp = parseInt(p.page, 10) - 1; 687 | } 688 | break; 689 | case 'next': 690 | if (p.page < p.pages) { 691 | p.newp = parseInt(p.page, 10) + 1; 692 | } 693 | break; 694 | case 'last': 695 | p.newp = p.pages; 696 | break; 697 | case 'input': 698 | var nv = parseInt($('.pcontrol input', this.pDiv).val(), 10); 699 | if (isNaN(nv)) { 700 | nv = 1; 701 | } 702 | if (nv < 1) { 703 | nv = 1; 704 | } else if (nv > p.pages) { 705 | nv = p.pages; 706 | } 707 | $('.pcontrol input', this.pDiv).val(nv); 708 | p.newp = nv; 709 | break; 710 | } 711 | if (p.newp == p.page) { 712 | return false; 713 | } 714 | if (p.onChangePage) { 715 | p.onChangePage(p.newp); 716 | } else { 717 | this.populate(); 718 | } 719 | }, 720 | addCellProp: function () { 721 | $('tbody tr td', g.bDiv).each(function () { 722 | var tdDiv = document.createElement('div'); 723 | var n = $('td', $(this).parent()).index(this); 724 | var pth = $('th:eq(' + n + ')', g.hDiv).get(0); 725 | if (pth != null) { 726 | if (p.sortname == $(pth).attr('abbr') && p.sortname) { 727 | this.className = 'sorted'; 728 | } 729 | $(tdDiv).css({ 730 | textAlign: pth.align, 731 | width: $('div:first', pth)[0].style.width 732 | }); 733 | if (pth.hidden) { 734 | $(this).css('display', 'none'); 735 | } 736 | } 737 | if (p.nowrap == false) { 738 | $(tdDiv).css('white-space', 'normal'); 739 | } 740 | if (this.innerHTML == '') { 741 | this.innerHTML = ' '; 742 | } 743 | tdDiv.innerHTML = this.innerHTML; 744 | var prnt = $(this).parent()[0]; 745 | var pid = false; 746 | if (prnt.id) { 747 | pid = prnt.id.substr(3); 748 | } 749 | if (pth != null) { 750 | if (pth.process) pth.process(tdDiv, pid); 751 | } 752 | $(this).empty().append(tdDiv).removeAttr('width'); //wrap content 753 | g.addTitleToCell(tdDiv); 754 | }); 755 | }, 756 | getCellDim: function (obj) {// get cell prop for editable event 757 | var ht = parseInt($(obj).height(), 10); 758 | var pht = parseInt($(obj).parent().height(), 10); 759 | var wt = parseInt(obj.style.width, 10); 760 | var pwt = parseInt($(obj).parent().width(), 10); 761 | var top = obj.offsetParent.offsetTop; 762 | var left = obj.offsetParent.offsetLeft; 763 | var pdl = parseInt($(obj).css('paddingLeft'), 10); 764 | var pdt = parseInt($(obj).css('paddingTop'), 10); 765 | return { 766 | ht: ht, 767 | wt: wt, 768 | top: top, 769 | left: left, 770 | pdl: pdl, 771 | pdt: pdt, 772 | pht: pht, 773 | pwt: pwt 774 | }; 775 | }, 776 | addRowProp: function () { 777 | $('tbody tr', g.bDiv).on('click', function (e) { 778 | var obj = (e.target || e.srcElement); 779 | if (obj.href || obj.type) return true; 780 | if (e.ctrlKey || e.metaKey) { 781 | // mousedown already took care of this case 782 | return; 783 | } 784 | $(this).toggleClass('trSelected'); 785 | if (p.singleSelect && ! g.multisel) { 786 | $(this).siblings().removeClass('trSelected'); 787 | } 788 | }).on('mousedown', function (e) { 789 | if (e.shiftKey) { 790 | $(this).toggleClass('trSelected'); 791 | g.multisel = true; 792 | this.focus(); 793 | $(g.gDiv).noSelect(); 794 | } 795 | if (e.ctrlKey || e.metaKey) { 796 | $(this).toggleClass('trSelected'); 797 | g.multisel = true; 798 | this.focus(); 799 | } 800 | }).on('mouseup', function (e) { 801 | if (g.multisel && ! (e.ctrlKey || e.metaKey)) { 802 | g.multisel = false; 803 | $(g.gDiv).noSelect(false); 804 | } 805 | }).on('dblclick', function () { 806 | if (p.onDoubleClick) { 807 | p.onDoubleClick(this, g, p); 808 | } 809 | }).hover(function (e) { 810 | if (g.multisel && e.shiftKey) { 811 | $(this).toggleClass('trSelected'); 812 | } 813 | }, function () {}); 814 | if (browser.msie && browser.version < 7.0) { 815 | $(this).hover(function () { 816 | $(this).addClass('trOver'); 817 | }, function () { 818 | $(this).removeClass('trOver'); 819 | }); 820 | } 821 | }, 822 | 823 | combo_flag: true, 824 | combo_resetIndex: function(selObj) 825 | { 826 | if(this.combo_flag) { 827 | selObj.selectedIndex = 0; 828 | } 829 | this.combo_flag = true; 830 | }, 831 | combo_doSelectAction: function(selObj) 832 | { 833 | eval( selObj.options[selObj.selectedIndex].value ); 834 | selObj.selectedIndex = 0; 835 | this.combo_flag = false; 836 | }, 837 | //Add title attribute to div if cell contents is truncated 838 | addTitleToCell: function(tdDiv) { 839 | if(p.addTitleToCell) { 840 | var $span = $('').css('display', 'none'), 841 | $div = (tdDiv instanceof jQuery) ? tdDiv : $(tdDiv), 842 | div_w = $div.outerWidth(), 843 | span_w = 0; 844 | 845 | $('body').children(':first').before($span); 846 | $span.html($div.html()); 847 | $span.css('font-size', '' + $div.css('font-size')); 848 | $span.css('padding-left', '' + $div.css('padding-left')); 849 | span_w = $span.innerWidth(); 850 | $span.remove(); 851 | 852 | if(span_w > div_w) { 853 | $div.attr('title', $div.text()); 854 | } else { 855 | $div.removeAttr('title'); 856 | } 857 | } 858 | }, 859 | autoResizeColumn: function (obj) { 860 | if(!p.dblClickResize) { 861 | return; 862 | } 863 | var n = $('div', this.cDrag).index(obj), 864 | $th = $('th:visible div:eq(' + n + ')', this.hDiv), 865 | ol = parseInt(obj.style.left, 10), 866 | ow = $th.width(), 867 | nw = 0, 868 | nl = 0, 869 | $span = $(''); 870 | $('body').children(':first').before($span); 871 | $span.html($th.html()); 872 | $span.css('font-size', '' + $th.css('font-size')); 873 | $span.css('padding-left', '' + $th.css('padding-left')); 874 | $span.css('padding-right', '' + $th.css('padding-right')); 875 | nw = $span.width(); 876 | $('tr', this.bDiv).each(function () { 877 | var $tdDiv = $('td:visible div:eq(' + n + ')', this), 878 | spanW = 0; 879 | $span.html($tdDiv.html()); 880 | $span.css('font-size', '' + $tdDiv.css('font-size')); 881 | $span.css('padding-left', '' + $tdDiv.css('padding-left')); 882 | $span.css('padding-right', '' + $tdDiv.css('padding-right')); 883 | spanW = $span.width(); 884 | nw = (spanW > nw) ? spanW : nw; 885 | }); 886 | $span.remove(); 887 | nw = (p.minWidth > nw) ? p.minWidth : nw; 888 | nl = ol + (nw - ow); 889 | $('div:eq(' + n + ')', this.cDrag).css('left', nl); 890 | this.colresize = { 891 | nw: nw, 892 | n: n 893 | }; 894 | g.dragEnd(); 895 | }, 896 | pager: 0 897 | }; 898 | 899 | g = p.getGridClass(g); //get the grid class 900 | 901 | if (p.colModel) { //create model if any 902 | thead = document.createElement('thead'); 903 | var tr = document.createElement('tr'); 904 | for (var i = 0; i < p.colModel.length; i++) { 905 | var cm = p.colModel[i]; 906 | var th = document.createElement('th'); 907 | $(th).attr('axis', 'col' + i); 908 | if( cm ) { // only use cm if its defined 909 | if ($.cookies) { 910 | var cookie_width = 'flexiwidths/'+cm.name; // Re-Store the widths in the cookies 911 | if( $.cookie(cookie_width) != undefined ) { 912 | cm.width = $.cookie(cookie_width); 913 | } 914 | } 915 | if( cm.display != undefined ) { 916 | th.innerHTML = cm.display; 917 | } 918 | if (cm.name && cm.sortable) { 919 | $(th).attr('abbr', cm.name); 920 | } 921 | if (cm.align) { 922 | th.align = cm.align; 923 | } 924 | if (cm.width) { 925 | $(th).attr('width', cm.width); 926 | } 927 | if ($(cm).attr('hide')) { 928 | th.hidden = true; 929 | } 930 | if (cm.process) { 931 | th.process = cm.process; 932 | } 933 | } else { 934 | th.innerHTML = ""; 935 | $(th).attr('width',30); 936 | } 937 | $(tr).append(th); 938 | } 939 | $(thead).append(tr); 940 | $(t).prepend(thead); 941 | } // end if p.colmodel 942 | //init divs 943 | g.gDiv = document.createElement('div'); //create global container 944 | g.mDiv = document.createElement('div'); //create title container 945 | g.hDiv = document.createElement('div'); //create header container 946 | g.bDiv = document.createElement('div'); //create body container 947 | g.vDiv = document.createElement('div'); //create grip 948 | g.rDiv = document.createElement('div'); //create horizontal resizer 949 | g.cDrag = document.createElement('div'); //create column drag 950 | g.block = document.createElement('div'); //creat blocker 951 | g.nDiv = document.createElement('div'); //create column show/hide popup 952 | g.nBtn = document.createElement('div'); //create column show/hide button 953 | g.iDiv = document.createElement('div'); //create editable layer 954 | g.tDiv = document.createElement('div'); //create toolbar 955 | g.sDiv = document.createElement('div'); 956 | g.pDiv = document.createElement('div'); //create pager container 957 | 958 | if(p.colResize === false) { //don't display column drag if we are not using it 959 | $(g.cDrag).css('display', 'none'); 960 | } 961 | 962 | if (!p.usepager) { 963 | g.pDiv.style.display = 'none'; 964 | } 965 | g.hTable = document.createElement('table'); 966 | g.gDiv.className = 'flexigrid'; 967 | if (p.width != 'auto') { 968 | g.gDiv.style.width = p.width + (isNaN(p.width) ? '' : 'px'); 969 | } 970 | //add conditional classes 971 | if (browser.msie) { 972 | $(g.gDiv).addClass('ie'); 973 | } 974 | if (p.novstripe) { 975 | $(g.gDiv).addClass('novstripe'); 976 | } 977 | $(t).before(g.gDiv); 978 | $(g.gDiv).append(t); 979 | //set toolbar 980 | if (p.buttons) { 981 | g.tDiv.className = 'tDiv'; 982 | var tDiv2 = document.createElement('div'); 983 | tDiv2.className = 'tDiv2'; 984 | for (var i = 0; i < p.buttons.length; i++) { 985 | var btn = p.buttons[i]; 986 | if (!btn.separator) { 987 | var btnDiv = document.createElement('div'); 988 | btnDiv.className = 'fbutton'; 989 | btnDiv.innerHTML = ("
") + (btn.hidename ? " " : btn.name) + ("
"); 990 | if (btn.bclass) $('span', btnDiv).addClass(btn.bclass).css({ 991 | paddingLeft: 20 992 | }); 993 | if (btn.bimage) // if bimage defined, use its string as an image url for this buttons style (RS) 994 | $('span',btnDiv).css( 'background', 'url('+btn.bimage+') no-repeat center left' ); 995 | $('span',btnDiv).css( 'paddingLeft', 20 ); 996 | 997 | if (btn.tooltip) // add title if exists (RS) 998 | $('span',btnDiv)[0].title = btn.tooltip; 999 | 1000 | btnDiv.onpress = btn.onpress; 1001 | btnDiv.name = btn.name; 1002 | if (btn.id) { 1003 | btnDiv.id = btn.id; 1004 | } 1005 | if (btn.onpress) { 1006 | $(btnDiv).click(function () { 1007 | this.onpress(this.id || this.name, g.gDiv); 1008 | }); 1009 | } 1010 | $(tDiv2).append(btnDiv); 1011 | if (browser.msie && browser.version < 7.0) { 1012 | $(btnDiv).hover(function () { 1013 | $(this).addClass('fbOver'); 1014 | }, function () { 1015 | $(this).removeClass('fbOver'); 1016 | }); 1017 | } 1018 | } else { 1019 | $(tDiv2).append("
"); 1020 | } 1021 | } 1022 | $(g.tDiv).append(tDiv2); 1023 | $(g.tDiv).append("
"); 1024 | $(g.gDiv).prepend(g.tDiv); 1025 | } 1026 | g.hDiv.className = 'hDiv'; 1027 | 1028 | // Define a combo button set with custom action'ed calls when clicked. 1029 | if( p.combobuttons && $(g.tDiv2) ) 1030 | { 1031 | var btnDiv = document.createElement('div'); 1032 | btnDiv.className = 'fbutton'; 1033 | 1034 | var tSelect = document.createElement('select'); 1035 | $(tSelect).change( function () { g.combo_doSelectAction( tSelect ) } ); 1036 | $(tSelect).click( function () { g.combo_resetIndex( tSelect) } ); 1037 | tSelect.className = 'cselect'; 1038 | $(btnDiv).append(tSelect); 1039 | 1040 | for (i=0;i'); 1075 | $('div', g.hDiv).append(g.hTable); 1076 | var thead = $("thead:first", t).get(0); 1077 | if (thead) $(g.hTable).append(thead); 1078 | thead = null; 1079 | if (!p.colmodel) var ci = 0; 1080 | $('thead tr:first th', g.hDiv).each(function () { 1081 | var thdiv = document.createElement('div'); 1082 | if ($(this).attr('abbr')) { 1083 | $(this).click(function (e) { 1084 | if (!$(this).hasClass('thOver')) return false; 1085 | var obj = (e.target || e.srcElement); 1086 | if (obj.href || obj.type) return true; 1087 | g.changeSort(this); 1088 | }); 1089 | if ($(this).attr('abbr') == p.sortname) { 1090 | this.className = 'sorted'; 1091 | thdiv.className = 's' + p.sortorder; 1092 | } 1093 | } 1094 | if (this.hidden) { 1095 | $(this).hide(); 1096 | } 1097 | if (!p.colmodel) { 1098 | $(this).attr('axis', 'col' + ci++); 1099 | } 1100 | 1101 | // if there isn't a default width, then the column headers don't match 1102 | // i'm sure there is a better way, but this at least stops it failing 1103 | if (this.width == '') { 1104 | this.width = 100; 1105 | } 1106 | 1107 | $(thdiv).css({ 1108 | textAlign: this.align, 1109 | width: this.width + 'px' 1110 | }); 1111 | thdiv.innerHTML = this.innerHTML; 1112 | $(this).empty().append(thdiv).removeAttr('width').mousedown(function (e) { 1113 | g.dragStart('colMove', e, this); 1114 | }).hover(function () { 1115 | if (!g.colresize && !$(this).hasClass('thMove') && !g.colCopy) { 1116 | $(this).addClass('thOver'); 1117 | } 1118 | if ($(this).attr('abbr') != p.sortname && !g.colCopy && !g.colresize && $(this).attr('abbr')) { 1119 | $('div', this).addClass('s' + p.sortorder); 1120 | } else if ($(this).attr('abbr') == p.sortname && !g.colCopy && !g.colresize && $(this).attr('abbr')) { 1121 | var no = (p.sortorder == 'asc') ? 'desc' : 'asc'; 1122 | $('div', this).removeClass('s' + p.sortorder).addClass('s' + no); 1123 | } 1124 | if (g.colCopy) { 1125 | var n = $('th', g.hDiv).index(this); 1126 | if (n == g.dcoln) { 1127 | return false; 1128 | } 1129 | if (n < g.dcoln) { 1130 | $(this).append(g.cdropleft); 1131 | } else { 1132 | $(this).append(g.cdropright); 1133 | } 1134 | g.dcolt = n; 1135 | } else if (!g.colresize) { 1136 | var nv = $('th:visible', g.hDiv).index(this); 1137 | var onl = parseInt($('div:eq(' + nv + ')', g.cDrag).css('left'), 10); 1138 | var nw = jQuery(g.nBtn).outerWidth(); 1139 | var nl = onl - nw + Math.floor(p.cgwidth / 2); 1140 | $(g.nDiv).hide(); 1141 | $(g.nBtn).hide(); 1142 | $(g.nBtn).css({ 1143 | 'left': nl, 1144 | top: g.hDiv.offsetTop 1145 | }).show(); 1146 | var ndw = parseInt($(g.nDiv).width(), 10); 1147 | $(g.nDiv).css({ 1148 | top: g.bDiv.offsetTop 1149 | }); 1150 | if ((nl + ndw) > $(g.gDiv).width()) { 1151 | $(g.nDiv).css('left', onl - ndw + 1); 1152 | } else { 1153 | $(g.nDiv).css('left', nl); 1154 | } 1155 | if ($(this).hasClass('sorted')) { 1156 | $(g.nBtn).addClass('srtd'); 1157 | } else { 1158 | $(g.nBtn).removeClass('srtd'); 1159 | } 1160 | } 1161 | }, function () { 1162 | $(this).removeClass('thOver'); 1163 | if ($(this).attr('abbr') != p.sortname) { 1164 | $('div', this).removeClass('s' + p.sortorder); 1165 | } else if ($(this).attr('abbr') == p.sortname) { 1166 | var no = (p.sortorder == 'asc') ? 'desc' : 'asc'; 1167 | $('div', this).addClass('s' + p.sortorder).removeClass('s' + no); 1168 | } 1169 | if (g.colCopy) { 1170 | $(g.cdropleft).remove(); 1171 | $(g.cdropright).remove(); 1172 | g.dcolt = null; 1173 | } 1174 | }); //wrap content 1175 | }); 1176 | //set bDiv 1177 | g.bDiv.className = 'bDiv'; 1178 | $(t).before(g.bDiv); 1179 | $(g.bDiv).css({ 1180 | height: (p.height == 'auto') ? 'auto' : p.height + "px" 1181 | }).scroll(function (e) { 1182 | g.scroll() 1183 | }).append(t); 1184 | if (p.height == 'auto') { 1185 | $('table', g.bDiv).addClass('autoht'); 1186 | } 1187 | //add td & row properties 1188 | g.addCellProp(); 1189 | g.addRowProp(); 1190 | //set cDrag only if we are using it 1191 | if (p.colResize === true) { 1192 | var cdcol = $('thead tr:first th:first', g.hDiv).get(0); 1193 | if(cdcol !== null) { 1194 | g.cDrag.className = 'cDrag'; 1195 | g.cdpad = 0; 1196 | g.cdpad += (isNaN(parseInt($('div', cdcol).css('borderLeftWidth'), 10)) ? 0 : parseInt($('div', cdcol).css('borderLeftWidth'), 10)); 1197 | g.cdpad += (isNaN(parseInt($('div', cdcol).css('borderRightWidth'), 10)) ? 0 : parseInt($('div', cdcol).css('borderRightWidth'), 10)); 1198 | g.cdpad += (isNaN(parseInt($('div', cdcol).css('paddingLeft'), 10)) ? 0 : parseInt($('div', cdcol).css('paddingLeft'), 10)); 1199 | g.cdpad += (isNaN(parseInt($('div', cdcol).css('paddingRight'), 10)) ? 0 : parseInt($('div', cdcol).css('paddingRight'), 10)); 1200 | g.cdpad += (isNaN(parseInt($(cdcol).css('borderLeftWidth'), 10)) ? 0 : parseInt($(cdcol).css('borderLeftWidth'), 10)); 1201 | g.cdpad += (isNaN(parseInt($(cdcol).css('borderRightWidth'), 10)) ? 0 : parseInt($(cdcol).css('borderRightWidth'), 10)); 1202 | g.cdpad += (isNaN(parseInt($(cdcol).css('paddingLeft'), 10)) ? 0 : parseInt($(cdcol).css('paddingLeft'), 10)); 1203 | g.cdpad += (isNaN(parseInt($(cdcol).css('paddingRight'), 10)) ? 0 : parseInt($(cdcol).css('paddingRight'), 10)); 1204 | $(g.bDiv).before(g.cDrag); 1205 | var cdheight = $(g.bDiv).height(); 1206 | var hdheight = $(g.hDiv).height(); 1207 | $(g.cDrag).css({ 1208 | top: -hdheight + 'px' 1209 | }); 1210 | $('thead tr:first th', g.hDiv).each(function() { 1211 | var cgDiv = document.createElement('div'); 1212 | $(g.cDrag).append(cgDiv); 1213 | if (!p.cgwidth) { 1214 | p.cgwidth = $(cgDiv).width(); 1215 | } 1216 | $(cgDiv).css({ 1217 | height: cdheight + hdheight 1218 | }).mousedown(function(e) { 1219 | g.dragStart('colresize', e, this); 1220 | }).dblclick(function(e) { 1221 | g.autoResizeColumn(this); 1222 | }); 1223 | if (browser.msie && browser.version < 7.0) { 1224 | g.fixHeight($(g.gDiv).height()); 1225 | $(cgDiv).hover(function() { 1226 | g.fixHeight(); 1227 | $(this).addClass('dragging'); 1228 | }, function() { 1229 | if(!g.colresize) { 1230 | $(this).removeClass('dragging'); 1231 | } 1232 | }); 1233 | } 1234 | }); 1235 | } 1236 | } 1237 | //add strip 1238 | if (p.striped) { 1239 | $('tbody tr:odd', g.bDiv).addClass('erow'); 1240 | } 1241 | if (p.resizable && p.height != 'auto') { 1242 | g.vDiv.className = 'vGrip'; 1243 | $(g.vDiv).mousedown(function (e) { 1244 | g.dragStart('vresize', e); 1245 | }).html(''); 1246 | $(g.bDiv).after(g.vDiv); 1247 | } 1248 | if (p.resizable && p.width != 'auto' && !p.nohresize) { 1249 | g.rDiv.className = 'hGrip'; 1250 | $(g.rDiv).mousedown(function (e) { 1251 | g.dragStart('vresize', e, true); 1252 | }).html('').css('height', $(g.gDiv).height()); 1253 | if (browser.msie && browser.version < 7.0) { 1254 | $(g.rDiv).hover(function () { 1255 | $(this).addClass('hgOver'); 1256 | }, function () { 1257 | $(this).removeClass('hgOver'); 1258 | }); 1259 | } 1260 | $(g.gDiv).append(g.rDiv); 1261 | } 1262 | // add pager 1263 | if (p.usepager) { 1264 | g.pDiv.className = 'pDiv'; 1265 | g.pDiv.innerHTML = '
'; 1266 | $(g.bDiv).after(g.pDiv); 1267 | var html = '
' + p.pagetext + ' ' + p.outof + ' 1
'; 1268 | $('div', g.pDiv).html(html); 1269 | $('.pReload', g.pDiv).click(function () { 1270 | g.populate(); 1271 | }); 1272 | $('.pFirst', g.pDiv).click(function () { 1273 | g.changePage('first'); 1274 | }); 1275 | $('.pPrev', g.pDiv).click(function () { 1276 | g.changePage('prev'); 1277 | }); 1278 | $('.pNext', g.pDiv).click(function () { 1279 | g.changePage('next'); 1280 | }); 1281 | $('.pLast', g.pDiv).click(function () { 1282 | g.changePage('last'); 1283 | }); 1284 | $('.pcontrol input', g.pDiv).keydown(function (e) { 1285 | if (e.keyCode == 13) { 1286 | g.changePage('input'); 1287 | } 1288 | }); 1289 | if (browser.msie && browser.version < 7) $('.pButton', g.pDiv).hover(function () { 1290 | $(this).addClass('pBtnOver'); 1291 | }, function () { 1292 | $(this).removeClass('pBtnOver'); 1293 | }); 1294 | if (p.useRp) { 1295 | var opt = '', 1296 | sel = ''; 1297 | for (var nx = 0; nx < p.rpOptions.length; nx++) { 1298 | if (p.rp == p.rpOptions[nx]) sel = 'selected="selected"'; 1299 | else sel = ''; 1300 | opt += ""; 1301 | } 1302 | $('.pDiv2', g.pDiv).prepend("
"); 1303 | $('select', g.pDiv).change(function () { 1304 | if (p.onRpChange) { 1305 | p.onRpChange(+this.value); 1306 | } else { 1307 | p.newp = 1; 1308 | p.rp = +this.value; 1309 | g.populate(); 1310 | } 1311 | }); 1312 | } 1313 | //add search button 1314 | if (p.searchitems) { 1315 | $('.pDiv2', g.pDiv).prepend("
"); 1316 | $('.pSearch', g.pDiv).click(function () { 1317 | $(g.sDiv).slideToggle('fast', function () { 1318 | $('.sDiv:visible input:first', g.gDiv).trigger('focus'); 1319 | }); 1320 | }); 1321 | //add search box 1322 | g.sDiv.className = 'sDiv'; 1323 | var sitems = p.searchitems; 1324 | var sopt = '', sel = ''; 1325 | for (var s = 0; s < sitems.length; s++) { 1326 | if (p.qtype === '' && sitems[s].isdefault === true) { 1327 | p.qtype = sitems[s].name; 1328 | sel = 'selected="selected"'; 1329 | } else { 1330 | sel = ''; 1331 | } 1332 | sopt += ""; 1333 | } 1334 | if (p.qtype === '') { 1335 | p.qtype = sitems[0].name; 1336 | } 1337 | $(g.sDiv).append("
" + p.findtext + 1338 | " "+ 1339 | "
"); 1340 | //Split into separate selectors because of bug in jQuery 1.3.2 1341 | $('input[name=q]', g.sDiv).keydown(function (e) { 1342 | if (e.keyCode == 13) { 1343 | g.doSearch(); 1344 | } 1345 | }); 1346 | $('select[name=qtype]', g.sDiv).keydown(function (e) { 1347 | if (e.keyCode == 13) { 1348 | g.doSearch(); 1349 | } 1350 | }); 1351 | $('input[value=Clear]', g.sDiv).click(function () { 1352 | $('input[name=q]', g.sDiv).val(''); 1353 | p.query = ''; 1354 | g.doSearch(); 1355 | }); 1356 | $(g.bDiv).after(g.sDiv); 1357 | } 1358 | } 1359 | $(g.pDiv, g.sDiv).append("
"); 1360 | // add title 1361 | if (p.title) { 1362 | g.mDiv.className = 'mDiv'; 1363 | g.mDiv.innerHTML = '
' + p.title + '
'; 1364 | $(g.gDiv).prepend(g.mDiv); 1365 | if (p.showTableToggleBtn) { 1366 | $(g.mDiv).append('
'); 1367 | $('div.ptogtitle', g.mDiv).click(function () { 1368 | $(g.gDiv).toggleClass('hideBody'); 1369 | $(this).toggleClass('vsble'); 1370 | }); 1371 | } 1372 | } 1373 | //setup cdrops 1374 | g.cdropleft = document.createElement('span'); 1375 | g.cdropleft.className = 'cdropleft'; 1376 | g.cdropright = document.createElement('span'); 1377 | g.cdropright.className = 'cdropright'; 1378 | //add block 1379 | g.block.className = 'gBlock'; 1380 | var gh = $(g.bDiv).height(); 1381 | var gtop = g.bDiv.offsetTop; 1382 | $(g.block).css({ 1383 | width: g.bDiv.style.width, 1384 | height: gh, 1385 | background: 'white', 1386 | position: 'relative', 1387 | marginBottom: (gh * -1), 1388 | zIndex: 1, 1389 | top: gtop, 1390 | left: '0px' 1391 | }); 1392 | $(g.block).fadeTo(0, p.blockOpacity); 1393 | // add column control 1394 | if ($('th', g.hDiv).length) { 1395 | g.nDiv.className = 'nDiv'; 1396 | g.nDiv.innerHTML = "
"; 1397 | $(g.nDiv).css({ 1398 | marginBottom: (gh * -1), 1399 | display: 'none', 1400 | top: gtop 1401 | }).noSelect(); 1402 | var cn = 0; 1403 | $('th div', g.hDiv).each(function () { 1404 | var kcol = $("th[axis='col" + cn + "']", g.hDiv)[0]; 1405 | var chk = 'checked="checked"'; 1406 | if (kcol.style.display == 'none') { 1407 | chk = ''; 1408 | } 1409 | $('tbody', g.nDiv).append('' + this.innerHTML + ''); 1410 | cn++; 1411 | }); 1412 | if (browser.msie && browser.version < 7.0) $('tr', g.nDiv).hover(function () { 1413 | $(this).addClass('ndcolover'); 1414 | }, function () { 1415 | $(this).removeClass('ndcolover'); 1416 | }); 1417 | $('td.ndcol2', g.nDiv).click(function () { 1418 | if ($('input:checked', g.nDiv).length <= p.minColToggle && $(this).prev().find('input')[0].checked) return false; 1419 | return g.toggleCol($(this).prev().find('input').val()); 1420 | }); 1421 | $('input.togCol', g.nDiv).click(function () { 1422 | if ($('input:checked', g.nDiv).length < p.minColToggle && this.checked === false) return false; 1423 | $(this).parent().next().trigger('click'); 1424 | }); 1425 | $(g.gDiv).prepend(g.nDiv); 1426 | $(g.nBtn).addClass('nBtn') 1427 | .html('
') 1428 | .attr('title', 'Hide/Show Columns') 1429 | .click(function () { 1430 | $(g.nDiv).toggle(); 1431 | return true; 1432 | } 1433 | ); 1434 | if (p.showToggleBtn) { 1435 | $(g.gDiv).prepend(g.nBtn); 1436 | } 1437 | } 1438 | // add date edit layer 1439 | $(g.iDiv).addClass('iDiv').css({ 1440 | display: 'none' 1441 | }); 1442 | $(g.bDiv).append(g.iDiv); 1443 | // add flexigrid events 1444 | $(g.bDiv).hover(function () { 1445 | $(g.nDiv).hide(); 1446 | $(g.nBtn).hide(); 1447 | }, function () { 1448 | if (g.multisel) { 1449 | g.multisel = false; 1450 | } 1451 | }); 1452 | $(g.gDiv).hover(function () {}, function () { 1453 | $(g.nDiv).hide(); 1454 | $(g.nBtn).hide(); 1455 | }); 1456 | //add document events 1457 | $(document).mousemove(function (e) { 1458 | g.dragMove(e); 1459 | }).mouseup(function (e) { 1460 | g.dragEnd(); 1461 | }).hover(function () {}, function () { 1462 | g.dragEnd(); 1463 | }); 1464 | //browser adjustments 1465 | if (browser.msie && browser.version < 7.0) { 1466 | $('.hDiv,.bDiv,.mDiv,.pDiv,.vGrip,.tDiv, .sDiv', g.gDiv).css({ 1467 | width: '100%' 1468 | }); 1469 | $(g.gDiv).addClass('ie6'); 1470 | if (p.width != 'auto') { 1471 | $(g.gDiv).addClass('ie6fullwidthbug'); 1472 | } 1473 | } 1474 | g.rePosDrag(); 1475 | g.fixHeight(); 1476 | //make grid functions accessible 1477 | t.p = p; 1478 | t.grid = g; 1479 | // load data 1480 | if (p.url && p.autoload) { 1481 | g.populate(); 1482 | } 1483 | return t; 1484 | }; 1485 | var docloaded = false; 1486 | $(document).ready(function () { 1487 | docloaded = true; 1488 | }); 1489 | $.fn.flexigrid = function (p) { 1490 | return this.each(function () { 1491 | if (!docloaded) { 1492 | $(this).hide(); 1493 | var t = this; 1494 | $(document).ready(function () { 1495 | $.addFlex(t, p); 1496 | }); 1497 | } else { 1498 | $.addFlex(this, p); 1499 | } 1500 | }); 1501 | }; //end flexigrid 1502 | $.fn.flexReload = function (p) { // function to reload grid 1503 | return this.each(function () { 1504 | if (this.grid && this.p.url) this.grid.populate(); 1505 | }); 1506 | }; //end flexReload 1507 | $.fn.flexOptions = function (p) { //function to update general options 1508 | return this.each(function () { 1509 | if (this.grid) $.extend(this.p, p); 1510 | }); 1511 | }; //end flexOptions 1512 | $.fn.flexToggleCol = function (cid, visible) { // function to reload grid 1513 | return this.each(function () { 1514 | if (this.grid) this.grid.toggleCol(cid, visible); 1515 | }); 1516 | }; //end flexToggleCol 1517 | $.fn.flexAddData = function (data) { // function to add data to grid 1518 | return this.each(function () { 1519 | if (this.grid) this.grid.addData(data); 1520 | }); 1521 | }; 1522 | $.fn.noSelect = function (p) { //no select plugin by me :-) 1523 | var prevent = (p === null) ? true : p; 1524 | if (prevent) { 1525 | return this.each(function () { 1526 | if (browser.msie || browser.safari) $(this).bind('selectstart', function () { 1527 | return false; 1528 | }); 1529 | else if (browser.mozilla) { 1530 | $(this).css('MozUserSelect', 'none'); 1531 | $('body').trigger('focus'); 1532 | } else if (browser.opera) $(this).bind('mousedown', function () { 1533 | return false; 1534 | }); 1535 | else $(this).attr('unselectable', 'on'); 1536 | }); 1537 | } else { 1538 | return this.each(function () { 1539 | if (browser.msie || browser.safari) $(this).unbind('selectstart'); 1540 | else if (browser.mozilla) $(this).css('MozUserSelect', 'inherit'); 1541 | else if (browser.opera) $(this).unbind('mousedown'); 1542 | else $(this).removeAttr('unselectable', 'on'); 1543 | }); 1544 | } 1545 | }; //end noSelect 1546 | $.fn.flexSearch = function(p) { // function to search grid 1547 | return this.each( function() { if (this.grid&&this.p.searchitems) this.grid.doSearch(); }); 1548 | }; //end flexSearch 1549 | $.fn.selectedRows = function (p) { // Returns the selected rows as an array, taken and adapted from http://stackoverflow.com/questions/11868404/flexigrid-get-selected-row-columns-values 1550 | var arReturn = []; 1551 | var arRow = []; 1552 | var selector = $(this.selector + ' .trSelected'); 1553 | 1554 | 1555 | $(selector).each(function (i, row) { 1556 | arRow = []; 1557 | var idr = $(row).data('id'); 1558 | $.each(row.cells, function (c, cell) { 1559 | var col = cell.abbr; 1560 | var val = cell.firstChild.innerHTML; 1561 | if (val == ' ') val = ''; // Trim the content 1562 | var idx = cell.cellIndex; 1563 | 1564 | arRow.push({ 1565 | Column: col, // Column identifier 1566 | Value: val, // Column value 1567 | CellIndex: idx, // Cell index 1568 | RowIdentifier: idr // Identifier of this row element 1569 | }); 1570 | }); 1571 | arReturn.push(arRow); 1572 | }); 1573 | return arReturn; 1574 | }; 1575 | })(jQuery); 1576 | -------------------------------------------------------------------------------- /chrome-extension/jquery-2.1.4.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery v2.1.4 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */ 2 | !function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)+1>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b="length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+K.uid++}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){ 3 | return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b)},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthx",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,ba=/<([\w:]+)/,ca=/<|&#?\w+;/,da=/<(?:script|style|link)/i,ea=/checked\s*(?:[^=]|=\s*.checked.)/i,fa=/^$|\/(?:java|ecma)script/i,ga=/^true\/(.*)/,ha=/^\s*\s*$/g,ia={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ia.optgroup=ia.option,ia.tbody=ia.tfoot=ia.colgroup=ia.caption=ia.thead,ia.th=ia.td;function ja(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function ka(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function la(a){var b=ga.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function ma(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function na(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function oa(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pa(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=oa(h),f=oa(a),d=0,e=f.length;e>d;d++)pa(f[d],g[d]);if(b)if(c)for(f=f||oa(a),g=g||oa(h),d=0,e=f.length;e>d;d++)na(f[d],g[d]);else na(a,h);return g=oa(h,"script"),g.length>0&&ma(g,!i&&oa(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(ca.test(e)){f=f||k.appendChild(b.createElement("div")),g=(ba.exec(e)||["",""])[1].toLowerCase(),h=ia[g]||ia._default,f.innerHTML=h[1]+e.replace(aa,"<$1>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=oa(k.appendChild(e),"script"),i&&ma(f),c)){j=0;while(e=f[j++])fa.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=ja(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(oa(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&ma(oa(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(oa(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!da.test(a)&&!ia[(ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(aa,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(oa(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(oa(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&ea.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(oa(c,"script"),ka),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,oa(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,la),j=0;g>j;j++)h=f[j],fa.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(ha,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qa,ra={};function sa(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function ta(a){var b=l,c=ra[a];return c||(c=sa(a,b),"none"!==c&&c||(qa=(qa||n("