commit d646c300cbf75ac887038d8fc1f344102c623dce Author: Tretter Date: Tue Feb 7 20:04:47 2017 +0000 initial checkin from subversion diff --git a/platforms/android/AndroidManifest.xml b/platforms/android/AndroidManifest.xml new file mode 100644 index 0000000..bd42134 --- /dev/null +++ b/platforms/android/AndroidManifest.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/platforms/android/build.xml b/platforms/android/build.xml new file mode 100644 index 0000000..4278dba --- /dev/null +++ b/platforms/android/build.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/platforms/android/cordova/build b/platforms/android/cordova/build new file mode 100644 index 0000000..752945f --- /dev/null +++ b/platforms/android/cordova/build @@ -0,0 +1,35 @@ +#!/usr/bin/env node + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var build = require('./lib/build'), + reqs = require('./lib/check_reqs'), + args = process.argv; + +// Support basic help commands +if(args[2] == '--help' || args[2] == '/?' || args[2] == '-h' || + args[2] == 'help' || args[2] == '-help' || args[2] == '/help') { + build.help(); +} else if(reqs.run()) { + build.run(args[2]); + process.exit(0); +} else { + process.exit(2); +} \ No newline at end of file diff --git a/platforms/android/cordova/check_reqs b/platforms/android/cordova/check_reqs new file mode 100644 index 0000000..4a8abee --- /dev/null +++ b/platforms/android/cordova/check_reqs @@ -0,0 +1,27 @@ +#!/usr/bin/env node + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var check_reqs = require('./lib/check_reqs'); + +if(!check_reqs.run()) { + process.exit(2); +} + diff --git a/platforms/android/cordova/clean b/platforms/android/cordova/clean new file mode 100644 index 0000000..6b72e71 --- /dev/null +++ b/platforms/android/cordova/clean @@ -0,0 +1,34 @@ +#!/usr/bin/env node + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var clean = require('./lib/clean'), + reqs = require('./lib/check_reqs'), + args = process.argv; + +// Usage support for when args are given +if(args.length > 2) { + clean.help(); +} else if(reqs.run()) { + clean.run(); + process.exit(0); +} else { + process.exit(2); +} \ No newline at end of file diff --git a/platforms/android/cordova/lib/appinfo.js b/platforms/android/cordova/lib/appinfo.js new file mode 100644 index 0000000..1f8ebe2 --- /dev/null +++ b/platforms/android/cordova/lib/appinfo.js @@ -0,0 +1,41 @@ +#!/usr/bin/env node + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var path = require('path'); +var fs = require('fs'); +var cachedAppInfo = null; + +function readAppInfoFromManifest() { + var manifestPath = path.join(__dirname, '..', '..', 'AndroidManifest.xml'); + var manifestData = fs.readFileSync(manifestPath, {encoding:'utf8'}); + var packageName = /\bpackage\s*=\s*"(.+?)"/.exec(manifestData); + if (!packageName) throw new Error('Could not find package name within ' + manifestPath); + var activityTag = //.exec(manifestData); + if (!activityTag) throw new Error('Could not find within ' + manifestPath); + var activityName = /\bandroid:name\s*=\s*"(.+?)"/.exec(activityTag); + if (!activityName) throw new Error('Could not find android:name within ' + manifestPath); + + return packageName[1] + '/.' + activityName[1]; +} + +exports.getActivityName = function() { + return cachedAppInfo = cachedAppInfo || readAppInfoFromManifest(); +}; diff --git a/platforms/android/cordova/lib/build.js b/platforms/android/cordova/lib/build.js new file mode 100644 index 0000000..84e4e02 --- /dev/null +++ b/platforms/android/cordova/lib/build.js @@ -0,0 +1,89 @@ +#!/usr/bin/env node + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var shell = require('shelljs'), + clean = require('./clean'), + path = require('path'), + fs = require('fs'), + ROOT = path.join(__dirname, '..', '..'); + +/* + * Builds the project with ant. + */ +module.exports.run = function(build_type) { + //default build type + build_type = typeof build_type !== 'undefined' ? build_type : "--debug"; + var cmd; + switch(build_type) { + case '--debug' : + clean.run(); + cmd = 'ant debug -f ' + path.join(ROOT, 'build.xml'); + break; + case '--release' : + clean.run(); + cmd = 'ant release -f ' + path.join(ROOT, 'build.xml'); + break; + case '--nobuild' : + console.log('Skipping build...'); + break; + default : + console.error('Build option \'' + build_type + '\' not recognized.'); + process.exit(2); + break; + } + if(cmd) { + var result = shell.exec(cmd, {silent:false, async:false}); + if(result.code > 0) { + console.error('ERROR: Failed to build android project.'); + console.error(result.output); + process.exit(2); + } + } +} + +/* + * Gets the path to the apk file, if not such file exists then + * the script will error out. (should we error or just return undefined?) + */ +module.exports.get_apk = function() { + if(fs.existsSync(path.join(ROOT, 'bin'))) { + var bin_files = fs.readdirSync(path.join(ROOT, 'bin')); + for (file in bin_files) { + if(path.extname(bin_files[file]) == '.apk') { + return path.join(ROOT, 'bin', bin_files[file]); + } + } + console.error('ERROR : No .apk found in \'bin\' folder'); + process.exit(2); + } else { + console.error('ERROR : unable to find project bin folder, could not locate .apk'); + process.exit(2); + } +} + +module.exports.help = function() { + console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'corodva', 'build')) + ' [build_type]'); + console.log('Build Types : '); + console.log(' \'--debug\': Default build, will build project in using ant debug'); + console.log(' \'--release\': will build project using ant release'); + console.log(' \'--nobuild\': will skip build process (can be used with run command)'); + process.exit(0); +} \ No newline at end of file diff --git a/platforms/android/cordova/lib/check_reqs.js b/platforms/android/cordova/lib/check_reqs.js new file mode 100644 index 0000000..c064499 --- /dev/null +++ b/platforms/android/cordova/lib/check_reqs.js @@ -0,0 +1,78 @@ +#!/usr/bin/env node + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var shell = require('shelljs'), + path = require('path'), + fs = require('fs'), + ROOT = path.join(__dirname, '..', '..'); + +// Get valid target from framework/project.properties +module.exports.get_target = function() { + if(fs.existsSync(path.join(ROOT, 'framework', 'project.properties'))) { + var target = shell.grep(/target=android-[\d+]/, path.join(ROOT, 'framework', 'project.properties')); + return target.split('=')[1].replace('\n', '').replace('\r', '').replace(' ', ''); + } else if (fs.existsSync(path.join(ROOT, 'project.properties'))) { + // if no target found, we're probably in a project and project.properties is in ROOT. + var target = shell.grep(/target=android-[\d+]/, path.join(ROOT, 'project.properties')); + return target.split('=')[1].replace('\n', '').replace('\r', '').replace(' ', ''); + } +} + +module.exports.check_ant = function() { + var test = shell.exec('ant -version', {silent:true, async:false}); + if(test.code > 0) { + console.error('ERROR : executing command \'ant\', make sure you have ant installed and added to your path.'); + return false; + } + return true; +} + +module.exports.check_java = function() { + if(process.env.JAVA_HOME) { + var test = shell.exec('java', {silent:true, async:false}); + if(test.code > 0) { + console.error('ERROR : executing command \'java\', make sure you java environment is set up. Including your JDK and JRE.'); + return false; + } + return true; + } else { + console.error('ERROR : Make sure JAVA_HOME is set, as well as paths to your JDK and JRE for java.'); + return false; + } +} + +module.exports.check_android = function() { + var valid_target = this.get_target(); + var targets = shell.exec('android list targets', {silent:true, async:false}); + + if(targets.code > 0 && targets.output.match(/command\snot\sfound/)) { + console.error('The command \"android\" failed. Make sure you have the latest Android SDK installed, and the \"android\" command (inside the tools/ folder) is added to your path.'); + return false; + } else if(!targets.output.match(valid_target)) { + console.error('Please install Android target ' + valid_target.split('-')[1] + ' (the Android newest SDK). Make sure you have the latest Android tools installed as well. Run \"android\" from your command-line to install/update any missing SDKs or tools.'); + return false; + } + return true; +} + +module.exports.run = function() { + return this.check_ant() && this.check_java && this.check_android(); +} diff --git a/platforms/android/cordova/lib/clean.js b/platforms/android/cordova/lib/clean.js new file mode 100644 index 0000000..579a5fa --- /dev/null +++ b/platforms/android/cordova/lib/clean.js @@ -0,0 +1,43 @@ +#!/usr/bin/env node + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var shell = require('shelljs'), + path = require('path'), + ROOT = path.join(__dirname, '..', '..'); + +/* + * Cleans the project using ant + */ +module.exports.run = function() { + var cmd = 'ant clean -f ' + path.join(ROOT, 'build.xml'); + var result = shell.exec(cmd, {silent:false, async:false}); + if (result.code > 0) { + console.error('ERROR: Failed to clean android project.'); + console.error(result.output); + process.exit(2); + } +} + +module.exports.help = function() { + console.log('Usage: ' + path.relative(process.cwd(), process.argv[1])); + console.log('Cleans the project directory.'); + process.exit(0); +} \ No newline at end of file diff --git a/platforms/android/cordova/lib/device.js b/platforms/android/cordova/lib/device.js new file mode 100644 index 0000000..46686b6 --- /dev/null +++ b/platforms/android/cordova/lib/device.js @@ -0,0 +1,95 @@ +#!/usr/bin/env node + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var shell = require('shelljs'), + path = require('path'), + build = require('./build'), + appinfo = require('./appinfo'), + exec = require('child_process').exec, + ROOT = path.join(__dirname, '..', '..'); + +/** + * Returns a list of the device ID's found + */ +module.exports.list = function() { + var cmd = 'adb devices'; + var result = shell.exec(cmd, {silent:true, async:false}); + if (result.code > 0) { + console.error('Failed to execute android command \'' + cmd + '\'.'); + process.exit(2); + } else { + var response = result.output.split('\n'); + var device_list = []; + for (var i = 1; i < response.length; i++) { + if (response[i].match(/\w+\tdevice/) && !response[i].match(/emulator/)) { + device_list.push(response[i].replace(/\tdevice/, '').replace('\r', '')); + } + } + return device_list; + } +} + +/* + * Installs a previously built application on the device + * and launches it. + */ +module.exports.install = function(target) { + var device_list = this.list(); + if (device_list.length > 0) { + // default device + target = typeof target !== 'undefined' ? target : device_list[0]; + if (device_list.indexOf(target) > -1) { + var apk_path = build.get_apk(); + var launchName = appinfo.getActivityName(); + console.log('Installing app on device...'); + cmd = 'adb -s ' + target + ' install -r ' + apk_path; + var install = shell.exec(cmd, {silent:false, async:false}); + if (install.error || install.output.match(/Failure/)) { + console.error('ERROR : Failed to install apk to device : '); + console.error(install.output); + process.exit(2); + } + + //unlock screen + cmd = 'adb -s ' + target + ' shell input keyevent 82'; + shell.exec(cmd, {silent:true, async:false}); + + // launch the application + console.log('Launching application...'); + cmd = 'adb -s ' + target + ' shell am start -W -a android.intent.action.MAIN -n ' + launchName; + var launch = shell.exec(cmd, {silent:true, async:false}); + if(launch.code > 0) { + console.error('ERROR : Failed to launch application on emulator : ' + launch.error); + console.error(launch.output); + process.exit(2); + } else { + console.log('LANCH SUCCESS'); + } + } else { + console.error('ERROR : Unable to find target \'' + target + '\'.'); + console.error('Failed to deploy to device.'); + process.exit(2); + } + } else { + console.error('ERROR : Failed to deploy to device, no devices found.'); + process.exit(2); + } +} diff --git a/platforms/android/cordova/lib/emulator.js b/platforms/android/cordova/lib/emulator.js new file mode 100644 index 0000000..6f8a7dd --- /dev/null +++ b/platforms/android/cordova/lib/emulator.js @@ -0,0 +1,337 @@ +#!/usr/bin/env node + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var shell = require('shelljs'), + path = require('path'), + appinfo = require('./appinfo'), + build = require('./build'), + ROOT = path.join(__dirname, '..', '..'), + new_emulator = 'cordova_emulator'; + +/** + * Returns a list of emulator images in the form of objects + * { + name : , + path : , + target : , + abi : , + skin : + } + */ +module.exports.list_images = function() { + var cmd = 'android list avds'; + var result = shell.exec(cmd, {silent:true, async:false}); + if (result.code > 0) { + console.error('Failed to execute android command \'' + cmd + '\'.'); + process.exit(2); + } else { + var response = result.output.split('\n'); + var emulator_list = []; + for (var i = 1; i < response.length; i++) { + // To return more detailed information use img_obj + var img_obj = {}; + if (response[i].match(/Name:\s/)) { + img_obj['name'] = response[i].split('Name: ')[1].replace('\r', ''); + if (response[i + 1].match(/Path:\s/)) { + i++; + img_obj['path'] = response[i].split('Path: ')[1].replace('\r', ''); + } + if (response[i + 1].match(/\(API\slevel\s/)) { + i++; + img_obj['target'] = response[i].replace('\r', ''); + } + if (response[i + 1].match(/ABI:\s/)) { + i++; + img_obj['abi'] = response[i].split('ABI: ')[1].replace('\r', ''); + } + if (response[i + 1].match(/Skin:\s/)) { + i++; + img_obj['skin'] = response[i].split('Skin: ')[1].replace('\r', ''); + } + + emulator_list.push(img_obj); + } + /* To just return a list of names use this + if (response[i].match(/Name:\s/)) { + emulator_list.push(response[i].split('Name: ')[1].replace('\r', ''); + }*/ + + } + return emulator_list; + } +} + +/** + * Will return the closest avd to the projects target + * or undefined if no avds exist. + */ +module.exports.best_image = function() { + var project_target = this.get_target().replace('android-', ''); + var images = this.list_images(); + var closest = 9999; + var best = images[0]; + for (i in images) { + var target = images[i].target; + if(target) { + var num = target.split('(API level ')[1].replace(')', ''); + if (num == project_target) { + return images[i]; + } else if (project_target - num < closest && project_target > num) { + var closest = project_target - num; + best = images[i]; + } + } + } + return best; +} + +module.exports.list_started = function() { + var cmd = 'adb devices'; + var result = shell.exec(cmd, {silent:true, async:false}); + if (result.code > 0) { + console.error('Failed to execute android command \'' + cmd + '\'.'); + process.exit(2); + } else { + var response = result.output.split('\n'); + var started_emulator_list = []; + for (var i = 1; i < response.length; i++) { + if (response[i].match(/device/) && response[i].match(/emulator/)) { + started_emulator_list.push(response[i].replace(/\tdevice/, '').replace('\r', '')); + } + } + return started_emulator_list; + } +} + +module.exports.get_target = function() { + var target = shell.grep(/target=android-[\d+]/, path.join(ROOT, 'project.properties')); + return target.split('=')[1].replace('\n', '').replace('\r', '').replace(' ', ''); +} + +module.exports.list_targets = function() { + var target_out = shell.exec('android list targets', {silent:true, async:false}).output.split('\n'); + var targets = []; + for (var i = target_out.length; i >= 0; i--) { + if(target_out[i].match(/id:/)) { + targets.push(targets[i].split(' ')[1]); + } + } + return targets; +} + +/* + * Starts an emulator with the given ID, + * and returns the started ID of that emulator. + * If no ID is given it will used the first image availible, + * if no image is availible it will error out (maybe create one?). + */ +module.exports.start = function(emulator_ID) { + var started_emulators = this.list_started(); + var num_started = started_emulators.length; + if (typeof emulator_ID === 'undefined') { + var emulator_list = this.list_images(); + if (emulator_list.length > 0) { + emulator_ID = this.best_image().name; + console.log('WARNING : no emulator specified, defaulting to ' + emulator_ID); + } else { + console.error('ERROR : No emulator images (avds) found, if you would like to create an'); + console.error(' avd follow the instructions provided here : '); + console.error(' http://developer.android.com/tools/devices/index.html') + console.error(' Or run \'android create avd --name --target \' '); + console.error(' in on the command line.'); + process.exit(2); + /*console.log('WARNING : no emulators availible, creating \'' + new_emulator + '\'.'); + this.create_image(new_emulator, this.get_target()); + emulator_ID = new_emulator;*/ + } + } + + var pipe_null = (process.platform == 'win32' || process.platform == 'win64'? '> NUL' : '> /dev/null'); + var cmd = 'emulator -avd ' + emulator_ID + ' ' + pipe_null + ' &'; + if(process.platform == 'win32' || process.platform == 'win64') { + cmd = '%comspec% /c start cmd /c ' + cmd; + } + var result = shell.exec(cmd, {silent:true, async:false}, function(code, output) { + if (code > 0) { + console.error('Failed to execute android command \'' + cmd + '\'.'); + console.error(output); + process.exit(2); + } + }); + + // wait for emulator to start + console.log('Waiting for emulator...'); + var new_started = this.wait_for_emulator(num_started); + var emulator_id; + if (new_started.length > 1) { + for (i in new_started) { + console.log(new_started[i]); + console.log(started_emulators.indexOf(new_started[i])); + if (started_emulators.indexOf(new_started[i]) < 0) { + emulator_id = new_started[i]; + } + } + } else { + emulator_id = new_started[0]; + } + if (!emulator_id) { + console.error('ERROR : Failed to start emulator, could not find new emulator'); + process.exit(2); + } + + //wait for emulator to boot up + process.stdout.write('Booting up emulator (this may take a while)...'); + this.wait_for_boot(emulator_id); + console.log('BOOT COMPLETE'); + + //unlock screen + cmd = 'adb -s ' + emulator_id + ' shell input keyevent 82'; + shell.exec(cmd, {silent:false, async:false}); + + //return the new emulator id for the started emulators + return emulator_id; +} + +/* + * Waits for the new emulator to apear on the started-emulator list. + */ +module.exports.wait_for_emulator = function(num_running) { + var new_started = this.list_started(); + if (new_started.length > num_running) { + return new_started; + } else { + this.sleep(1); + return this.wait_for_emulator(num_running); + } +} + +/* + * Waits for the boot animation property of the emulator to switch to 'stopped' + */ +module.exports.wait_for_boot = function(emulator_id) { + var cmd; + // ShellJS opens a lot of file handles, and the default on OS X is too small. + // TODO : This is not working, need to find a better way to increese the ulimit. + if(process.platform == 'win32' || process.platform == 'win64') { + cmd = 'adb -s ' + emulator_id + ' shell getprop init.svc.bootanim'; + } else { + cmd = 'ulimit -S -n 4096; adb -s ' + emulator_id + ' shell getprop init.svc.bootanim'; + } + var boot_anim = shell.exec(cmd, {silent:true, async:false}); + if (boot_anim.output.match(/stopped/)) { + return; + } else { + process.stdout.write('.'); + this.sleep(3); + return this.wait_for_boot(emulator_id); + } +} + +/* + * TODO : find a better way to wait for the emulator (maybe using async methods?) + */ +module.exports.sleep = function(time_sec) { + if (process.platform == 'win32' || process.platform == 'win64') { + shell.exec('ping 127.0.0.1 -n ' + time_sec, {silent:true, async:false}); + } else { + shell.exec('sleep ' + time_sec, {silent:true, async:false}); + } +} + +/* + * Create avd + * TODO : Enter the stdin input required to complete the creation of an avd. + */ +module.exports.create_image = function(name, target) { + console.log('Creating avd named ' + name); + if (target) { + var cmd = 'android create avd --name ' + name + ' --target ' + target; + var create = shell.exec(cmd, {sient:false, async:false}); + if (create.error) { + console.error('ERROR : Failed to create emulator image : '); + console.error(' Do you have the latest android targets including ' + target + '?'); + console.error(create.output); + process.exit(2); + } + } else { + console.log('WARNING : Project target not found, creating avd with a different target but the project may fail to install.'); + var cmd = 'android create avd --name ' + name + ' --target ' + this.list_targets()[0]; + var create = shell.exec(cmd, {sient:false, async:false}); + if (create.error) { + console.error('ERROR : Failed to create emulator image : '); + console.error(create.output); + process.exit(2); + } + console.error('ERROR : Unable to create an avd emulator, no targets found.'); + console.error('Please insure you have targets availible by runing the "android" command'). + process.exit(2); + } +} + +/* + * Installs a previously built application on the emulator and launches it. + * If no target is specified, then it picks one. + * If no started emulators are found, error out. + */ +module.exports.install = function(target) { + var emulator_list = this.list_started(); + if (emulator_list.length < 1) { + console.error('ERROR : No started emulators found, please start an emultor before deploying your project.'); + process.exit(2); + /*console.log('WARNING : No started emulators found, attemting to start an avd...'); + this.start(this.best_image().name);*/ + } + // default emulator + target = typeof target !== 'undefined' ? target : emulator_list[0]; + if (emulator_list.indexOf(target) > -1) { + console.log('Installing app on emulator...'); + var apk_path = build.get_apk(); + var cmd = 'adb -s ' + target + ' install -r ' + apk_path; + var install = shell.exec(cmd, {sient:false, async:false}); + if (install.error || install.output.match(/Failure/)) { + console.error('ERROR : Failed to install apk to emulator : '); + console.error(install.output); + process.exit(2); + } + + //unlock screen + cmd = 'adb -s ' + target + ' shell input keyevent 82'; + shell.exec(cmd, {silent:true, async:false}); + + // launch the application + console.log('Launching application...'); + var launchName = appinfo.getActivityName(); + cmd = 'adb -s ' + target + ' shell am start -W -a android.intent.action.MAIN -n ' + launchName; + console.log(cmd); + var launch = shell.exec(cmd, {silent:false, async:false}); + if(launch.code > 0) { + console.error('ERROR : Failed to launch application on emulator : ' + launch.error); + console.error(launch.output); + process.exit(2); + } else { + console.log('LANCH SUCCESS'); + } + } else { + console.error('ERROR : Unable to find target \'' + target + '\'.'); + console.error('Failed to deploy to emulator.'); + process.exit(2); + } +} diff --git a/platforms/android/cordova/lib/install-device b/platforms/android/cordova/lib/install-device new file mode 100644 index 0000000..cf53918 --- /dev/null +++ b/platforms/android/cordova/lib/install-device @@ -0,0 +1,38 @@ +#!/usr/bin/env node + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var device = require('./device'), + args = process.argv; + +if(args.length > 2) { + var install_target; + if (args[2].substring(0, 9) == '--target=') { + install_target = args[2].substring(9, args[2].length); + device.install(install_target); + process.exit(0); + } else { + console.error('ERROR : argument \'' + args[2] + '\' not recognized.'); + process.exit(2); + } +} else { + device.install(); + process.exit(0); +} \ No newline at end of file diff --git a/platforms/android/cordova/lib/install-emulator b/platforms/android/cordova/lib/install-emulator new file mode 100644 index 0000000..70421be --- /dev/null +++ b/platforms/android/cordova/lib/install-emulator @@ -0,0 +1,38 @@ +#!/usr/bin/env node + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var emulator = require('./emulator'), + args = process.argv; + +if(args.length > 2) { + var install_target; + if (args[2].substring(0, 9) == '--target=') { + install_target = args[2].substring(9, args[2].length); + emulator.install(install_target); + process.exit(0); + } else { + console.error('ERROR : argument \'' + args[2] + '\' not recognized.'); + process.exit(2); + } +} else { + emulator.install(); + process.exit(0); +} \ No newline at end of file diff --git a/platforms/android/cordova/lib/list-devices b/platforms/android/cordova/lib/list-devices new file mode 100644 index 0000000..bdd0abd --- /dev/null +++ b/platforms/android/cordova/lib/list-devices @@ -0,0 +1,28 @@ +#!/usr/bin/env node + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var devices = require('./device'); + +// Usage support for when args are given +var device_list = devices.list(); +for(device in device_list) { + console.log(device_list[device]); +} \ No newline at end of file diff --git a/platforms/android/cordova/lib/list-emulator-images b/platforms/android/cordova/lib/list-emulator-images new file mode 100644 index 0000000..69f4789 --- /dev/null +++ b/platforms/android/cordova/lib/list-emulator-images @@ -0,0 +1,29 @@ +#!/usr/bin/env node + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var emulators = require('./emulator'); + +// Usage support for when args are given +var emulator_list = emulators.list_images(); +for(emulator in emulator_list) { + console.log(emulator_list[emulator].name); + process.exit(0); +} \ No newline at end of file diff --git a/platforms/android/cordova/lib/list-started-emulators b/platforms/android/cordova/lib/list-started-emulators new file mode 100644 index 0000000..3e69b2f --- /dev/null +++ b/platforms/android/cordova/lib/list-started-emulators @@ -0,0 +1,29 @@ +#!/usr/bin/env node + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var emulators = require('./emulator'); + +// Usage support for when args are given +var emulator_list = emulators.list_started(); +for(emulator in emulator_list) { + console.log(emulator_list[emulator]); + process.exit(0); +} \ No newline at end of file diff --git a/platforms/android/cordova/lib/log.js b/platforms/android/cordova/lib/log.js new file mode 100644 index 0000000..ff14e46 --- /dev/null +++ b/platforms/android/cordova/lib/log.js @@ -0,0 +1,43 @@ +#!/usr/bin/env node + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var shell = require('shelljs'), + path = require('path'), + ROOT = path.join(__dirname, '..', '..'); + +/* + * Starts running logcat in the shell. + */ +module.exports.run = function() { + var cmd = 'adb logcat | grep -v nativeGetEnabledTags'; + var result = shell.exec(cmd, {silent:false, async:false}); + if (result.code > 0) { + console.error('ERROR: Failed to run logcat command.'); + console.error(result.output); + process.exit(2); + } +} + +module.exports.help = function() { + console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'corodva', 'log'))); + console.log('Gives the logcat output on the command line.'); + process.exit(0); +} \ No newline at end of file diff --git a/platforms/android/cordova/lib/run.js b/platforms/android/cordova/lib/run.js new file mode 100644 index 0000000..b1c8b2b --- /dev/null +++ b/platforms/android/cordova/lib/run.js @@ -0,0 +1,123 @@ +#!/usr/bin/env node + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var path = require('path'), + build = require('./build'), + emulator = require('./emulator'), + device = require('./device'); + +/* + * Runs the application on a device if availible. + * If not device is found, it will use a started emulator. + * If no started emulators are found it will attempt to start an avd. + * If no avds are found it will error out. + */ + module.exports.run = function(args) { + var build_type; + var install_target; + + for (var i=2; i -1) { + device.install(install_target); + } else if (started_emulators.indexOf(install_target) > -1) { + emulator.install(install_target); + } else { + // if target emulator isn't started, then start it. + var emulator_ID; + for(avd in avds) { + if(avds[avd].name == install_target) { + emulator_ID = emulator.start(install_target); + emulator.install(emulator_ID); + break; + } + } + if(!emulator_ID) { + console.error('ERROR : Target \'' + install_target + '\' not found, unalbe to run project'); + process.exit(2); + } + } + } else { + // no target given, deploy to device if availible, otherwise use the emulator. + var device_list = device.list(); + if (device_list.length > 0) { + console.log('WARNING : No target specified, deploying to device \'' + device_list[0] + '\'.'); + device.install(device_list[0]) + } else { + var emulator_list = emulator.list_started(); + if (emulator_list.length > 0) { + console.log('WARNING : No target specified, deploying to emulator \'' + emulator_list[0] + '\'.'); + emulator.install(emulator_list[0]); + } else { + console.log('WARNING : No started emulators found, starting an emulator.'); + var best_avd = emulator.best_image(); + if(best_avd) { + var emulator_ID = emulator.start(best_avd.name); + console.log('WARNING : No target specified, deploying to emulator \'' + emulator_ID + '\'.'); + emulator.install(emulator_ID); + } else { + emulator.start(); + } + } + } + } +} + +module.exports.help = function() { + console.log('Usage: ' + path.relative(process.cwd(), args[0]) + ' [options]'); + console.log('Build options :'); + console.log(' --debug : Builds project in debug mode'); + console.log(' --release : Builds project in release mode'); + console.log(' --nobuild : Runs the currently built project without recompiling'); + console.log('Deploy options :'); + console.log(' --device : Will deploy the built project to a device'); + console.log(' --emulator : Will deploy the built project to an emulator if one exists'); + console.log(' --target= : Installs to the target with the specified id.'); + process.exit(0); +} \ No newline at end of file diff --git a/platforms/android/cordova/lib/start-emulator b/platforms/android/cordova/lib/start-emulator new file mode 100644 index 0000000..8ea6d3f --- /dev/null +++ b/platforms/android/cordova/lib/start-emulator @@ -0,0 +1,38 @@ +#!/usr/bin/env node + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var emulator = require('./emulator'), + args = process.argv; + +if(args.length > 2) { + var install_target; + if (args[2].substring(0, 9) == '--target=') { + install_target = args[2].substring(9, args[2].length); + emulator.start(install_target); + process.exit(0); + } else { + console.error('ERROR : argument \'' + args[2] + '\' not recognized.'); + process.exit(2); + } +} else { + emulator.start(); + process.exit(0); +} \ No newline at end of file diff --git a/platforms/android/cordova/log b/platforms/android/cordova/log new file mode 100644 index 0000000..514f69c --- /dev/null +++ b/platforms/android/cordova/log @@ -0,0 +1,33 @@ +#!/usr/bin/env node + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var log = require('./lib/log'), + reqs = require('./lib/check_reqs'), + args = process.argv; + +// Usage support for when args are given +if(args.length > 2) { + log.help(); +} else if(reqs.run()) { + log.run(); +} else { + process.exit(2); +} \ No newline at end of file diff --git a/platforms/android/cordova/node_modules/.bin/shjs b/platforms/android/cordova/node_modules/.bin/shjs new file mode 100644 index 0000000..badb37c --- /dev/null +++ b/platforms/android/cordova/node_modules/.bin/shjs @@ -0,0 +1 @@ +link ../shelljs/bin/shjs \ No newline at end of file diff --git a/platforms/android/cordova/node_modules/shelljs/.documentup.json b/platforms/android/cordova/node_modules/shelljs/.documentup.json new file mode 100644 index 0000000..57fe301 --- /dev/null +++ b/platforms/android/cordova/node_modules/shelljs/.documentup.json @@ -0,0 +1,6 @@ +{ + "name": "ShellJS", + "twitter": [ + "r2r" + ] +} diff --git a/platforms/android/cordova/node_modules/shelljs/.npmignore b/platforms/android/cordova/node_modules/shelljs/.npmignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/platforms/android/cordova/node_modules/shelljs/.npmignore @@ -0,0 +1 @@ +node_modules/ diff --git a/platforms/android/cordova/node_modules/shelljs/.travis.yml b/platforms/android/cordova/node_modules/shelljs/.travis.yml new file mode 100644 index 0000000..5caf599 --- /dev/null +++ b/platforms/android/cordova/node_modules/shelljs/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - 0.6 + - 0.8 + diff --git a/platforms/android/cordova/node_modules/shelljs/LICENSE b/platforms/android/cordova/node_modules/shelljs/LICENSE new file mode 100644 index 0000000..1b35ee9 --- /dev/null +++ b/platforms/android/cordova/node_modules/shelljs/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2012, Artur Adib +All rights reserved. + +You may use this project under the terms of the New BSD license as follows: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Artur Adib nor the + names of the contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL ARTUR ADIB BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/platforms/android/cordova/node_modules/shelljs/README.md b/platforms/android/cordova/node_modules/shelljs/README.md new file mode 100644 index 0000000..8b45592 --- /dev/null +++ b/platforms/android/cordova/node_modules/shelljs/README.md @@ -0,0 +1,513 @@ +# ShellJS - Unix shell commands for Node.js [![Build Status](https://secure.travis-ci.org/arturadib/shelljs.png)](http://travis-ci.org/arturadib/shelljs) + +ShellJS is a portable **(Windows/Linux/OS X)** implementation of Unix shell commands on top of the Node.js API. You can use it to eliminate your shell script's dependency on Unix while still keeping its familiar and powerful commands. You can also install it globally so you can run it from outside Node projects - say goodbye to those gnarly Bash scripts! + +The project is [unit-tested](http://travis-ci.org/arturadib/shelljs) and battled-tested in projects like: + ++ [PDF.js](http://github.com/mozilla/pdf.js) - Firefox's next-gen PDF reader ++ [Firebug](http://getfirebug.com/) - Firefox's infamous debugger ++ [JSHint](http://jshint.com) - Most popular JavaScript linter ++ [Zepto](http://zeptojs.com) - jQuery-compatible JavaScript library for modern browsers ++ [Yeoman](http://yeoman.io/) - Web application stack and development tool ++ [Deployd.com](http://deployd.com) - Open source PaaS for quick API backend generation + +and [many more](https://npmjs.org/browse/depended/shelljs). + +## Installing + +Via npm: + +```bash +$ npm install [-g] shelljs +``` + +If the global option `-g` is specified, the binary `shjs` will be installed. This makes it possible to +run ShellJS scripts much like any shell script from the command line, i.e. without requiring a `node_modules` folder: + +```bash +$ shjs my_script +``` + +You can also just copy `shell.js` into your project's directory, and `require()` accordingly. + + +## Examples + +### JavaScript + +```javascript +require('shelljs/global'); + +if (!which('git')) { + echo('Sorry, this script requires git'); + exit(1); +} + +// Copy files to release dir +mkdir('-p', 'out/Release'); +cp('-R', 'stuff/*', 'out/Release'); + +// Replace macros in each .js file +cd('lib'); +ls('*.js').forEach(function(file) { + sed('-i', 'BUILD_VERSION', 'v0.1.2', file); + sed('-i', /.*REMOVE_THIS_LINE.*\n/, '', file); + sed('-i', /.*REPLACE_LINE_WITH_MACRO.*\n/, cat('macro.js'), file); +}); +cd('..'); + +// Run external tool synchronously +if (exec('git commit -am "Auto-commit"').code !== 0) { + echo('Error: Git commit failed'); + exit(1); +} +``` + +### CoffeeScript + +```coffeescript +require 'shelljs/global' + +if not which 'git' + echo 'Sorry, this script requires git' + exit 1 + +# Copy files to release dir +mkdir '-p', 'out/Release' +cp '-R', 'stuff/*', 'out/Release' + +# Replace macros in each .js file +cd 'lib' +for file in ls '*.js' + sed '-i', 'BUILD_VERSION', 'v0.1.2', file + sed '-i', /.*REMOVE_THIS_LINE.*\n/, '', file + sed '-i', /.*REPLACE_LINE_WITH_MACRO.*\n/, cat 'macro.js', file +cd '..' + +# Run external tool synchronously +if (exec 'git commit -am "Auto-commit"').code != 0 + echo 'Error: Git commit failed' + exit 1 +``` + +## Global vs. Local + +The example above uses the convenience script `shelljs/global` to reduce verbosity. If polluting your global namespace is not desirable, simply require `shelljs`. + +Example: + +```javascript +var shell = require('shelljs'); +shell.echo('hello world'); +``` + +## Make tool + +A convenience script `shelljs/make` is also provided to mimic the behavior of a Unix Makefile. In this case all shell objects are global, and command line arguments will cause the script to execute only the corresponding function in the global `target` object. To avoid redundant calls, target functions are executed only once per script. + +Example (CoffeeScript): + +```coffeescript +require 'shelljs/make' + +target.all = -> + target.bundle() + target.docs() + +target.bundle = -> + cd __dirname + mkdir 'build' + cd 'lib' + (cat '*.js').to '../build/output.js' + +target.docs = -> + cd __dirname + mkdir 'docs' + cd 'lib' + for file in ls '*.js' + text = grep '//@', file # extract special comments + text.replace '//@', '' # remove comment tags + text.to 'docs/my_docs.md' +``` + +To run the target `all`, call the above script without arguments: `$ node make`. To run the target `docs`: `$ node make docs`, and so on. + + + + + + +## Command reference + + +All commands run synchronously, unless otherwise stated. + + +### cd('dir') +Changes to directory `dir` for the duration of the script + +### pwd() +Returns the current directory. + +### ls([options ,] path [,path ...]) +### ls([options ,] path_array) +Available options: + ++ `-R`: recursive ++ `-A`: all files (include files beginning with `.`, except for `.` and `..`) + +Examples: + +```javascript +ls('projs/*.js'); +ls('-R', '/users/me', '/tmp'); +ls('-R', ['/users/me', '/tmp']); // same as above +``` + +Returns array of files in the given path, or in current directory if no path provided. + +### find(path [,path ...]) +### find(path_array) +Examples: + +```javascript +find('src', 'lib'); +find(['src', 'lib']); // same as above +find('.').filter(function(file) { return file.match(/\.js$/); }); +``` + +Returns array of all files (however deep) in the given paths. + +The main difference from `ls('-R', path)` is that the resulting file names +include the base directories, e.g. `lib/resources/file1` instead of just `file1`. + +### cp([options ,] source [,source ...], dest) +### cp([options ,] source_array, dest) +Available options: + ++ `-f`: force ++ `-r, -R`: recursive + +Examples: + +```javascript +cp('file1', 'dir1'); +cp('-Rf', '/tmp/*', '/usr/local/*', '/home/tmp'); +cp('-Rf', ['/tmp/*', '/usr/local/*'], '/home/tmp'); // same as above +``` + +Copies files. The wildcard `*` is accepted. + +### rm([options ,] file [, file ...]) +### rm([options ,] file_array) +Available options: + ++ `-f`: force ++ `-r, -R`: recursive + +Examples: + +```javascript +rm('-rf', '/tmp/*'); +rm('some_file.txt', 'another_file.txt'); +rm(['some_file.txt', 'another_file.txt']); // same as above +``` + +Removes files. The wildcard `*` is accepted. + +### mv(source [, source ...], dest') +### mv(source_array, dest') +Available options: + ++ `f`: force + +Examples: + +```javascript +mv('-f', 'file', 'dir/'); +mv('file1', 'file2', 'dir/'); +mv(['file1', 'file2'], 'dir/'); // same as above +``` + +Moves files. The wildcard `*` is accepted. + +### mkdir([options ,] dir [, dir ...]) +### mkdir([options ,] dir_array) +Available options: + ++ `p`: full path (will create intermediate dirs if necessary) + +Examples: + +```javascript +mkdir('-p', '/tmp/a/b/c/d', '/tmp/e/f/g'); +mkdir('-p', ['/tmp/a/b/c/d', '/tmp/e/f/g']); // same as above +``` + +Creates directories. + +### test(expression) +Available expression primaries: + ++ `'-b', 'path'`: true if path is a block device ++ `'-c', 'path'`: true if path is a character device ++ `'-d', 'path'`: true if path is a directory ++ `'-e', 'path'`: true if path exists ++ `'-f', 'path'`: true if path is a regular file ++ `'-L', 'path'`: true if path is a symboilc link ++ `'-p', 'path'`: true if path is a pipe (FIFO) ++ `'-S', 'path'`: true if path is a socket + +Examples: + +```javascript +if (test('-d', path)) { /* do something with dir */ }; +if (!test('-f', path)) continue; // skip if it's a regular file +``` + +Evaluates expression using the available primaries and returns corresponding value. + +### cat(file [, file ...]) +### cat(file_array) + +Examples: + +```javascript +var str = cat('file*.txt'); +var str = cat('file1', 'file2'); +var str = cat(['file1', 'file2']); // same as above +``` + +Returns a string containing the given file, or a concatenated string +containing the files if more than one file is given (a new line character is +introduced between each file). Wildcard `*` accepted. + +### 'string'.to(file) + +Examples: + +```javascript +cat('input.txt').to('output.txt'); +``` + +Analogous to the redirection operator `>` in Unix, but works with JavaScript strings (such as +those returned by `cat`, `grep`, etc). _Like Unix redirections, `to()` will overwrite any existing file!_ + +### sed([options ,] search_regex, replace_str, file) +Available options: + ++ `-i`: Replace contents of 'file' in-place. _Note that no backups will be created!_ + +Examples: + +```javascript +sed('-i', 'PROGRAM_VERSION', 'v0.1.3', 'source.js'); +sed(/.*DELETE_THIS_LINE.*\n/, '', 'source.js'); +``` + +Reads an input string from `file` and performs a JavaScript `replace()` on the input +using the given search regex and replacement string. Returns the new string after replacement. + +### grep([options ,] regex_filter, file [, file ...]) +### grep([options ,] regex_filter, file_array) +Available options: + ++ `-v`: Inverse the sense of the regex and print the lines not matching the criteria. + +Examples: + +```javascript +grep('-v', 'GLOBAL_VARIABLE', '*.js'); +grep('GLOBAL_VARIABLE', '*.js'); +``` + +Reads input string from given files and returns a string containing all lines of the +file that match the given `regex_filter`. Wildcard `*` accepted. + +### which(command) + +Examples: + +```javascript +var nodeExec = which('node'); +``` + +Searches for `command` in the system's PATH. On Windows looks for `.exe`, `.cmd`, and `.bat` extensions. +Returns string containing the absolute path to the command. + +### echo(string [,string ...]) + +Examples: + +```javascript +echo('hello world'); +var str = echo('hello world'); +``` + +Prints string to stdout, and returns string with additional utility methods +like `.to()`. + +### dirs([options | '+N' | '-N']) + +Available options: + ++ `-c`: Clears the directory stack by deleting all of the elements. + +Arguments: + ++ `+N`: Displays the Nth directory (counting from the left of the list printed by dirs when invoked without options), starting with zero. ++ `-N`: Displays the Nth directory (counting from the right of the list printed by dirs when invoked without options), starting with zero. + +Display the list of currently remembered directories. Returns an array of paths in the stack, or a single path if +N or -N was specified. + +See also: pushd, popd + +### pushd([options,] [dir | '-N' | '+N']) + +Available options: + ++ `-n`: Suppresses the normal change of directory when adding directories to the stack, so that only the stack is manipulated. + +Arguments: + ++ `dir`: Makes the current working directory be the top of the stack, and then executes the equivalent of `cd dir`. ++ `+N`: Brings the Nth directory (counting from the left of the list printed by dirs, starting with zero) to the top of the list by rotating the stack. ++ `-N`: Brings the Nth directory (counting from the right of the list printed by dirs, starting with zero) to the top of the list by rotating the stack. + +Examples: + +```javascript +// process.cwd() === '/usr' +pushd('/etc'); // Returns /etc /usr +pushd('+1'); // Returns /usr /etc +``` + +Save the current directory on the top of the directory stack and then cd to `dir`. With no arguments, pushd exchanges the top two directories. Returns an array of paths in the stack. + +### popd([options,] ['-N' | '+N']) + +Available options: + ++ `-n`: Suppresses the normal change of directory when removing directories from the stack, so that only the stack is manipulated. + +Arguments: + ++ `+N`: Removes the Nth directory (counting from the left of the list printed by dirs), starting with zero. ++ `-N`: Removes the Nth directory (counting from the right of the list printed by dirs), starting with zero. + +Examples: + +```javascript +echo(process.cwd()); // '/usr' +pushd('/etc'); // '/etc /usr' +echo(process.cwd()); // '/etc' +popd(); // '/usr' +echo(process.cwd()); // '/usr' +``` + +When no arguments are given, popd removes the top directory from the stack and performs a cd to the new top directory. The elements are numbered from 0 starting at the first directory listed with dirs; i.e., popd is equivalent to popd +0. Returns an array of paths in the stack. + +### exit(code) +Exits the current process with the given exit code. + +### env['VAR_NAME'] +Object containing environment variables (both getter and setter). Shortcut to process.env. + +### exec(command [, options] [, callback]) +Available options (all `false` by default): + ++ `async`: Asynchronous execution. Defaults to true if a callback is provided. ++ `silent`: Do not echo program output to console. + +Examples: + +```javascript +var version = exec('node --version', {silent:true}).output; + +var child = exec('some_long_running_process', {async:true}); +child.stdout.on('data', function(data) { + /* ... do something with data ... */ +}); + +exec('some_long_running_process', function(code, output) { + console.log('Exit code:', code); + console.log('Program output:', output); +}); +``` + +Executes the given `command` _synchronously_, unless otherwise specified. +When in synchronous mode returns the object `{ code:..., output:... }`, containing the program's +`output` (stdout + stderr) and its exit `code`. Otherwise returns the child process object, and +the `callback` gets the arguments `(code, output)`. + +**Note:** For long-lived processes, it's best to run `exec()` asynchronously as +the current synchronous implementation uses a lot of CPU. This should be getting +fixed soon. + +### chmod(octal_mode || octal_string, file) +### chmod(symbolic_mode, file) + +Available options: + ++ `-v`: output a diagnostic for every file processed ++ `-c`: like verbose but report only when a change is made ++ `-R`: change files and directories recursively + +Examples: + +```javascript +chmod(755, '/Users/brandon'); +chmod('755', '/Users/brandon'); // same as above +chmod('u+x', '/Users/brandon'); +``` + +Alters the permissions of a file or directory by either specifying the +absolute permissions in octal form or expressing the changes in symbols. +This command tries to mimic the POSIX behavior as much as possible. +Notable exceptions: + ++ In symbolic modes, 'a-r' and '-r' are identical. No consideration is + given to the umask. ++ There is no "quiet" option since default behavior is to run silent. + +## Configuration + + +### config.silent +Example: + +```javascript +var silentState = config.silent; // save old silent state +config.silent = true; +/* ... */ +config.silent = silentState; // restore old silent state +``` + +Suppresses all command output if `true`, except for `echo()` calls. +Default is `false`. + +### config.fatal +Example: + +```javascript +config.fatal = true; +cp('this_file_does_not_exist', '/dev/null'); // dies here +/* more commands... */ +``` + +If `true` the script will die on errors. Default is `false`. + +## Non-Unix commands + + +### tempdir() +Searches and returns string containing a writeable, platform-dependent temporary directory. +Follows Python's [tempfile algorithm](http://docs.python.org/library/tempfile.html#tempfile.tempdir). + +### error() +Tests if error occurred in the last command. Returns `null` if no error occurred, +otherwise returns string explaining the error diff --git a/platforms/android/cordova/node_modules/shelljs/bin/shjs b/platforms/android/cordova/node_modules/shelljs/bin/shjs new file mode 100644 index 0000000..d239a7a --- /dev/null +++ b/platforms/android/cordova/node_modules/shelljs/bin/shjs @@ -0,0 +1,51 @@ +#!/usr/bin/env node +require('../global'); + +if (process.argv.length < 3) { + console.log('ShellJS: missing argument (script name)'); + console.log(); + process.exit(1); +} + +var args, + scriptName = process.argv[2]; +env['NODE_PATH'] = __dirname + '/../..'; + +if (!scriptName.match(/\.js/) && !scriptName.match(/\.coffee/)) { + if (test('-f', scriptName + '.js')) + scriptName += '.js'; + if (test('-f', scriptName + '.coffee')) + scriptName += '.coffee'; +} + +if (!test('-f', scriptName)) { + console.log('ShellJS: script not found ('+scriptName+')'); + console.log(); + process.exit(1); +} + +args = process.argv.slice(3); + +for (var i = 0, l = args.length; i < l; i++) { + if (args[i][0] !== "-"){ + args[i] = '"' + args[i] + '"'; // fixes arguments with multiple words + } +} + +if (scriptName.match(/\.coffee$/)) { + // + // CoffeeScript + // + if (which('coffee')) { + exec('coffee ' + scriptName + ' ' + args.join(' '), { async: true }); + } else { + console.log('ShellJS: CoffeeScript interpreter not found'); + console.log(); + process.exit(1); + } +} else { + // + // JavaScript + // + exec('node ' + scriptName + ' ' + args.join(' '), { async: true }); +} diff --git a/platforms/android/cordova/node_modules/shelljs/global.js b/platforms/android/cordova/node_modules/shelljs/global.js new file mode 100644 index 0000000..97f0033 --- /dev/null +++ b/platforms/android/cordova/node_modules/shelljs/global.js @@ -0,0 +1,3 @@ +var shell = require('./shell.js'); +for (var cmd in shell) + global[cmd] = shell[cmd]; diff --git a/platforms/android/cordova/node_modules/shelljs/jshint.json b/platforms/android/cordova/node_modules/shelljs/jshint.json new file mode 100644 index 0000000..205ed9c --- /dev/null +++ b/platforms/android/cordova/node_modules/shelljs/jshint.json @@ -0,0 +1,4 @@ +{ + "loopfunc": true, + "sub": true +} \ No newline at end of file diff --git a/platforms/android/cordova/node_modules/shelljs/make.js b/platforms/android/cordova/node_modules/shelljs/make.js new file mode 100644 index 0000000..b636447 --- /dev/null +++ b/platforms/android/cordova/node_modules/shelljs/make.js @@ -0,0 +1,48 @@ +require('./global'); +config.fatal = true; + +global.target = {}; + +// This ensures we only execute the script targets after the entire script has +// been evaluated +var args = process.argv.slice(2); +setTimeout(function() { + var t; + + if (args.length === 1 && args[0] === '--help') { + console.log('Available targets:'); + for (t in target) + console.log(' ' + t); + return; + } + + // Wrap targets to prevent duplicate execution + for (t in target) { + (function(t, oldTarget){ + + // Wrap it + target[t] = function(force) { + if (oldTarget.done && !force) + return; + oldTarget.done = true; + return oldTarget.apply(oldTarget, arguments); + }; + + })(t, target[t]); + } + + // Execute desired targets + if (args.length > 0) { + args.forEach(function(arg) { + if (arg in target) + target[arg](); + else { + console.log('no such target: ' + arg); + exit(1); + } + }); + } else if ('all' in target) { + target.all(); + } + +}, 0); diff --git a/platforms/android/cordova/node_modules/shelljs/package.json b/platforms/android/cordova/node_modules/shelljs/package.json new file mode 100644 index 0000000..6f545b9 --- /dev/null +++ b/platforms/android/cordova/node_modules/shelljs/package.json @@ -0,0 +1,48 @@ +{ + "name": "shelljs", + "version": "0.1.4", + "author": { + "name": "Artur Adib", + "email": "aadib@mozilla.com" + }, + "description": "Portable Unix shell commands for Node.js", + "keywords": [ + "unix", + "shell", + "makefile", + "make", + "jake", + "synchronous" + ], + "repository": { + "type": "git", + "url": "git://github.com/arturadib/shelljs.git" + }, + "homepage": "http://github.com/arturadib/shelljs", + "main": "./shell.js", + "scripts": { + "test": "node scripts/run-tests" + }, + "bin": { + "shjs": "./bin/shjs" + }, + "dependencies": {}, + "devDependencies": { + "jshint": "~1.1.0" + }, + "optionalDependencies": {}, + "engines": { + "node": "*" + }, + "readme": "# ShellJS - Unix shell commands for Node.js [![Build Status](https://secure.travis-ci.org/arturadib/shelljs.png)](http://travis-ci.org/arturadib/shelljs)\n\nShellJS is a portable **(Windows/Linux/OS X)** implementation of Unix shell commands on top of the Node.js API. You can use it to eliminate your shell script's dependency on Unix while still keeping its familiar and powerful commands. You can also install it globally so you can run it from outside Node projects - say goodbye to those gnarly Bash scripts!\n\nThe project is [unit-tested](http://travis-ci.org/arturadib/shelljs) and battled-tested in projects like:\n\n+ [PDF.js](http://github.com/mozilla/pdf.js) - Firefox's next-gen PDF reader\n+ [Firebug](http://getfirebug.com/) - Firefox's infamous debugger\n+ [JSHint](http://jshint.com) - Most popular JavaScript linter\n+ [Zepto](http://zeptojs.com) - jQuery-compatible JavaScript library for modern browsers\n+ [Yeoman](http://yeoman.io/) - Web application stack and development tool\n+ [Deployd.com](http://deployd.com) - Open source PaaS for quick API backend generation\n\nand [many more](https://npmjs.org/browse/depended/shelljs).\n\n## Installing\n\nVia npm:\n\n```bash\n$ npm install [-g] shelljs\n```\n\nIf the global option `-g` is specified, the binary `shjs` will be installed. This makes it possible to\nrun ShellJS scripts much like any shell script from the command line, i.e. without requiring a `node_modules` folder:\n\n```bash\n$ shjs my_script\n```\n\nYou can also just copy `shell.js` into your project's directory, and `require()` accordingly.\n\n\n## Examples\n\n### JavaScript\n\n```javascript\nrequire('shelljs/global');\n\nif (!which('git')) {\n echo('Sorry, this script requires git');\n exit(1);\n}\n\n// Copy files to release dir\nmkdir('-p', 'out/Release');\ncp('-R', 'stuff/*', 'out/Release');\n\n// Replace macros in each .js file\ncd('lib');\nls('*.js').forEach(function(file) {\n sed('-i', 'BUILD_VERSION', 'v0.1.2', file);\n sed('-i', /.*REMOVE_THIS_LINE.*\\n/, '', file);\n sed('-i', /.*REPLACE_LINE_WITH_MACRO.*\\n/, cat('macro.js'), file);\n});\ncd('..');\n\n// Run external tool synchronously\nif (exec('git commit -am \"Auto-commit\"').code !== 0) {\n echo('Error: Git commit failed');\n exit(1);\n}\n```\n\n### CoffeeScript\n\n```coffeescript\nrequire 'shelljs/global'\n\nif not which 'git'\n echo 'Sorry, this script requires git'\n exit 1\n\n# Copy files to release dir\nmkdir '-p', 'out/Release'\ncp '-R', 'stuff/*', 'out/Release'\n\n# Replace macros in each .js file\ncd 'lib'\nfor file in ls '*.js'\n sed '-i', 'BUILD_VERSION', 'v0.1.2', file\n sed '-i', /.*REMOVE_THIS_LINE.*\\n/, '', file\n sed '-i', /.*REPLACE_LINE_WITH_MACRO.*\\n/, cat 'macro.js', file\ncd '..'\n\n# Run external tool synchronously\nif (exec 'git commit -am \"Auto-commit\"').code != 0\n echo 'Error: Git commit failed'\n exit 1\n```\n\n## Global vs. Local\n\nThe example above uses the convenience script `shelljs/global` to reduce verbosity. If polluting your global namespace is not desirable, simply require `shelljs`.\n\nExample:\n\n```javascript\nvar shell = require('shelljs');\nshell.echo('hello world');\n```\n\n## Make tool\n\nA convenience script `shelljs/make` is also provided to mimic the behavior of a Unix Makefile. In this case all shell objects are global, and command line arguments will cause the script to execute only the corresponding function in the global `target` object. To avoid redundant calls, target functions are executed only once per script.\n\nExample (CoffeeScript):\n\n```coffeescript\nrequire 'shelljs/make'\n\ntarget.all = ->\n target.bundle()\n target.docs()\n\ntarget.bundle = ->\n cd __dirname\n mkdir 'build'\n cd 'lib'\n (cat '*.js').to '../build/output.js'\n\ntarget.docs = ->\n cd __dirname\n mkdir 'docs'\n cd 'lib'\n for file in ls '*.js'\n text = grep '//@', file # extract special comments\n text.replace '//@', '' # remove comment tags\n text.to 'docs/my_docs.md'\n```\n\nTo run the target `all`, call the above script without arguments: `$ node make`. To run the target `docs`: `$ node make docs`, and so on.\n\n\n\n\n\n\n## Command reference\n\n\nAll commands run synchronously, unless otherwise stated.\n\n\n### cd('dir')\nChanges to directory `dir` for the duration of the script\n\n### pwd()\nReturns the current directory.\n\n### ls([options ,] path [,path ...])\n### ls([options ,] path_array)\nAvailable options:\n\n+ `-R`: recursive\n+ `-A`: all files (include files beginning with `.`, except for `.` and `..`)\n\nExamples:\n\n```javascript\nls('projs/*.js');\nls('-R', '/users/me', '/tmp');\nls('-R', ['/users/me', '/tmp']); // same as above\n```\n\nReturns array of files in the given path, or in current directory if no path provided.\n\n### find(path [,path ...])\n### find(path_array)\nExamples:\n\n```javascript\nfind('src', 'lib');\nfind(['src', 'lib']); // same as above\nfind('.').filter(function(file) { return file.match(/\\.js$/); });\n```\n\nReturns array of all files (however deep) in the given paths.\n\nThe main difference from `ls('-R', path)` is that the resulting file names\ninclude the base directories, e.g. `lib/resources/file1` instead of just `file1`.\n\n### cp([options ,] source [,source ...], dest)\n### cp([options ,] source_array, dest)\nAvailable options:\n\n+ `-f`: force\n+ `-r, -R`: recursive\n\nExamples:\n\n```javascript\ncp('file1', 'dir1');\ncp('-Rf', '/tmp/*', '/usr/local/*', '/home/tmp');\ncp('-Rf', ['/tmp/*', '/usr/local/*'], '/home/tmp'); // same as above\n```\n\nCopies files. The wildcard `*` is accepted.\n\n### rm([options ,] file [, file ...])\n### rm([options ,] file_array)\nAvailable options:\n\n+ `-f`: force\n+ `-r, -R`: recursive\n\nExamples:\n\n```javascript\nrm('-rf', '/tmp/*');\nrm('some_file.txt', 'another_file.txt');\nrm(['some_file.txt', 'another_file.txt']); // same as above\n```\n\nRemoves files. The wildcard `*` is accepted.\n\n### mv(source [, source ...], dest')\n### mv(source_array, dest')\nAvailable options:\n\n+ `f`: force\n\nExamples:\n\n```javascript\nmv('-f', 'file', 'dir/');\nmv('file1', 'file2', 'dir/');\nmv(['file1', 'file2'], 'dir/'); // same as above\n```\n\nMoves files. The wildcard `*` is accepted.\n\n### mkdir([options ,] dir [, dir ...])\n### mkdir([options ,] dir_array)\nAvailable options:\n\n+ `p`: full path (will create intermediate dirs if necessary)\n\nExamples:\n\n```javascript\nmkdir('-p', '/tmp/a/b/c/d', '/tmp/e/f/g');\nmkdir('-p', ['/tmp/a/b/c/d', '/tmp/e/f/g']); // same as above\n```\n\nCreates directories.\n\n### test(expression)\nAvailable expression primaries:\n\n+ `'-b', 'path'`: true if path is a block device\n+ `'-c', 'path'`: true if path is a character device\n+ `'-d', 'path'`: true if path is a directory\n+ `'-e', 'path'`: true if path exists\n+ `'-f', 'path'`: true if path is a regular file\n+ `'-L', 'path'`: true if path is a symboilc link\n+ `'-p', 'path'`: true if path is a pipe (FIFO)\n+ `'-S', 'path'`: true if path is a socket\n\nExamples:\n\n```javascript\nif (test('-d', path)) { /* do something with dir */ };\nif (!test('-f', path)) continue; // skip if it's a regular file\n```\n\nEvaluates expression using the available primaries and returns corresponding value.\n\n### cat(file [, file ...])\n### cat(file_array)\n\nExamples:\n\n```javascript\nvar str = cat('file*.txt');\nvar str = cat('file1', 'file2');\nvar str = cat(['file1', 'file2']); // same as above\n```\n\nReturns a string containing the given file, or a concatenated string\ncontaining the files if more than one file is given (a new line character is\nintroduced between each file). Wildcard `*` accepted.\n\n### 'string'.to(file)\n\nExamples:\n\n```javascript\ncat('input.txt').to('output.txt');\n```\n\nAnalogous to the redirection operator `>` in Unix, but works with JavaScript strings (such as\nthose returned by `cat`, `grep`, etc). _Like Unix redirections, `to()` will overwrite any existing file!_\n\n### sed([options ,] search_regex, replace_str, file)\nAvailable options:\n\n+ `-i`: Replace contents of 'file' in-place. _Note that no backups will be created!_\n\nExamples:\n\n```javascript\nsed('-i', 'PROGRAM_VERSION', 'v0.1.3', 'source.js');\nsed(/.*DELETE_THIS_LINE.*\\n/, '', 'source.js');\n```\n\nReads an input string from `file` and performs a JavaScript `replace()` on the input\nusing the given search regex and replacement string. Returns the new string after replacement.\n\n### grep([options ,] regex_filter, file [, file ...])\n### grep([options ,] regex_filter, file_array)\nAvailable options:\n\n+ `-v`: Inverse the sense of the regex and print the lines not matching the criteria.\n\nExamples:\n\n```javascript\ngrep('-v', 'GLOBAL_VARIABLE', '*.js');\ngrep('GLOBAL_VARIABLE', '*.js');\n```\n\nReads input string from given files and returns a string containing all lines of the\nfile that match the given `regex_filter`. Wildcard `*` accepted.\n\n### which(command)\n\nExamples:\n\n```javascript\nvar nodeExec = which('node');\n```\n\nSearches for `command` in the system's PATH. On Windows looks for `.exe`, `.cmd`, and `.bat` extensions.\nReturns string containing the absolute path to the command.\n\n### echo(string [,string ...])\n\nExamples:\n\n```javascript\necho('hello world');\nvar str = echo('hello world');\n```\n\nPrints string to stdout, and returns string with additional utility methods\nlike `.to()`.\n\n### dirs([options | '+N' | '-N'])\n\nAvailable options:\n\n+ `-c`: Clears the directory stack by deleting all of the elements.\n\nArguments:\n\n+ `+N`: Displays the Nth directory (counting from the left of the list printed by dirs when invoked without options), starting with zero.\n+ `-N`: Displays the Nth directory (counting from the right of the list printed by dirs when invoked without options), starting with zero.\n\nDisplay the list of currently remembered directories. Returns an array of paths in the stack, or a single path if +N or -N was specified.\n\nSee also: pushd, popd\n\n### pushd([options,] [dir | '-N' | '+N'])\n\nAvailable options:\n\n+ `-n`: Suppresses the normal change of directory when adding directories to the stack, so that only the stack is manipulated.\n\nArguments:\n\n+ `dir`: Makes the current working directory be the top of the stack, and then executes the equivalent of `cd dir`.\n+ `+N`: Brings the Nth directory (counting from the left of the list printed by dirs, starting with zero) to the top of the list by rotating the stack.\n+ `-N`: Brings the Nth directory (counting from the right of the list printed by dirs, starting with zero) to the top of the list by rotating the stack.\n\nExamples:\n\n```javascript\n// process.cwd() === '/usr'\npushd('/etc'); // Returns /etc /usr\npushd('+1'); // Returns /usr /etc\n```\n\nSave the current directory on the top of the directory stack and then cd to `dir`. With no arguments, pushd exchanges the top two directories. Returns an array of paths in the stack.\n\n### popd([options,] ['-N' | '+N'])\n\nAvailable options:\n\n+ `-n`: Suppresses the normal change of directory when removing directories from the stack, so that only the stack is manipulated.\n\nArguments:\n\n+ `+N`: Removes the Nth directory (counting from the left of the list printed by dirs), starting with zero.\n+ `-N`: Removes the Nth directory (counting from the right of the list printed by dirs), starting with zero.\n\nExamples:\n\n```javascript\necho(process.cwd()); // '/usr'\npushd('/etc'); // '/etc /usr'\necho(process.cwd()); // '/etc'\npopd(); // '/usr'\necho(process.cwd()); // '/usr'\n```\n\nWhen no arguments are given, popd removes the top directory from the stack and performs a cd to the new top directory. The elements are numbered from 0 starting at the first directory listed with dirs; i.e., popd is equivalent to popd +0. Returns an array of paths in the stack.\n\n### exit(code)\nExits the current process with the given exit code.\n\n### env['VAR_NAME']\nObject containing environment variables (both getter and setter). Shortcut to process.env.\n\n### exec(command [, options] [, callback])\nAvailable options (all `false` by default):\n\n+ `async`: Asynchronous execution. Defaults to true if a callback is provided.\n+ `silent`: Do not echo program output to console.\n\nExamples:\n\n```javascript\nvar version = exec('node --version', {silent:true}).output;\n\nvar child = exec('some_long_running_process', {async:true});\nchild.stdout.on('data', function(data) {\n /* ... do something with data ... */\n});\n\nexec('some_long_running_process', function(code, output) {\n console.log('Exit code:', code);\n console.log('Program output:', output);\n});\n```\n\nExecutes the given `command` _synchronously_, unless otherwise specified.\nWhen in synchronous mode returns the object `{ code:..., output:... }`, containing the program's\n`output` (stdout + stderr) and its exit `code`. Otherwise returns the child process object, and\nthe `callback` gets the arguments `(code, output)`.\n\n**Note:** For long-lived processes, it's best to run `exec()` asynchronously as\nthe current synchronous implementation uses a lot of CPU. This should be getting\nfixed soon.\n\n### chmod(octal_mode || octal_string, file)\n### chmod(symbolic_mode, file)\n\nAvailable options:\n\n+ `-v`: output a diagnostic for every file processed\n+ `-c`: like verbose but report only when a change is made\n+ `-R`: change files and directories recursively\n\nExamples:\n\n```javascript\nchmod(755, '/Users/brandon');\nchmod('755', '/Users/brandon'); // same as above \nchmod('u+x', '/Users/brandon');\n```\n\nAlters the permissions of a file or directory by either specifying the\nabsolute permissions in octal form or expressing the changes in symbols.\nThis command tries to mimic the POSIX behavior as much as possible.\nNotable exceptions:\n\n+ In symbolic modes, 'a-r' and '-r' are identical. No consideration is\n given to the umask.\n+ There is no \"quiet\" option since default behavior is to run silent.\n\n## Configuration\n\n\n### config.silent\nExample:\n\n```javascript\nvar silentState = config.silent; // save old silent state\nconfig.silent = true;\n/* ... */\nconfig.silent = silentState; // restore old silent state\n```\n\nSuppresses all command output if `true`, except for `echo()` calls.\nDefault is `false`.\n\n### config.fatal\nExample:\n\n```javascript\nconfig.fatal = true;\ncp('this_file_does_not_exist', '/dev/null'); // dies here\n/* more commands... */\n```\n\nIf `true` the script will die on errors. Default is `false`.\n\n## Non-Unix commands\n\n\n### tempdir()\nSearches and returns string containing a writeable, platform-dependent temporary directory.\nFollows Python's [tempfile algorithm](http://docs.python.org/library/tempfile.html#tempfile.tempdir).\n\n### error()\nTests if error occurred in the last command. Returns `null` if no error occurred,\notherwise returns string explaining the error\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/arturadib/shelljs/issues" + }, + "_id": "shelljs@0.1.4", + "dist": { + "shasum": "7a8aeaa3dc3c0be2e59d83168e83b4c4bc4dac95" + }, + "_from": "shelljs@0.1.4", + "_resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz" +} diff --git a/platforms/android/cordova/node_modules/shelljs/scripts/docs.js b/platforms/android/cordova/node_modules/shelljs/scripts/docs.js new file mode 100644 index 0000000..68a2138 --- /dev/null +++ b/platforms/android/cordova/node_modules/shelljs/scripts/docs.js @@ -0,0 +1,15 @@ +#!/usr/bin/env node +require('../global'); + +echo('Appending docs to README.md'); + +cd(__dirname + '/..'); + +// Extract docs from shell.js +var docs = grep('//@', 'shell.js'); +// Remove '//@' +docs = docs.replace(/\/\/\@ ?/g, ''); +// Append docs to README +sed('-i', /## Command reference(.|\n)*/, '## Command reference\n\n' + docs, 'README.md'); + +echo('All done.'); diff --git a/platforms/android/cordova/node_modules/shelljs/scripts/run-tests.js b/platforms/android/cordova/node_modules/shelljs/scripts/run-tests.js new file mode 100644 index 0000000..a9d32fc --- /dev/null +++ b/platforms/android/cordova/node_modules/shelljs/scripts/run-tests.js @@ -0,0 +1,50 @@ +#!/usr/bin/env node +require('../global'); + +var path = require('path'); + +var failed = false; + +// +// Lint +// +JSHINT_BIN = './node_modules/jshint/bin/jshint'; +cd(__dirname + '/..'); + +if (!test('-f', JSHINT_BIN)) { + echo('JSHint not found. Run `npm install` in the root dir first.'); + exit(1); +} + +if (exec(JSHINT_BIN + ' --config jshint.json *.js test/*.js').code !== 0) { + failed = true; + echo('*** JSHINT FAILED! (return code != 0)'); + echo(); +} else { + echo('All JSHint tests passed'); + echo(); +} + +// +// Unit tests +// +cd(__dirname + '/../test'); +ls('*.js').forEach(function(file) { + echo('Running test:', file); + if (exec('node ' + file).code !== 123) { // 123 avoids false positives (e.g. premature exit) + failed = true; + echo('*** TEST FAILED! (missing exit code "123")'); + echo(); + } +}); + +if (failed) { + echo(); + echo('*******************************************************'); + echo('WARNING: Some tests did not pass!'); + echo('*******************************************************'); + exit(1); +} else { + echo(); + echo('All tests passed.'); +} diff --git a/platforms/android/cordova/node_modules/shelljs/shell.js b/platforms/android/cordova/node_modules/shelljs/shell.js new file mode 100644 index 0000000..7a4f4c8 --- /dev/null +++ b/platforms/android/cordova/node_modules/shelljs/shell.js @@ -0,0 +1,1901 @@ +// +// ShellJS +// Unix shell commands on top of Node's API +// +// Copyright (c) 2012 Artur Adib +// http://github.com/arturadib/shelljs +// + +var fs = require('fs'), + path = require('path'), + util = require('util'), + vm = require('vm'), + child = require('child_process'), + os = require('os'); + +// Node shims for < v0.7 +fs.existsSync = fs.existsSync || path.existsSync; + +var config = { + silent: false, + fatal: false +}; + +var state = { + error: null, + currentCmd: 'shell.js', + tempDir: null + }, + platform = os.type().match(/^Win/) ? 'win' : 'unix'; + + +//@ +//@ All commands run synchronously, unless otherwise stated. +//@ + + +//@ +//@ ### cd('dir') +//@ Changes to directory `dir` for the duration of the script +function _cd(options, dir) { + if (!dir) + error('directory not specified'); + + if (!fs.existsSync(dir)) + error('no such file or directory: ' + dir); + + if (!fs.statSync(dir).isDirectory()) + error('not a directory: ' + dir); + + process.chdir(dir); +} +exports.cd = wrap('cd', _cd); + +//@ +//@ ### pwd() +//@ Returns the current directory. +function _pwd(options) { + var pwd = path.resolve(process.cwd()); + return ShellString(pwd); +} +exports.pwd = wrap('pwd', _pwd); + + +//@ +//@ ### ls([options ,] path [,path ...]) +//@ ### ls([options ,] path_array) +//@ Available options: +//@ +//@ + `-R`: recursive +//@ + `-A`: all files (include files beginning with `.`, except for `.` and `..`) +//@ +//@ Examples: +//@ +//@ ```javascript +//@ ls('projs/*.js'); +//@ ls('-R', '/users/me', '/tmp'); +//@ ls('-R', ['/users/me', '/tmp']); // same as above +//@ ``` +//@ +//@ Returns array of files in the given path, or in current directory if no path provided. +function _ls(options, paths) { + options = parseOptions(options, { + 'R': 'recursive', + 'A': 'all', + 'a': 'all_deprecated' + }); + + if (options.all_deprecated) { + // We won't support the -a option as it's hard to image why it's useful + // (it includes '.' and '..' in addition to '.*' files) + // For backwards compatibility we'll dump a deprecated message and proceed as before + log('ls: Option -a is deprecated. Use -A instead'); + options.all = true; + } + + if (!paths) + paths = ['.']; + else if (typeof paths === 'object') + paths = paths; // assume array + else if (typeof paths === 'string') + paths = [].slice.call(arguments, 1); + + var list = []; + + // Conditionally pushes file to list - returns true if pushed, false otherwise + // (e.g. prevents hidden files to be included unless explicitly told so) + function pushFile(file, query) { + // hidden file? + if (path.basename(file)[0] === '.') { + // not explicitly asking for hidden files? + if (!options.all && !(path.basename(query)[0] === '.' && path.basename(query).length > 1)) + return false; + } + + if (platform === 'win') + file = file.replace(/\\/g, '/'); + + list.push(file); + return true; + } + + paths.forEach(function(p) { + if (fs.existsSync(p)) { + var stats = fs.statSync(p); + // Simple file? + if (stats.isFile()) { + pushFile(p, p); + return; // continue + } + + // Simple dir? + if (stats.isDirectory()) { + // Iterate over p contents + fs.readdirSync(p).forEach(function(file) { + if (!pushFile(file, p)) + return; + + // Recursive? + if (options.recursive) { + var oldDir = _pwd(); + _cd('', p); + if (fs.statSync(file).isDirectory()) + list = list.concat(_ls('-R'+(options.all?'A':''), file+'/*')); + _cd('', oldDir); + } + }); + return; // continue + } + } + + // p does not exist - possible wildcard present + + var basename = path.basename(p); + var dirname = path.dirname(p); + // Wildcard present on an existing dir? (e.g. '/tmp/*.js') + if (basename.search(/\*/) > -1 && fs.existsSync(dirname) && fs.statSync(dirname).isDirectory) { + // Escape special regular expression chars + var regexp = basename.replace(/(\^|\$|\(|\)|<|>|\[|\]|\{|\}|\.|\+|\?)/g, '\\$1'); + // Translates wildcard into regex + regexp = '^' + regexp.replace(/\*/g, '.*') + '$'; + // Iterate over directory contents + fs.readdirSync(dirname).forEach(function(file) { + if (file.match(new RegExp(regexp))) { + if (!pushFile(path.normalize(dirname+'/'+file), basename)) + return; + + // Recursive? + if (options.recursive) { + var pp = dirname + '/' + file; + if (fs.lstatSync(pp).isDirectory()) + list = list.concat(_ls('-R'+(options.all?'A':''), pp+'/*')); + } // recursive + } // if file matches + }); // forEach + return; + } + + error('no such file or directory: ' + p, true); + }); + + return list; +} +exports.ls = wrap('ls', _ls); + + +//@ +//@ ### find(path [,path ...]) +//@ ### find(path_array) +//@ Examples: +//@ +//@ ```javascript +//@ find('src', 'lib'); +//@ find(['src', 'lib']); // same as above +//@ find('.').filter(function(file) { return file.match(/\.js$/); }); +//@ ``` +//@ +//@ Returns array of all files (however deep) in the given paths. +//@ +//@ The main difference from `ls('-R', path)` is that the resulting file names +//@ include the base directories, e.g. `lib/resources/file1` instead of just `file1`. +function _find(options, paths) { + if (!paths) + error('no path specified'); + else if (typeof paths === 'object') + paths = paths; // assume array + else if (typeof paths === 'string') + paths = [].slice.call(arguments, 1); + + var list = []; + + function pushFile(file) { + if (platform === 'win') + file = file.replace(/\\/g, '/'); + list.push(file); + } + + // why not simply do ls('-R', paths)? because the output wouldn't give the base dirs + // to get the base dir in the output, we need instead ls('-R', 'dir/*') for every directory + + paths.forEach(function(file) { + pushFile(file); + + if (fs.statSync(file).isDirectory()) { + _ls('-RA', file+'/*').forEach(function(subfile) { + pushFile(subfile); + }); + } + }); + + return list; +} +exports.find = wrap('find', _find); + + +//@ +//@ ### cp([options ,] source [,source ...], dest) +//@ ### cp([options ,] source_array, dest) +//@ Available options: +//@ +//@ + `-f`: force +//@ + `-r, -R`: recursive +//@ +//@ Examples: +//@ +//@ ```javascript +//@ cp('file1', 'dir1'); +//@ cp('-Rf', '/tmp/*', '/usr/local/*', '/home/tmp'); +//@ cp('-Rf', ['/tmp/*', '/usr/local/*'], '/home/tmp'); // same as above +//@ ``` +//@ +//@ Copies files. The wildcard `*` is accepted. +function _cp(options, sources, dest) { + options = parseOptions(options, { + 'f': 'force', + 'R': 'recursive', + 'r': 'recursive' + }); + + // Get sources, dest + if (arguments.length < 3) { + error('missing and/or '); + } else if (arguments.length > 3) { + sources = [].slice.call(arguments, 1, arguments.length - 1); + dest = arguments[arguments.length - 1]; + } else if (typeof sources === 'string') { + sources = [sources]; + } else if ('length' in sources) { + sources = sources; // no-op for array + } else { + error('invalid arguments'); + } + + var exists = fs.existsSync(dest), + stats = exists && fs.statSync(dest); + + // Dest is not existing dir, but multiple sources given + if ((!exists || !stats.isDirectory()) && sources.length > 1) + error('dest is not a directory (too many sources)'); + + // Dest is an existing file, but no -f given + if (exists && stats.isFile() && !options.force) + error('dest file already exists: ' + dest); + + if (options.recursive) { + // Recursive allows the shortcut syntax "sourcedir/" for "sourcedir/*" + // (see Github issue #15) + sources.forEach(function(src, i) { + if (src[src.length - 1] === '/') + sources[i] += '*'; + }); + + // Create dest + try { + fs.mkdirSync(dest, parseInt('0777', 8)); + } catch (e) { + // like Unix's cp, keep going even if we can't create dest dir + } + } + + sources = expand(sources); + + sources.forEach(function(src) { + if (!fs.existsSync(src)) { + error('no such file or directory: '+src, true); + return; // skip file + } + + // If here, src exists + if (fs.statSync(src).isDirectory()) { + if (!options.recursive) { + // Non-Recursive + log(src + ' is a directory (not copied)'); + } else { + // Recursive + // 'cp /a/source dest' should create 'source' in 'dest' + var newDest = path.join(dest, path.basename(src)), + checkDir = fs.statSync(src); + try { + fs.mkdirSync(newDest, checkDir.mode); + } catch (e) { + //if the directory already exists, that's okay + if (e.code !== 'EEXIST') throw e; + } + + cpdirSyncRecursive(src, newDest, {force: options.force}); + } + return; // done with dir + } + + // If here, src is a file + + // When copying to '/path/dir': + // thisDest = '/path/dir/file1' + var thisDest = dest; + if (fs.existsSync(dest) && fs.statSync(dest).isDirectory()) + thisDest = path.normalize(dest + '/' + path.basename(src)); + + if (fs.existsSync(thisDest) && !options.force) { + error('dest file already exists: ' + thisDest, true); + return; // skip file + } + + copyFileSync(src, thisDest); + }); // forEach(src) +} +exports.cp = wrap('cp', _cp); + +//@ +//@ ### rm([options ,] file [, file ...]) +//@ ### rm([options ,] file_array) +//@ Available options: +//@ +//@ + `-f`: force +//@ + `-r, -R`: recursive +//@ +//@ Examples: +//@ +//@ ```javascript +//@ rm('-rf', '/tmp/*'); +//@ rm('some_file.txt', 'another_file.txt'); +//@ rm(['some_file.txt', 'another_file.txt']); // same as above +//@ ``` +//@ +//@ Removes files. The wildcard `*` is accepted. +function _rm(options, files) { + options = parseOptions(options, { + 'f': 'force', + 'r': 'recursive', + 'R': 'recursive' + }); + if (!files) + error('no paths given'); + + if (typeof files === 'string') + files = [].slice.call(arguments, 1); + // if it's array leave it as it is + + files = expand(files); + + files.forEach(function(file) { + if (!fs.existsSync(file)) { + // Path does not exist, no force flag given + if (!options.force) + error('no such file or directory: '+file, true); + + return; // skip file + } + + // If here, path exists + + var stats = fs.statSync(file); + // Remove simple file + if (stats.isFile()) { + + // Do not check for file writing permissions + if (options.force) { + _unlinkSync(file); + return; + } + + if (isWriteable(file)) + _unlinkSync(file); + else + error('permission denied: '+file, true); + + return; + } // simple file + + // Path is an existing directory, but no -r flag given + if (stats.isDirectory() && !options.recursive) { + error('path is a directory', true); + return; // skip path + } + + // Recursively remove existing directory + if (stats.isDirectory() && options.recursive) { + rmdirSyncRecursive(file, options.force); + } + }); // forEach(file) +} // rm +exports.rm = wrap('rm', _rm); + +//@ +//@ ### mv(source [, source ...], dest') +//@ ### mv(source_array, dest') +//@ Available options: +//@ +//@ + `f`: force +//@ +//@ Examples: +//@ +//@ ```javascript +//@ mv('-f', 'file', 'dir/'); +//@ mv('file1', 'file2', 'dir/'); +//@ mv(['file1', 'file2'], 'dir/'); // same as above +//@ ``` +//@ +//@ Moves files. The wildcard `*` is accepted. +function _mv(options, sources, dest) { + options = parseOptions(options, { + 'f': 'force' + }); + + // Get sources, dest + if (arguments.length < 3) { + error('missing and/or '); + } else if (arguments.length > 3) { + sources = [].slice.call(arguments, 1, arguments.length - 1); + dest = arguments[arguments.length - 1]; + } else if (typeof sources === 'string') { + sources = [sources]; + } else if ('length' in sources) { + sources = sources; // no-op for array + } else { + error('invalid arguments'); + } + + sources = expand(sources); + + var exists = fs.existsSync(dest), + stats = exists && fs.statSync(dest); + + // Dest is not existing dir, but multiple sources given + if ((!exists || !stats.isDirectory()) && sources.length > 1) + error('dest is not a directory (too many sources)'); + + // Dest is an existing file, but no -f given + if (exists && stats.isFile() && !options.force) + error('dest file already exists: ' + dest); + + sources.forEach(function(src) { + if (!fs.existsSync(src)) { + error('no such file or directory: '+src, true); + return; // skip file + } + + // If here, src exists + + // When copying to '/path/dir': + // thisDest = '/path/dir/file1' + var thisDest = dest; + if (fs.existsSync(dest) && fs.statSync(dest).isDirectory()) + thisDest = path.normalize(dest + '/' + path.basename(src)); + + if (fs.existsSync(thisDest) && !options.force) { + error('dest file already exists: ' + thisDest, true); + return; // skip file + } + + if (path.resolve(src) === path.dirname(path.resolve(thisDest))) { + error('cannot move to self: '+src, true); + return; // skip file + } + + fs.renameSync(src, thisDest); + }); // forEach(src) +} // mv +exports.mv = wrap('mv', _mv); + +//@ +//@ ### mkdir([options ,] dir [, dir ...]) +//@ ### mkdir([options ,] dir_array) +//@ Available options: +//@ +//@ + `p`: full path (will create intermediate dirs if necessary) +//@ +//@ Examples: +//@ +//@ ```javascript +//@ mkdir('-p', '/tmp/a/b/c/d', '/tmp/e/f/g'); +//@ mkdir('-p', ['/tmp/a/b/c/d', '/tmp/e/f/g']); // same as above +//@ ``` +//@ +//@ Creates directories. +function _mkdir(options, dirs) { + options = parseOptions(options, { + 'p': 'fullpath' + }); + if (!dirs) + error('no paths given'); + + if (typeof dirs === 'string') + dirs = [].slice.call(arguments, 1); + // if it's array leave it as it is + + dirs.forEach(function(dir) { + if (fs.existsSync(dir)) { + if (!options.fullpath) + error('path already exists: ' + dir, true); + return; // skip dir + } + + // Base dir does not exist, and no -p option given + var baseDir = path.dirname(dir); + if (!fs.existsSync(baseDir) && !options.fullpath) { + error('no such file or directory: ' + baseDir, true); + return; // skip dir + } + + if (options.fullpath) + mkdirSyncRecursive(dir); + else + fs.mkdirSync(dir, parseInt('0777', 8)); + }); +} // mkdir +exports.mkdir = wrap('mkdir', _mkdir); + +//@ +//@ ### test(expression) +//@ Available expression primaries: +//@ +//@ + `'-b', 'path'`: true if path is a block device +//@ + `'-c', 'path'`: true if path is a character device +//@ + `'-d', 'path'`: true if path is a directory +//@ + `'-e', 'path'`: true if path exists +//@ + `'-f', 'path'`: true if path is a regular file +//@ + `'-L', 'path'`: true if path is a symboilc link +//@ + `'-p', 'path'`: true if path is a pipe (FIFO) +//@ + `'-S', 'path'`: true if path is a socket +//@ +//@ Examples: +//@ +//@ ```javascript +//@ if (test('-d', path)) { /* do something with dir */ }; +//@ if (!test('-f', path)) continue; // skip if it's a regular file +//@ ``` +//@ +//@ Evaluates expression using the available primaries and returns corresponding value. +function _test(options, path) { + if (!path) + error('no path given'); + + // hack - only works with unary primaries + options = parseOptions(options, { + 'b': 'block', + 'c': 'character', + 'd': 'directory', + 'e': 'exists', + 'f': 'file', + 'L': 'link', + 'p': 'pipe', + 'S': 'socket' + }); + + var canInterpret = false; + for (var key in options) + if (options[key] === true) { + canInterpret = true; + break; + } + + if (!canInterpret) + error('could not interpret expression'); + + if (options.link) { + try { + return fs.lstatSync(path).isSymbolicLink(); + } catch(e) { + return false; + } + } + + if (!fs.existsSync(path)) + return false; + + if (options.exists) + return true; + + var stats = fs.statSync(path); + + if (options.block) + return stats.isBlockDevice(); + + if (options.character) + return stats.isCharacterDevice(); + + if (options.directory) + return stats.isDirectory(); + + if (options.file) + return stats.isFile(); + + if (options.pipe) + return stats.isFIFO(); + + if (options.socket) + return stats.isSocket(); +} // test +exports.test = wrap('test', _test); + + +//@ +//@ ### cat(file [, file ...]) +//@ ### cat(file_array) +//@ +//@ Examples: +//@ +//@ ```javascript +//@ var str = cat('file*.txt'); +//@ var str = cat('file1', 'file2'); +//@ var str = cat(['file1', 'file2']); // same as above +//@ ``` +//@ +//@ Returns a string containing the given file, or a concatenated string +//@ containing the files if more than one file is given (a new line character is +//@ introduced between each file). Wildcard `*` accepted. +function _cat(options, files) { + var cat = ''; + + if (!files) + error('no paths given'); + + if (typeof files === 'string') + files = [].slice.call(arguments, 1); + // if it's array leave it as it is + + files = expand(files); + + files.forEach(function(file) { + if (!fs.existsSync(file)) + error('no such file or directory: ' + file); + + cat += fs.readFileSync(file, 'utf8') + '\n'; + }); + + if (cat[cat.length-1] === '\n') + cat = cat.substring(0, cat.length-1); + + return ShellString(cat); +} +exports.cat = wrap('cat', _cat); + +//@ +//@ ### 'string'.to(file) +//@ +//@ Examples: +//@ +//@ ```javascript +//@ cat('input.txt').to('output.txt'); +//@ ``` +//@ +//@ Analogous to the redirection operator `>` in Unix, but works with JavaScript strings (such as +//@ those returned by `cat`, `grep`, etc). _Like Unix redirections, `to()` will overwrite any existing file!_ +function _to(options, file) { + if (!file) + error('wrong arguments'); + + if (!fs.existsSync( path.dirname(file) )) + error('no such file or directory: ' + path.dirname(file)); + + try { + fs.writeFileSync(file, this.toString(), 'utf8'); + } catch(e) { + error('could not write to file (code '+e.code+'): '+file, true); + } +} +// In the future, when Proxies are default, we can add methods like `.to()` to primitive strings. +// For now, this is a dummy function to bookmark places we need such strings +function ShellString(str) { + return str; +} +String.prototype.to = wrap('to', _to); + +//@ +//@ ### sed([options ,] search_regex, replace_str, file) +//@ Available options: +//@ +//@ + `-i`: Replace contents of 'file' in-place. _Note that no backups will be created!_ +//@ +//@ Examples: +//@ +//@ ```javascript +//@ sed('-i', 'PROGRAM_VERSION', 'v0.1.3', 'source.js'); +//@ sed(/.*DELETE_THIS_LINE.*\n/, '', 'source.js'); +//@ ``` +//@ +//@ Reads an input string from `file` and performs a JavaScript `replace()` on the input +//@ using the given search regex and replacement string. Returns the new string after replacement. +function _sed(options, regex, replacement, file) { + options = parseOptions(options, { + 'i': 'inplace' + }); + + if (typeof replacement === 'string') + replacement = replacement; // no-op + else if (typeof replacement === 'number') + replacement = replacement.toString(); // fallback + else + error('invalid replacement string'); + + if (!file) + error('no file given'); + + if (!fs.existsSync(file)) + error('no such file or directory: ' + file); + + var result = fs.readFileSync(file, 'utf8').replace(regex, replacement); + if (options.inplace) + fs.writeFileSync(file, result, 'utf8'); + + return ShellString(result); +} +exports.sed = wrap('sed', _sed); + +//@ +//@ ### grep([options ,] regex_filter, file [, file ...]) +//@ ### grep([options ,] regex_filter, file_array) +//@ Available options: +//@ +//@ + `-v`: Inverse the sense of the regex and print the lines not matching the criteria. +//@ +//@ Examples: +//@ +//@ ```javascript +//@ grep('-v', 'GLOBAL_VARIABLE', '*.js'); +//@ grep('GLOBAL_VARIABLE', '*.js'); +//@ ``` +//@ +//@ Reads input string from given files and returns a string containing all lines of the +//@ file that match the given `regex_filter`. Wildcard `*` accepted. +function _grep(options, regex, files) { + options = parseOptions(options, { + 'v': 'inverse' + }); + + if (!files) + error('no paths given'); + + if (typeof files === 'string') + files = [].slice.call(arguments, 2); + // if it's array leave it as it is + + files = expand(files); + + var grep = ''; + files.forEach(function(file) { + if (!fs.existsSync(file)) { + error('no such file or directory: ' + file, true); + return; + } + + var contents = fs.readFileSync(file, 'utf8'), + lines = contents.split(/\r*\n/); + lines.forEach(function(line) { + var matched = line.match(regex); + if ((options.inverse && !matched) || (!options.inverse && matched)) + grep += line + '\n'; + }); + }); + + return ShellString(grep); +} +exports.grep = wrap('grep', _grep); + + +//@ +//@ ### which(command) +//@ +//@ Examples: +//@ +//@ ```javascript +//@ var nodeExec = which('node'); +//@ ``` +//@ +//@ Searches for `command` in the system's PATH. On Windows looks for `.exe`, `.cmd`, and `.bat` extensions. +//@ Returns string containing the absolute path to the command. +function _which(options, cmd) { + if (!cmd) + error('must specify command'); + + var pathEnv = process.env.path || process.env.Path || process.env.PATH, + pathArray = splitPath(pathEnv), + where = null; + + // No relative/absolute paths provided? + if (cmd.search(/\//) === -1) { + // Search for command in PATH + pathArray.forEach(function(dir) { + if (where) + return; // already found it + + var attempt = path.resolve(dir + '/' + cmd); + if (fs.existsSync(attempt)) { + where = attempt; + return; + } + + if (platform === 'win') { + var baseAttempt = attempt; + attempt = baseAttempt + '.exe'; + if (fs.existsSync(attempt)) { + where = attempt; + return; + } + attempt = baseAttempt + '.cmd'; + if (fs.existsSync(attempt)) { + where = attempt; + return; + } + attempt = baseAttempt + '.bat'; + if (fs.existsSync(attempt)) { + where = attempt; + return; + } + } // if 'win' + }); + } + + // Command not found anywhere? + if (!fs.existsSync(cmd) && !where) + return null; + + where = where || path.resolve(cmd); + + return ShellString(where); +} +exports.which = wrap('which', _which); + +//@ +//@ ### echo(string [,string ...]) +//@ +//@ Examples: +//@ +//@ ```javascript +//@ echo('hello world'); +//@ var str = echo('hello world'); +//@ ``` +//@ +//@ Prints string to stdout, and returns string with additional utility methods +//@ like `.to()`. +function _echo() { + var messages = [].slice.call(arguments, 0); + console.log.apply(this, messages); + return ShellString(messages.join(' ')); +} +exports.echo = _echo; // don't wrap() as it could parse '-options' + +// Pushd/popd/dirs internals +var _dirStack = []; + +function _isStackIndex(index) { + return (/^[\-+]\d+$/).test(index); +} + +function _parseStackIndex(index) { + if (_isStackIndex(index)) { + if (Math.abs(index) < _dirStack.length + 1) { // +1 for pwd + return (/^-/).test(index) ? Number(index) - 1 : Number(index); + } else { + error(index + ': directory stack index out of range'); + } + } else { + error(index + ': invalid number'); + } +} + +function _actualDirStack() { + return [process.cwd()].concat(_dirStack); +} + +//@ +//@ ### dirs([options | '+N' | '-N']) +//@ +//@ Available options: +//@ +//@ + `-c`: Clears the directory stack by deleting all of the elements. +//@ +//@ Arguments: +//@ +//@ + `+N`: Displays the Nth directory (counting from the left of the list printed by dirs when invoked without options), starting with zero. +//@ + `-N`: Displays the Nth directory (counting from the right of the list printed by dirs when invoked without options), starting with zero. +//@ +//@ Display the list of currently remembered directories. Returns an array of paths in the stack, or a single path if +N or -N was specified. +//@ +//@ See also: pushd, popd +function _dirs(options, index) { + if (_isStackIndex(options)) { + index = options; + options = ''; + } + + options = parseOptions(options, { + 'c' : 'clear' + }); + + if (options['clear']) { + _dirStack = []; + return _dirStack; + } + + var stack = _actualDirStack(); + + if (index) { + index = _parseStackIndex(index); + + if (index < 0) { + index = stack.length + index; + } + + log(stack[index]); + return stack[index]; + } + + log(stack.join(' ')); + + return stack; +} +exports.dirs = wrap("dirs", _dirs); + +//@ +//@ ### pushd([options,] [dir | '-N' | '+N']) +//@ +//@ Available options: +//@ +//@ + `-n`: Suppresses the normal change of directory when adding directories to the stack, so that only the stack is manipulated. +//@ +//@ Arguments: +//@ +//@ + `dir`: Makes the current working directory be the top of the stack, and then executes the equivalent of `cd dir`. +//@ + `+N`: Brings the Nth directory (counting from the left of the list printed by dirs, starting with zero) to the top of the list by rotating the stack. +//@ + `-N`: Brings the Nth directory (counting from the right of the list printed by dirs, starting with zero) to the top of the list by rotating the stack. +//@ +//@ Examples: +//@ +//@ ```javascript +//@ // process.cwd() === '/usr' +//@ pushd('/etc'); // Returns /etc /usr +//@ pushd('+1'); // Returns /usr /etc +//@ ``` +//@ +//@ Save the current directory on the top of the directory stack and then cd to `dir`. With no arguments, pushd exchanges the top two directories. Returns an array of paths in the stack. +function _pushd(options, dir) { + if (_isStackIndex(options)) { + dir = options; + options = ''; + } + + options = parseOptions(options, { + 'n' : 'no-cd' + }); + + var dirs = _actualDirStack(); + + if (dir === '+0') { + return dirs; // +0 is a noop + } else if (!dir) { + if (dirs.length > 1) { + dirs = dirs.splice(1, 1).concat(dirs); + } else { + return error('no other directory'); + } + } else if (_isStackIndex(dir)) { + var n = _parseStackIndex(dir); + dirs = dirs.slice(n).concat(dirs.slice(0, n)); + } else { + if (options['no-cd']) { + dirs.splice(1, 0, dir); + } else { + dirs.unshift(dir); + } + } + + if (options['no-cd']) { + dirs = dirs.slice(1); + } else { + dir = path.resolve(dirs.shift()); + _cd('', dir); + } + + _dirStack = dirs; + return _dirs(''); +} +exports.pushd = wrap('pushd', _pushd); + +//@ +//@ ### popd([options,] ['-N' | '+N']) +//@ +//@ Available options: +//@ +//@ + `-n`: Suppresses the normal change of directory when removing directories from the stack, so that only the stack is manipulated. +//@ +//@ Arguments: +//@ +//@ + `+N`: Removes the Nth directory (counting from the left of the list printed by dirs), starting with zero. +//@ + `-N`: Removes the Nth directory (counting from the right of the list printed by dirs), starting with zero. +//@ +//@ Examples: +//@ +//@ ```javascript +//@ echo(process.cwd()); // '/usr' +//@ pushd('/etc'); // '/etc /usr' +//@ echo(process.cwd()); // '/etc' +//@ popd(); // '/usr' +//@ echo(process.cwd()); // '/usr' +//@ ``` +//@ +//@ When no arguments are given, popd removes the top directory from the stack and performs a cd to the new top directory. The elements are numbered from 0 starting at the first directory listed with dirs; i.e., popd is equivalent to popd +0. Returns an array of paths in the stack. +function _popd(options, index) { + if (_isStackIndex(options)) { + index = options; + options = ''; + } + + options = parseOptions(options, { + 'n' : 'no-cd' + }); + + if (!_dirStack.length) { + return error('directory stack empty'); + } + + index = _parseStackIndex(index || '+0'); + + if (options['no-cd'] || index > 0 || _dirStack.length + index === 0) { + index = index > 0 ? index - 1 : index; + _dirStack.splice(index, 1); + } else { + var dir = path.resolve(_dirStack.shift()); + _cd('', dir); + } + + return _dirs(''); +} +exports.popd = wrap("popd", _popd); + +//@ +//@ ### exit(code) +//@ Exits the current process with the given exit code. +exports.exit = process.exit; + +//@ +//@ ### env['VAR_NAME'] +//@ Object containing environment variables (both getter and setter). Shortcut to process.env. +exports.env = process.env; + +//@ +//@ ### exec(command [, options] [, callback]) +//@ Available options (all `false` by default): +//@ +//@ + `async`: Asynchronous execution. Defaults to true if a callback is provided. +//@ + `silent`: Do not echo program output to console. +//@ +//@ Examples: +//@ +//@ ```javascript +//@ var version = exec('node --version', {silent:true}).output; +//@ +//@ var child = exec('some_long_running_process', {async:true}); +//@ child.stdout.on('data', function(data) { +//@ /* ... do something with data ... */ +//@ }); +//@ +//@ exec('some_long_running_process', function(code, output) { +//@ console.log('Exit code:', code); +//@ console.log('Program output:', output); +//@ }); +//@ ``` +//@ +//@ Executes the given `command` _synchronously_, unless otherwise specified. +//@ When in synchronous mode returns the object `{ code:..., output:... }`, containing the program's +//@ `output` (stdout + stderr) and its exit `code`. Otherwise returns the child process object, and +//@ the `callback` gets the arguments `(code, output)`. +//@ +//@ **Note:** For long-lived processes, it's best to run `exec()` asynchronously as +//@ the current synchronous implementation uses a lot of CPU. This should be getting +//@ fixed soon. +function _exec(command, options, callback) { + if (!command) + error('must specify command'); + + // Callback is defined instead of options. + if (typeof options === 'function') { + callback = options; + options = { async: true }; + } + + // Callback is defined with options. + if (typeof options === 'object' && typeof callback === 'function') { + options.async = true; + } + + options = extend({ + silent: config.silent, + async: false + }, options); + + if (options.async) + return execAsync(command, options, callback); + else + return execSync(command, options); +} +exports.exec = wrap('exec', _exec, {notUnix:true}); + +var PERMS = (function (base) { + return { + OTHER_EXEC : base.EXEC, + OTHER_WRITE : base.WRITE, + OTHER_READ : base.READ, + + GROUP_EXEC : base.EXEC << 3, + GROUP_WRITE : base.WRITE << 3, + GROUP_READ : base.READ << 3, + + OWNER_EXEC : base.EXEC << 6, + OWNER_WRITE : base.WRITE << 6, + OWNER_READ : base.READ << 6, + + // Literal octal numbers are apparently not allowed in "strict" javascript. Using parseInt is + // the preferred way, else a jshint warning is thrown. + STICKY : parseInt('01000', 8), + SETGID : parseInt('02000', 8), + SETUID : parseInt('04000', 8), + + TYPE_MASK : parseInt('0770000', 8) + }; +})({ + EXEC : 1, + WRITE : 2, + READ : 4 +}); + + +//@ +//@ ### chmod(octal_mode || octal_string, file) +//@ ### chmod(symbolic_mode, file) +//@ +//@ Available options: +//@ +//@ + `-v`: output a diagnostic for every file processed//@ +//@ + `-c`: like verbose but report only when a change is made//@ +//@ + `-R`: change files and directories recursively//@ +//@ +//@ Examples: +//@ +//@ ```javascript +//@ chmod(755, '/Users/brandon'); +//@ chmod('755', '/Users/brandon'); // same as above +//@ chmod('u+x', '/Users/brandon'); +//@ ``` +//@ +//@ Alters the permissions of a file or directory by either specifying the +//@ absolute permissions in octal form or expressing the changes in symbols. +//@ This command tries to mimic the POSIX behavior as much as possible. +//@ Notable exceptions: +//@ +//@ + In symbolic modes, 'a-r' and '-r' are identical. No consideration is +//@ given to the umask. +//@ + There is no "quiet" option since default behavior is to run silent. +function _chmod(options, mode, filePattern) { + if (!filePattern) { + if (options.length > 0 && options.charAt(0) === '-') { + // Special case where the specified file permissions started with - to subtract perms, which + // get picked up by the option parser as command flags. + // If we are down by one argument and options starts with -, shift everything over. + filePattern = mode; + mode = options; + options = ''; + } + else { + error('You must specify a file.'); + } + } + + options = parseOptions(options, { + 'R': 'recursive', + 'c': 'changes', + 'v': 'verbose' + }); + + if (typeof filePattern === 'string') { + filePattern = [ filePattern ]; + } + + var files; + + if (options.recursive) { + files = []; + expand(filePattern).forEach(function addFile(expandedFile) { + var stat = fs.lstatSync(expandedFile); + + if (!stat.isSymbolicLink()) { + files.push(expandedFile); + + if (stat.isDirectory()) { // intentionally does not follow symlinks. + fs.readdirSync(expandedFile).forEach(function (child) { + addFile(expandedFile + '/' + child); + }); + } + } + }); + } + else { + files = expand(filePattern); + } + + files.forEach(function innerChmod(file) { + file = path.resolve(file); + if (!fs.existsSync(file)) { + error('File not found: ' + file); + } + + // When recursing, don't follow symlinks. + if (options.recursive && fs.lstatSync(file).isSymbolicLink()) { + return; + } + + var perms = fs.statSync(file).mode; + var type = perms & PERMS.TYPE_MASK; + + var newPerms = perms; + + if (isNaN(parseInt(mode, 8))) { + // parse options + mode.split(',').forEach(function (symbolicMode) { + /*jshint regexdash:true */ + var pattern = /([ugoa]*)([=\+-])([rwxXst]*)/i; + var matches = pattern.exec(symbolicMode); + + if (matches) { + var applyTo = matches[1]; + var operator = matches[2]; + var change = matches[3]; + + var changeOwner = applyTo.indexOf('u') != -1 || applyTo === 'a' || applyTo === ''; + var changeGroup = applyTo.indexOf('g') != -1 || applyTo === 'a' || applyTo === ''; + var changeOther = applyTo.indexOf('o') != -1 || applyTo === 'a' || applyTo === ''; + + var changeRead = change.indexOf('r') != -1; + var changeWrite = change.indexOf('w') != -1; + var changeExec = change.indexOf('x') != -1; + var changeSticky = change.indexOf('t') != -1; + var changeSetuid = change.indexOf('s') != -1; + + var mask = 0; + if (changeOwner) { + mask |= (changeRead ? PERMS.OWNER_READ : 0) + (changeWrite ? PERMS.OWNER_WRITE : 0) + (changeExec ? PERMS.OWNER_EXEC : 0) + (changeSetuid ? PERMS.SETUID : 0); + } + if (changeGroup) { + mask |= (changeRead ? PERMS.GROUP_READ : 0) + (changeWrite ? PERMS.GROUP_WRITE : 0) + (changeExec ? PERMS.GROUP_EXEC : 0) + (changeSetuid ? PERMS.SETGID : 0); + } + if (changeOther) { + mask |= (changeRead ? PERMS.OTHER_READ : 0) + (changeWrite ? PERMS.OTHER_WRITE : 0) + (changeExec ? PERMS.OTHER_EXEC : 0); + } + + // Sticky bit is special - it's not tied to user, group or other. + if (changeSticky) { + mask |= PERMS.STICKY; + } + + switch (operator) { + case '+': + newPerms |= mask; + break; + + case '-': + newPerms &= ~mask; + break; + + case '=': + newPerms = type + mask; + + // According to POSIX, when using = to explicitly set the permissions, setuid and setgid can never be cleared. + if (fs.statSync(file).isDirectory()) { + newPerms |= (PERMS.SETUID + PERMS.SETGID) & perms; + } + break; + } + + if (options.verbose) { + log(file + ' -> ' + newPerms.toString(8)); + } + + if (perms != newPerms) { + if (!options.verbose && options.changes) { + log(file + ' -> ' + newPerms.toString(8)); + } + fs.chmodSync(file, newPerms); + } + } + else { + error('Invalid symbolic mode change: ' + symbolicMode); + } + }); + } + else { + // they gave us a full number + newPerms = type + parseInt(mode, 8); + + // POSIX rules are that setuid and setgid can only be added using numeric form, but not cleared. + if (fs.statSync(file).isDirectory()) { + newPerms |= (PERMS.SETUID + PERMS.SETGID) & perms; + } + + fs.chmodSync(file, newPerms); + } + }); +} +exports.chmod = wrap('chmod', _chmod); + + +//@ +//@ ## Configuration +//@ + + + +exports.config = config; + +//@ +//@ ### config.silent +//@ Example: +//@ +//@ ```javascript +//@ var silentState = config.silent; // save old silent state +//@ config.silent = true; +//@ /* ... */ +//@ config.silent = silentState; // restore old silent state +//@ ``` +//@ +//@ Suppresses all command output if `true`, except for `echo()` calls. +//@ Default is `false`. + +//@ +//@ ### config.fatal +//@ Example: +//@ +//@ ```javascript +//@ config.fatal = true; +//@ cp('this_file_does_not_exist', '/dev/null'); // dies here +//@ /* more commands... */ +//@ ``` +//@ +//@ If `true` the script will die on errors. Default is `false`. + + + + +//@ +//@ ## Non-Unix commands +//@ + + + + + + +//@ +//@ ### tempdir() +//@ Searches and returns string containing a writeable, platform-dependent temporary directory. +//@ Follows Python's [tempfile algorithm](http://docs.python.org/library/tempfile.html#tempfile.tempdir). +exports.tempdir = wrap('tempdir', tempDir); + + +//@ +//@ ### error() +//@ Tests if error occurred in the last command. Returns `null` if no error occurred, +//@ otherwise returns string explaining the error +exports.error = function() { + return state.error; +}; + + + + + +//////////////////////////////////////////////////////////////////////////////////////////////// +// +// Auxiliary functions (internal use only) +// + +function log() { + if (!config.silent) + console.log.apply(this, arguments); +} + +function deprecate(what, msg) { + console.log('*** ShellJS.'+what+': This function is deprecated.', msg); +} + +function write(msg) { + if (!config.silent) + process.stdout.write(msg); +} + +// Shows error message. Throws unless _continue or config.fatal are true +function error(msg, _continue) { + if (state.error === null) + state.error = ''; + state.error += state.currentCmd + ': ' + msg + '\n'; + + log(state.error); + + if (config.fatal) + process.exit(1); + + if (!_continue) + throw ''; +} + +// Returns {'alice': true, 'bob': false} when passed: +// parseOptions('-a', {'a':'alice', 'b':'bob'}); +function parseOptions(str, map) { + if (!map) + error('parseOptions() internal error: no map given'); + + // All options are false by default + var options = {}; + for (var letter in map) + options[map[letter]] = false; + + if (!str) + return options; // defaults + + if (typeof str !== 'string') + error('parseOptions() internal error: wrong str'); + + // e.g. match[1] = 'Rf' for str = '-Rf' + var match = str.match(/^\-(.+)/); + if (!match) + return options; + + // e.g. chars = ['R', 'f'] + var chars = match[1].split(''); + + chars.forEach(function(c) { + if (c in map) + options[map[c]] = true; + else + error('option not recognized: '+c); + }); + + return options; +} + +// Common wrapper for all Unix-like commands +function wrap(cmd, fn, options) { + return function() { + var retValue = null; + + state.currentCmd = cmd; + state.error = null; + + try { + var args = [].slice.call(arguments, 0); + + if (options && options.notUnix) { + retValue = fn.apply(this, args); + } else { + if (args.length === 0 || typeof args[0] !== 'string' || args[0][0] !== '-') + args.unshift(''); // only add dummy option if '-option' not already present + retValue = fn.apply(this, args); + } + } catch (e) { + if (!state.error) { + // If state.error hasn't been set it's an error thrown by Node, not us - probably a bug... + console.log('shell.js: internal error'); + console.log(e.stack || e); + process.exit(1); + } + if (config.fatal) + throw e; + } + + state.currentCmd = 'shell.js'; + return retValue; + }; +} // wrap + +// Buffered file copy, synchronous +// (Using readFileSync() + writeFileSync() could easily cause a memory overflow +// with large files) +function copyFileSync(srcFile, destFile) { + if (!fs.existsSync(srcFile)) + error('copyFileSync: no such file or directory: ' + srcFile); + + var BUF_LENGTH = 64*1024, + buf = new Buffer(BUF_LENGTH), + bytesRead = BUF_LENGTH, + pos = 0, + fdr = null, + fdw = null; + + try { + fdr = fs.openSync(srcFile, 'r'); + } catch(e) { + error('copyFileSync: could not read src file ('+srcFile+')'); + } + + try { + fdw = fs.openSync(destFile, 'w'); + } catch(e) { + error('copyFileSync: could not write to dest file (code='+e.code+'):'+destFile); + } + + while (bytesRead === BUF_LENGTH) { + bytesRead = fs.readSync(fdr, buf, 0, BUF_LENGTH, pos); + fs.writeSync(fdw, buf, 0, bytesRead); + pos += bytesRead; + } + + fs.closeSync(fdr); + fs.closeSync(fdw); +} + +// Recursively copies 'sourceDir' into 'destDir' +// Adapted from https://github.com/ryanmcgrath/wrench-js +// +// Copyright (c) 2010 Ryan McGrath +// Copyright (c) 2012 Artur Adib +// +// Licensed under the MIT License +// http://www.opensource.org/licenses/mit-license.php +function cpdirSyncRecursive(sourceDir, destDir, opts) { + if (!opts) opts = {}; + + /* Create the directory where all our junk is moving to; read the mode of the source directory and mirror it */ + var checkDir = fs.statSync(sourceDir); + try { + fs.mkdirSync(destDir, checkDir.mode); + } catch (e) { + //if the directory already exists, that's okay + if (e.code !== 'EEXIST') throw e; + } + + var files = fs.readdirSync(sourceDir); + + for(var i = 0; i < files.length; i++) { + var currFile = fs.lstatSync(sourceDir + "/" + files[i]); + + if (currFile.isDirectory()) { + /* recursion this thing right on back. */ + cpdirSyncRecursive(sourceDir + "/" + files[i], destDir + "/" + files[i], opts); + } else if (currFile.isSymbolicLink()) { + var symlinkFull = fs.readlinkSync(sourceDir + "/" + files[i]); + fs.symlinkSync(symlinkFull, destDir + "/" + files[i]); + } else { + /* At this point, we've hit a file actually worth copying... so copy it on over. */ + if (fs.existsSync(destDir + "/" + files[i]) && !opts.force) { + log('skipping existing file: ' + files[i]); + } else { + copyFileSync(sourceDir + "/" + files[i], destDir + "/" + files[i]); + } + } + + } // for files +} // cpdirSyncRecursive + +// Recursively removes 'dir' +// Adapted from https://github.com/ryanmcgrath/wrench-js +// +// Copyright (c) 2010 Ryan McGrath +// Copyright (c) 2012 Artur Adib +// +// Licensed under the MIT License +// http://www.opensource.org/licenses/mit-license.php +function rmdirSyncRecursive(dir, force) { + var files; + + files = fs.readdirSync(dir); + + // Loop through and delete everything in the sub-tree after checking it + for(var i = 0; i < files.length; i++) { + var file = dir + "/" + files[i], + currFile = fs.lstatSync(file); + + if(currFile.isDirectory()) { // Recursive function back to the beginning + rmdirSyncRecursive(file, force); + } + + else if(currFile.isSymbolicLink()) { // Unlink symlinks + if (force || isWriteable(file)) { + try { + _unlinkSync(file); + } catch (e) { + error('could not remove file (code '+e.code+'): ' + file, true); + } + } + } + + else // Assume it's a file - perhaps a try/catch belongs here? + if (force || isWriteable(file)) { + try { + _unlinkSync(file); + } catch (e) { + error('could not remove file (code '+e.code+'): ' + file, true); + } + } + } + + // Now that we know everything in the sub-tree has been deleted, we can delete the main directory. + // Huzzah for the shopkeep. + + var result; + try { + result = fs.rmdirSync(dir); + } catch(e) { + error('could not remove directory (code '+e.code+'): ' + dir, true); + } + + return result; +} // rmdirSyncRecursive + +// Recursively creates 'dir' +function mkdirSyncRecursive(dir) { + var baseDir = path.dirname(dir); + + // Base dir exists, no recursion necessary + if (fs.existsSync(baseDir)) { + fs.mkdirSync(dir, parseInt('0777', 8)); + return; + } + + // Base dir does not exist, go recursive + mkdirSyncRecursive(baseDir); + + // Base dir created, can create dir + fs.mkdirSync(dir, parseInt('0777', 8)); +} + +// e.g. 'shelljs_a5f185d0443ca...' +function randomFileName() { + function randomHash(count) { + if (count === 1) + return parseInt(16*Math.random(), 10).toString(16); + else { + var hash = ''; + for (var i=0; i&1'; // works on both win/unix + + var script = + "var child = require('child_process')," + + " fs = require('fs');" + + "child.exec('"+escape(cmd)+"', {env: process.env, maxBuffer: 20*1024*1024}, function(err) {" + + " fs.writeFileSync('"+escape(codeFile)+"', err ? err.code.toString() : '0');" + + "});"; + + if (fs.existsSync(scriptFile)) _unlinkSync(scriptFile); + if (fs.existsSync(stdoutFile)) _unlinkSync(stdoutFile); + if (fs.existsSync(codeFile)) _unlinkSync(codeFile); + + fs.writeFileSync(scriptFile, script); + child.exec('"'+process.execPath+'" '+scriptFile, { + env: process.env, + cwd: exports.pwd(), + maxBuffer: 20*1024*1024 + }); + + // The wait loop + // sleepFile is used as a dummy I/O op to mitigate unnecessary CPU usage + // (tried many I/O sync ops, writeFileSync() seems to be only one that is effective in reducing + // CPU usage, though apparently not so much on Windows) + while (!fs.existsSync(codeFile)) { updateStdout(); fs.writeFileSync(sleepFile, 'a'); } + while (!fs.existsSync(stdoutFile)) { updateStdout(); fs.writeFileSync(sleepFile, 'a'); } + + // At this point codeFile exists, but it's not necessarily flushed yet. + // Keep reading it until it is. + var code = parseInt('', 10); + while (isNaN(code)) { + code = parseInt(fs.readFileSync(codeFile, 'utf8'), 10); + } + + var stdout = fs.readFileSync(stdoutFile, 'utf8'); + + // No biggie if we can't erase the files now -- they're in a temp dir anyway + try { _unlinkSync(scriptFile); } catch(e) {} + try { _unlinkSync(stdoutFile); } catch(e) {} + try { _unlinkSync(codeFile); } catch(e) {} + try { _unlinkSync(sleepFile); } catch(e) {} + + // True if successful, false if not + var obj = { + code: code, + output: stdout + }; + return obj; +} // execSync() + +// Expands wildcards with matching file names. For a given array of file names 'list', returns +// another array containing all file names as per ls(list[i]). +// For example: +// expand(['file*.js']) = ['file1.js', 'file2.js', ...] +// (if the files 'file1.js', 'file2.js', etc, exist in the current dir) +function expand(list) { + var expanded = []; + list.forEach(function(listEl) { + // Wildcard present? + if (listEl.search(/\*/) > -1) { + _ls('', listEl).forEach(function(file) { + expanded.push(file); + }); + } else { + expanded.push(listEl); + } + }); + return expanded; +} + +// Cross-platform method for splitting environment PATH variables +function splitPath(p) { + if (!p) + return []; + + if (platform === 'win') + return p.split(';'); + else + return p.split(':'); +} + +// extend(target_obj, source_obj1 [, source_obj2 ...]) +// Shallow extend, e.g.: +// extend({A:1}, {b:2}, {c:3}) returns {A:1, b:2, c:3} +function extend(target) { + var sources = [].slice.call(arguments, 1); + sources.forEach(function(source) { + for (var key in source) + target[key] = source[key]; + }); + + return target; +} + +// Normalizes _unlinkSync() across platforms to match Unix behavior, i.e. +// file can be unlinked even if it's read-only, see joyent/node#3006 +function _unlinkSync(file) { + try { + fs.unlinkSync(file); + } catch(e) { + // Try to override file permission + if (e.code === 'EPERM') { + fs.chmodSync(file, '0666'); + fs.unlinkSync(file); + } else { + throw e; + } + } +} + +// Hack to determine if file has write permissions for current user +// Avoids having to check user, group, etc, but it's probably slow +function isWriteable(file) { + var writePermission = true; + try { + var __fd = fs.openSync(file, 'a'); + fs.closeSync(__fd); + } catch(e) { + writePermission = false; + } + + return writePermission; +} diff --git a/platforms/android/cordova/run b/platforms/android/cordova/run new file mode 100644 index 0000000..c3a5772 --- /dev/null +++ b/platforms/android/cordova/run @@ -0,0 +1,35 @@ +#!/usr/bin/env node + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var run = require('./lib/run'), + reqs = require('./lib/check_reqs'), + args = process.argv; + +// Support basic help commands +if (args[2] == '--help' || args[2] == '/?' || args[2] == '-h' || + args[2] == 'help' || args[2] == '-help' || args[2] == '/help') { + run.help(); +} else if(reqs.run()) { + run.run(args); + process.exit(0); +} else { + process.exit(2); +} \ No newline at end of file diff --git a/platforms/android/cordova/version b/platforms/android/cordova/version new file mode 100644 index 0000000..de1a76d --- /dev/null +++ b/platforms/android/cordova/version @@ -0,0 +1,25 @@ +#!/usr/bin/env node + +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +// Coho updates this line: +var VERSION = "3.1.0"; + +console.log(VERSION); diff --git a/platforms/android/libs/cordova-3.1.0.jar b/platforms/android/libs/cordova-3.1.0.jar new file mode 100644 index 0000000..334cc6e Binary files /dev/null and b/platforms/android/libs/cordova-3.1.0.jar differ diff --git a/platforms/android/local.properties b/platforms/android/local.properties new file mode 100644 index 0000000..6e285da --- /dev/null +++ b/platforms/android/local.properties @@ -0,0 +1,10 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must *NOT* be checked into Version Control Systems, +# as it contains information specific to your local configuration. + +# location of the SDK. This is only used by Ant +# For customization when using a Version Control System, please read the +# header note. +sdk.dir=/home/dev/sdk diff --git a/platforms/android/proguard-project.txt b/platforms/android/proguard-project.txt new file mode 100644 index 0000000..f2fe155 --- /dev/null +++ b/platforms/android/proguard-project.txt @@ -0,0 +1,20 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/platforms/android/project.properties b/platforms/android/project.properties new file mode 100644 index 0000000..a3ee5ab --- /dev/null +++ b/platforms/android/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-17 diff --git a/platforms/android/res/drawable-hdpi/icon.png b/platforms/android/res/drawable-hdpi/icon.png new file mode 100644 index 0000000..4d27634 Binary files /dev/null and b/platforms/android/res/drawable-hdpi/icon.png differ diff --git a/platforms/android/res/drawable-ldpi/icon.png b/platforms/android/res/drawable-ldpi/icon.png new file mode 100644 index 0000000..cd5032a Binary files /dev/null and b/platforms/android/res/drawable-ldpi/icon.png differ diff --git a/platforms/android/res/drawable-mdpi/icon.png b/platforms/android/res/drawable-mdpi/icon.png new file mode 100644 index 0000000..e79c606 Binary files /dev/null and b/platforms/android/res/drawable-mdpi/icon.png differ diff --git a/platforms/android/res/drawable-xhdpi/icon.png b/platforms/android/res/drawable-xhdpi/icon.png new file mode 100644 index 0000000..ec7ffbf Binary files /dev/null and b/platforms/android/res/drawable-xhdpi/icon.png differ diff --git a/platforms/android/res/drawable/icon.png b/platforms/android/res/drawable/icon.png new file mode 100644 index 0000000..ec7ffbf Binary files /dev/null and b/platforms/android/res/drawable/icon.png differ diff --git a/platforms/android/res/values/strings.xml b/platforms/android/res/values/strings.xml new file mode 100644 index 0000000..c5464d5 --- /dev/null +++ b/platforms/android/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Flex-Survey + diff --git a/platforms/android/res/xml/config.xml b/platforms/android/res/xml/config.xml new file mode 100644 index 0000000..898b90c --- /dev/null +++ b/platforms/android/res/xml/config.xml @@ -0,0 +1,38 @@ + + + Hello Cordova + + A sample Apache Cordova application that responds to the deviceready event. + + + Apache Cordova Team + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/platforms/android/src/com/accenture/flexsurvey/FlexSurvey.java b/platforms/android/src/com/accenture/flexsurvey/FlexSurvey.java new file mode 100644 index 0000000..f6707e5 --- /dev/null +++ b/platforms/android/src/com/accenture/flexsurvey/FlexSurvey.java @@ -0,0 +1,37 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +package com.accenture.flexsurvey; + +import android.os.Bundle; +import org.apache.cordova.*; + +public class FlexSurvey extends CordovaActivity +{ + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + super.init(); + // Set by in config.xml + super.loadUrl(Config.getStartUrl()); + //super.loadUrl("file:///android_asset/www/index.html") + } +} + diff --git a/platforms/android/src/org/apache/cordova/camera/CameraLauncher.java b/platforms/android/src/org/apache/cordova/camera/CameraLauncher.java new file mode 100644 index 0000000..ec8222d --- /dev/null +++ b/platforms/android/src/org/apache/cordova/camera/CameraLauncher.java @@ -0,0 +1,819 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ +package org.apache.cordova.camera; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.LOG; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; + +import android.app.Activity; +import android.content.ContentValues; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.graphics.Bitmap.CompressFormat; +import android.media.MediaScannerConnection; +import android.media.MediaScannerConnection.MediaScannerConnectionClient; +import android.net.Uri; +import android.os.Environment; +import android.provider.MediaStore; +import android.util.Base64; +import android.util.Log; + +/** + * This class launches the camera view, allows the user to take a picture, closes the camera view, + * and returns the captured image. When the camera view is closed, the screen displayed before + * the camera view was shown is redisplayed. + */ +public class CameraLauncher extends CordovaPlugin implements MediaScannerConnectionClient { + + private static final int DATA_URL = 0; // Return base64 encoded string + private static final int FILE_URI = 1; // Return file uri (content://media/external/images/media/2 for Android) + private static final int NATIVE_URI = 2; // On Android, this is the same as FILE_URI + + private static final int PHOTOLIBRARY = 0; // Choose image from picture library (same as SAVEDPHOTOALBUM for Android) + private static final int CAMERA = 1; // Take picture from camera + private static final int SAVEDPHOTOALBUM = 2; // Choose image from picture library (same as PHOTOLIBRARY for Android) + + private static final int PICTURE = 0; // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType + private static final int VIDEO = 1; // allow selection of video only, ONLY RETURNS URL + private static final int ALLMEDIA = 2; // allow selection from all media types + + private static final int JPEG = 0; // Take a picture of type JPEG + private static final int PNG = 1; // Take a picture of type PNG + private static final String GET_PICTURE = "Get Picture"; + private static final String GET_VIDEO = "Get Video"; + private static final String GET_All = "Get All"; + + private static final String LOG_TAG = "CameraLauncher"; + + private int mQuality; // Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality) + private int targetWidth; // desired width of the image + private int targetHeight; // desired height of the image + private Uri imageUri; // Uri of captured image + private int encodingType; // Type of encoding to use + private int mediaType; // What type of media to retrieve + private boolean saveToPhotoAlbum; // Should the picture be saved to the device's photo album + private boolean correctOrientation; // Should the pictures orientation be corrected + //private boolean allowEdit; // Should we allow the user to crop the image. UNUSED. + + public CallbackContext callbackContext; + private int numPics; + + private MediaScannerConnection conn; // Used to update gallery app with newly-written files + private Uri scanMe; // Uri of image to be added to content store + + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArry of arguments for the plugin. + * @param callbackContext The callback id used when calling back into JavaScript. + * @return A PluginResult object with a status and message. + */ + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { + this.callbackContext = callbackContext; + + if (action.equals("takePicture")) { + int srcType = CAMERA; + int destType = FILE_URI; + this.saveToPhotoAlbum = false; + this.targetHeight = 0; + this.targetWidth = 0; + this.encodingType = JPEG; + this.mediaType = PICTURE; + this.mQuality = 80; + + this.mQuality = args.getInt(0); + destType = args.getInt(1); + srcType = args.getInt(2); + this.targetWidth = args.getInt(3); + this.targetHeight = args.getInt(4); + this.encodingType = args.getInt(5); + this.mediaType = args.getInt(6); + //this.allowEdit = args.getBoolean(7); // This field is unused. + this.correctOrientation = args.getBoolean(8); + this.saveToPhotoAlbum = args.getBoolean(9); + + // If the user specifies a 0 or smaller width/height + // make it -1 so later comparisons succeed + if (this.targetWidth < 1) { + this.targetWidth = -1; + } + if (this.targetHeight < 1) { + this.targetHeight = -1; + } + + try { + if (srcType == CAMERA) { + this.takePicture(destType, encodingType); + } + else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) { + this.getImage(srcType, destType); + } + } + catch (IllegalArgumentException e) + { + callbackContext.error("Illegal Argument Exception"); + PluginResult r = new PluginResult(PluginResult.Status.ERROR); + callbackContext.sendPluginResult(r); + return true; + } + + PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT); + r.setKeepCallback(true); + callbackContext.sendPluginResult(r); + + return true; + } + return false; + } + + //-------------------------------------------------------------------------- + // LOCAL METHODS + //-------------------------------------------------------------------------- + + private String getTempDirectoryPath() { + File cache = null; + + // SD Card Mounted + if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + cache = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + + "/Android/data/" + cordova.getActivity().getPackageName() + "/cache/"); + } + // Use internal storage + else { + cache = cordova.getActivity().getCacheDir(); + } + + // Create the cache directory if it doesn't exist + cache.mkdirs(); + return cache.getAbsolutePath(); + } + + /** + * Take a picture with the camera. + * When an image is captured or the camera view is cancelled, the result is returned + * in CordovaActivity.onActivityResult, which forwards the result to this.onActivityResult. + * + * The image can either be returned as a base64 string or a URI that points to the file. + * To display base64 string in an img tag, set the source to: + * img.src="data:image/jpeg;base64,"+result; + * or to display URI in an img tag + * img.src=result; + * + * @param quality Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality) + * @param returnType Set the type of image to return. + */ + public void takePicture(int returnType, int encodingType) { + // Save the number of images currently on disk for later + this.numPics = queryImgDB(whichContentStore()).getCount(); + + // Display camera + Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); + + // Specify file so that large image is captured and returned + File photo = createCaptureFile(encodingType); + intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo)); + this.imageUri = Uri.fromFile(photo); + + if (this.cordova != null) { + this.cordova.startActivityForResult((CordovaPlugin) this, intent, (CAMERA + 1) * 16 + returnType + 1); + } +// else +// LOG.d(LOG_TAG, "ERROR: You must use the CordovaInterface for this to work correctly. Please implement it in your activity"); + } + + /** + * Create a file in the applications temporary directory based upon the supplied encoding. + * + * @param encodingType of the image to be taken + * @return a File object pointing to the temporary picture + */ + private File createCaptureFile(int encodingType) { + File photo = null; + if (encodingType == JPEG) { + photo = new File(getTempDirectoryPath(), ".Pic.jpg"); + } else if (encodingType == PNG) { + photo = new File(getTempDirectoryPath(), ".Pic.png"); + } else { + throw new IllegalArgumentException("Invalid Encoding Type: " + encodingType); + } + return photo; + } + + /** + * Get image from photo library. + * + * @param quality Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality) + * @param srcType The album to get image from. + * @param returnType Set the type of image to return. + */ + // TODO: Images selected from SDCARD don't display correctly, but from CAMERA ALBUM do! + public void getImage(int srcType, int returnType) { + Intent intent = new Intent(); + String title = GET_PICTURE; + if (this.mediaType == PICTURE) { + intent.setType("image/*"); + } + else if (this.mediaType == VIDEO) { + intent.setType("video/*"); + title = GET_VIDEO; + } + else if (this.mediaType == ALLMEDIA) { + // I wanted to make the type 'image/*, video/*' but this does not work on all versions + // of android so I had to go with the wildcard search. + intent.setType("*/*"); + title = GET_All; + } + + intent.setAction(Intent.ACTION_GET_CONTENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + if (this.cordova != null) { + this.cordova.startActivityForResult((CordovaPlugin) this, Intent.createChooser(intent, + new String(title)), (srcType + 1) * 16 + returnType + 1); + } + } + + /** + * Called when the camera view exits. + * + * @param requestCode The request code originally supplied to startActivityForResult(), + * allowing you to identify who this result came from. + * @param resultCode The integer result code returned by the child activity through its setResult(). + * @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras"). + */ + public void onActivityResult(int requestCode, int resultCode, Intent intent) { + + // Get src and dest types from request code + int srcType = (requestCode / 16) - 1; + int destType = (requestCode % 16) - 1; + int rotate = 0; + + // If CAMERA + if (srcType == CAMERA) { + // If image available + if (resultCode == Activity.RESULT_OK) { + try { + // Create an ExifHelper to save the exif data that is lost during compression + ExifHelper exif = new ExifHelper(); + try { + if (this.encodingType == JPEG) { + exif.createInFile(getTempDirectoryPath() + "/.Pic.jpg"); + exif.readExifData(); + rotate = exif.getOrientation(); + } + } catch (IOException e) { + e.printStackTrace(); + } + + Bitmap bitmap = null; + Uri uri = null; + + // If sending base64 image back + if (destType == DATA_URL) { + bitmap = getScaledBitmap(FileHelper.stripFileProtocol(imageUri.toString())); + if (bitmap == null) { + // Try to get the bitmap from intent. + bitmap = (Bitmap)intent.getExtras().get("data"); + } + + // Double-check the bitmap. + if (bitmap == null) { + Log.d(LOG_TAG, "I either have a null image path or bitmap"); + this.failPicture("Unable to create bitmap!"); + return; + } + + if (rotate != 0 && this.correctOrientation) { + bitmap = getRotatedBitmap(rotate, bitmap, exif); + } + + this.processPicture(bitmap); + checkForDuplicateImage(DATA_URL); + } + + // If sending filename back + else if (destType == FILE_URI || destType == NATIVE_URI) { + if (this.saveToPhotoAlbum) { + Uri inputUri = getUriFromMediaStore(); + //Just because we have a media URI doesn't mean we have a real file, we need to make it + uri = Uri.fromFile(new File(FileHelper.getRealPath(inputUri, this.cordova))); + } else { + uri = Uri.fromFile(new File(getTempDirectoryPath(), System.currentTimeMillis() + ".jpg")); + } + + if (uri == null) { + this.failPicture("Error capturing image - no media storage found."); + } + + // If all this is true we shouldn't compress the image. + if (this.targetHeight == -1 && this.targetWidth == -1 && this.mQuality == 100 && + !this.correctOrientation) { + writeUncompressedImage(uri); + + this.callbackContext.success(uri.toString()); + } else { + bitmap = getScaledBitmap(FileHelper.stripFileProtocol(imageUri.toString())); + + if (rotate != 0 && this.correctOrientation) { + bitmap = getRotatedBitmap(rotate, bitmap, exif); + } + + // Add compressed version of captured image to returned media store Uri + OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri); + bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os); + os.close(); + + // Restore exif data to file + if (this.encodingType == JPEG) { + String exifPath; + if (this.saveToPhotoAlbum) { + exifPath = FileHelper.getRealPath(uri, this.cordova); + } else { + exifPath = uri.getPath(); + } + exif.createOutFile(exifPath); + exif.writeExifData(); + } + + } + // Send Uri back to JavaScript for viewing image + this.callbackContext.success(uri.toString()); + } + + this.cleanup(FILE_URI, this.imageUri, uri, bitmap); + bitmap = null; + + } catch (IOException e) { + e.printStackTrace(); + this.failPicture("Error capturing image."); + } + } + + // If cancelled + else if (resultCode == Activity.RESULT_CANCELED) { + this.failPicture("Camera cancelled."); + } + + // If something else + else { + this.failPicture("Did not complete!"); + } + } + + // If retrieving photo from library + else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) { + if (resultCode == Activity.RESULT_OK) { + Uri uri = intent.getData(); + + // If you ask for video or all media type you will automatically get back a file URI + // and there will be no attempt to resize any returned data + if (this.mediaType != PICTURE) { + this.callbackContext.success(uri.toString()); + } + else { + // This is a special case to just return the path as no scaling, + // rotating, nor compressing needs to be done + if (this.targetHeight == -1 && this.targetWidth == -1 && + (destType == FILE_URI || destType == NATIVE_URI) && !this.correctOrientation) { + this.callbackContext.success(uri.toString()); + } else { + String uriString = uri.toString(); + // Get the path to the image. Makes loading so much easier. + String mimeType = FileHelper.getMimeType(uriString, this.cordova); + // If we don't have a valid image so quit. + if (!("image/jpeg".equalsIgnoreCase(mimeType) || "image/png".equalsIgnoreCase(mimeType))) { + Log.d(LOG_TAG, "I either have a null image path or bitmap"); + this.failPicture("Unable to retrieve path to picture!"); + return; + } + Bitmap bitmap = null; + try { + bitmap = getScaledBitmap(uriString); + } catch (IOException e) { + e.printStackTrace(); + } + if (bitmap == null) { + Log.d(LOG_TAG, "I either have a null image path or bitmap"); + this.failPicture("Unable to create bitmap!"); + return; + } + + if (this.correctOrientation) { + rotate = getImageOrientation(uri); + if (rotate != 0) { + Matrix matrix = new Matrix(); + matrix.setRotate(rotate); + bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); + } + } + + // If sending base64 image back + if (destType == DATA_URL) { + this.processPicture(bitmap); + } + + // If sending filename back + else if (destType == FILE_URI || destType == NATIVE_URI) { + // Do we need to scale the returned file + if (this.targetHeight > 0 && this.targetWidth > 0) { + try { + // Create an ExifHelper to save the exif data that is lost during compression + String resizePath = getTempDirectoryPath() + "/resize.jpg"; + // Some content: URIs do not map to file paths (e.g. picasa). + String realPath = FileHelper.getRealPath(uri, this.cordova); + ExifHelper exif = new ExifHelper(); + if (realPath != null && this.encodingType == JPEG) { + try { + exif.createInFile(realPath); + exif.readExifData(); + rotate = exif.getOrientation(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + OutputStream os = new FileOutputStream(resizePath); + bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os); + os.close(); + + // Restore exif data to file + if (realPath != null && this.encodingType == JPEG) { + exif.createOutFile(resizePath); + exif.writeExifData(); + } + + // The resized image is cached by the app in order to get around this and not have to delete you + // application cache I'm adding the current system time to the end of the file url. + this.callbackContext.success("file://" + resizePath + "?" + System.currentTimeMillis()); + } catch (Exception e) { + e.printStackTrace(); + this.failPicture("Error retrieving image."); + } + } + else { + this.callbackContext.success(uri.toString()); + } + } + if (bitmap != null) { + bitmap.recycle(); + bitmap = null; + } + System.gc(); + } + } + } + else if (resultCode == Activity.RESULT_CANCELED) { + this.failPicture("Selection cancelled."); + } + else { + this.failPicture("Selection did not complete!"); + } + } + } + + private int getImageOrientation(Uri uri) { + String[] cols = { MediaStore.Images.Media.ORIENTATION }; + Cursor cursor = cordova.getActivity().getContentResolver().query(uri, + cols, null, null, null); + int rotate = 0; + if (cursor != null) { + cursor.moveToPosition(0); + rotate = cursor.getInt(0); + cursor.close(); + } + return rotate; + } + + /** + * Figure out if the bitmap should be rotated. For instance if the picture was taken in + * portrait mode + * + * @param rotate + * @param bitmap + * @return rotated bitmap + */ + private Bitmap getRotatedBitmap(int rotate, Bitmap bitmap, ExifHelper exif) { + Matrix matrix = new Matrix(); + if (rotate == 180) { + matrix.setRotate(rotate); + } else { + matrix.setRotate(rotate, (float) bitmap.getWidth() / 2, (float) bitmap.getHeight() / 2); + } + bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); + exif.resetOrientation(); + return bitmap; + } + + /** + * In the special case where the default width, height and quality are unchanged + * we just write the file out to disk saving the expensive Bitmap.compress function. + * + * @param uri + * @throws FileNotFoundException + * @throws IOException + */ + private void writeUncompressedImage(Uri uri) throws FileNotFoundException, + IOException { + FileInputStream fis = new FileInputStream(FileHelper.stripFileProtocol(imageUri.toString())); + OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri); + byte[] buffer = new byte[4096]; + int len; + while ((len = fis.read(buffer)) != -1) { + os.write(buffer, 0, len); + } + os.flush(); + os.close(); + fis.close(); + } + + /** + * Create entry in media store for image + * + * @return uri + */ + private Uri getUriFromMediaStore() { + ContentValues values = new ContentValues(); + values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); + Uri uri; + try { + uri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); + } catch (UnsupportedOperationException e) { + LOG.d(LOG_TAG, "Can't write to external media storage."); + try { + uri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values); + } catch (UnsupportedOperationException ex) { + LOG.d(LOG_TAG, "Can't write to internal media storage."); + return null; + } + } + return uri; + } + + /** + * Return a scaled bitmap based on the target width and height + * + * @param imagePath + * @return + * @throws IOException + */ + private Bitmap getScaledBitmap(String imageUrl) throws IOException { + // If no new width or height were specified return the original bitmap + if (this.targetWidth <= 0 && this.targetHeight <= 0) { + return BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova)); + } + + // figure out the original width and height of the image + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova), null, options); + + //CB-2292: WTF? Why is the width null? + if(options.outWidth == 0 || options.outHeight == 0) + { + return null; + } + + // determine the correct aspect ratio + int[] widthHeight = calculateAspectRatio(options.outWidth, options.outHeight); + + // Load in the smallest bitmap possible that is closest to the size we want + options.inJustDecodeBounds = false; + options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, this.targetWidth, this.targetHeight); + Bitmap unscaledBitmap = BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova), null, options); + if (unscaledBitmap == null) { + return null; + } + + return Bitmap.createScaledBitmap(unscaledBitmap, widthHeight[0], widthHeight[1], true); + } + + /** + * Maintain the aspect ratio so the resulting image does not look smooshed + * + * @param origWidth + * @param origHeight + * @return + */ + public int[] calculateAspectRatio(int origWidth, int origHeight) { + int newWidth = this.targetWidth; + int newHeight = this.targetHeight; + + // If no new width or height were specified return the original bitmap + if (newWidth <= 0 && newHeight <= 0) { + newWidth = origWidth; + newHeight = origHeight; + } + // Only the width was specified + else if (newWidth > 0 && newHeight <= 0) { + newHeight = (newWidth * origHeight) / origWidth; + } + // only the height was specified + else if (newWidth <= 0 && newHeight > 0) { + newWidth = (newHeight * origWidth) / origHeight; + } + // If the user specified both a positive width and height + // (potentially different aspect ratio) then the width or height is + // scaled so that the image fits while maintaining aspect ratio. + // Alternatively, the specified width and height could have been + // kept and Bitmap.SCALE_TO_FIT specified when scaling, but this + // would result in whitespace in the new image. + else { + double newRatio = newWidth / (double) newHeight; + double origRatio = origWidth / (double) origHeight; + + if (origRatio > newRatio) { + newHeight = (newWidth * origHeight) / origWidth; + } else if (origRatio < newRatio) { + newWidth = (newHeight * origWidth) / origHeight; + } + } + + int[] retval = new int[2]; + retval[0] = newWidth; + retval[1] = newHeight; + return retval; + } + + /** + * Figure out what ratio we can load our image into memory at while still being bigger than + * our desired width and height + * + * @param srcWidth + * @param srcHeight + * @param dstWidth + * @param dstHeight + * @return + */ + public static int calculateSampleSize(int srcWidth, int srcHeight, int dstWidth, int dstHeight) { + final float srcAspect = (float)srcWidth / (float)srcHeight; + final float dstAspect = (float)dstWidth / (float)dstHeight; + + if (srcAspect > dstAspect) { + return srcWidth / dstWidth; + } else { + return srcHeight / dstHeight; + } + } + + /** + * Creates a cursor that can be used to determine how many images we have. + * + * @return a cursor + */ + private Cursor queryImgDB(Uri contentStore) { + return this.cordova.getActivity().getContentResolver().query( + contentStore, + new String[] { MediaStore.Images.Media._ID }, + null, + null, + null); + } + + /** + * Cleans up after picture taking. Checking for duplicates and that kind of stuff. + * @param newImage + */ + private void cleanup(int imageType, Uri oldImage, Uri newImage, Bitmap bitmap) { + if (bitmap != null) { + bitmap.recycle(); + } + + // Clean up initial camera-written image file. + (new File(FileHelper.stripFileProtocol(oldImage.toString()))).delete(); + + checkForDuplicateImage(imageType); + // Scan for the gallery to update pic refs in gallery + if (this.saveToPhotoAlbum && newImage != null) { + this.scanForGallery(newImage); + } + + System.gc(); + } + + /** + * Used to find out if we are in a situation where the Camera Intent adds to images + * to the content store. If we are using a FILE_URI and the number of images in the DB + * increases by 2 we have a duplicate, when using a DATA_URL the number is 1. + * + * @param type FILE_URI or DATA_URL + */ + private void checkForDuplicateImage(int type) { + int diff = 1; + Uri contentStore = whichContentStore(); + Cursor cursor = queryImgDB(contentStore); + int currentNumOfImages = cursor.getCount(); + + if (type == FILE_URI && this.saveToPhotoAlbum) { + diff = 2; + } + + // delete the duplicate file if the difference is 2 for file URI or 1 for Data URL + if ((currentNumOfImages - numPics) == diff) { + cursor.moveToLast(); + int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID))); + if (diff == 2) { + id--; + } + Uri uri = Uri.parse(contentStore + "/" + id); + this.cordova.getActivity().getContentResolver().delete(uri, null, null); + cursor.close(); + } + } + + /** + * Determine if we are storing the images in internal or external storage + * @return Uri + */ + private Uri whichContentStore() { + if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + return android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + } else { + return android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI; + } + } + + /** + * Compress bitmap using jpeg, convert to Base64 encoded string, and return to JavaScript. + * + * @param bitmap + */ + public void processPicture(Bitmap bitmap) { + ByteArrayOutputStream jpeg_data = new ByteArrayOutputStream(); + try { + if (bitmap.compress(CompressFormat.JPEG, mQuality, jpeg_data)) { + byte[] code = jpeg_data.toByteArray(); + byte[] output = Base64.encode(code, Base64.NO_WRAP); + String js_out = new String(output); + this.callbackContext.success(js_out); + js_out = null; + output = null; + code = null; + } + } catch (Exception e) { + this.failPicture("Error compressing image."); + } + jpeg_data = null; + } + + /** + * Send error message to JavaScript. + * + * @param err + */ + public void failPicture(String err) { + this.callbackContext.error(err); + } + + private void scanForGallery(Uri newImage) { + this.scanMe = newImage; + if(this.conn != null) { + this.conn.disconnect(); + } + this.conn = new MediaScannerConnection(this.cordova.getActivity().getApplicationContext(), this); + conn.connect(); + } + + public void onMediaScannerConnected() { + try{ + this.conn.scanFile(this.scanMe.toString(), "image/*"); + } catch (java.lang.IllegalStateException e){ + LOG.e(LOG_TAG, "Can't scan file in MediaScanner after taking picture"); + } + + } + + public void onScanCompleted(String path, Uri uri) { + this.conn.disconnect(); + } +} diff --git a/platforms/android/src/org/apache/cordova/camera/ExifHelper.java b/platforms/android/src/org/apache/cordova/camera/ExifHelper.java new file mode 100644 index 0000000..5160a2f --- /dev/null +++ b/platforms/android/src/org/apache/cordova/camera/ExifHelper.java @@ -0,0 +1,185 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ +package org.apache.cordova.camera; + +import java.io.IOException; + +import android.media.ExifInterface; + +public class ExifHelper { + private String aperture = null; + private String datetime = null; + private String exposureTime = null; + private String flash = null; + private String focalLength = null; + private String gpsAltitude = null; + private String gpsAltitudeRef = null; + private String gpsDateStamp = null; + private String gpsLatitude = null; + private String gpsLatitudeRef = null; + private String gpsLongitude = null; + private String gpsLongitudeRef = null; + private String gpsProcessingMethod = null; + private String gpsTimestamp = null; + private String iso = null; + private String make = null; + private String model = null; + private String orientation = null; + private String whiteBalance = null; + + private ExifInterface inFile = null; + private ExifInterface outFile = null; + + /** + * The file before it is compressed + * + * @param filePath + * @throws IOException + */ + public void createInFile(String filePath) throws IOException { + this.inFile = new ExifInterface(filePath); + } + + /** + * The file after it has been compressed + * + * @param filePath + * @throws IOException + */ + public void createOutFile(String filePath) throws IOException { + this.outFile = new ExifInterface(filePath); + } + + /** + * Reads all the EXIF data from the input file. + */ + public void readExifData() { + this.aperture = inFile.getAttribute(ExifInterface.TAG_APERTURE); + this.datetime = inFile.getAttribute(ExifInterface.TAG_DATETIME); + this.exposureTime = inFile.getAttribute(ExifInterface.TAG_EXPOSURE_TIME); + this.flash = inFile.getAttribute(ExifInterface.TAG_FLASH); + this.focalLength = inFile.getAttribute(ExifInterface.TAG_FOCAL_LENGTH); + this.gpsAltitude = inFile.getAttribute(ExifInterface.TAG_GPS_ALTITUDE); + this.gpsAltitudeRef = inFile.getAttribute(ExifInterface.TAG_GPS_ALTITUDE_REF); + this.gpsDateStamp = inFile.getAttribute(ExifInterface.TAG_GPS_DATESTAMP); + this.gpsLatitude = inFile.getAttribute(ExifInterface.TAG_GPS_LATITUDE); + this.gpsLatitudeRef = inFile.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF); + this.gpsLongitude = inFile.getAttribute(ExifInterface.TAG_GPS_LONGITUDE); + this.gpsLongitudeRef = inFile.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF); + this.gpsProcessingMethod = inFile.getAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD); + this.gpsTimestamp = inFile.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP); + this.iso = inFile.getAttribute(ExifInterface.TAG_ISO); + this.make = inFile.getAttribute(ExifInterface.TAG_MAKE); + this.model = inFile.getAttribute(ExifInterface.TAG_MODEL); + this.orientation = inFile.getAttribute(ExifInterface.TAG_ORIENTATION); + this.whiteBalance = inFile.getAttribute(ExifInterface.TAG_WHITE_BALANCE); + } + + /** + * Writes the previously stored EXIF data to the output file. + * + * @throws IOException + */ + public void writeExifData() throws IOException { + // Don't try to write to a null file + if (this.outFile == null) { + return; + } + + if (this.aperture != null) { + this.outFile.setAttribute(ExifInterface.TAG_APERTURE, this.aperture); + } + if (this.datetime != null) { + this.outFile.setAttribute(ExifInterface.TAG_DATETIME, this.datetime); + } + if (this.exposureTime != null) { + this.outFile.setAttribute(ExifInterface.TAG_EXPOSURE_TIME, this.exposureTime); + } + if (this.flash != null) { + this.outFile.setAttribute(ExifInterface.TAG_FLASH, this.flash); + } + if (this.focalLength != null) { + this.outFile.setAttribute(ExifInterface.TAG_FOCAL_LENGTH, this.focalLength); + } + if (this.gpsAltitude != null) { + this.outFile.setAttribute(ExifInterface.TAG_GPS_ALTITUDE, this.gpsAltitude); + } + if (this.gpsAltitudeRef != null) { + this.outFile.setAttribute(ExifInterface.TAG_GPS_ALTITUDE_REF, this.gpsAltitudeRef); + } + if (this.gpsDateStamp != null) { + this.outFile.setAttribute(ExifInterface.TAG_GPS_DATESTAMP, this.gpsDateStamp); + } + if (this.gpsLatitude != null) { + this.outFile.setAttribute(ExifInterface.TAG_GPS_LATITUDE, this.gpsLatitude); + } + if (this.gpsLatitudeRef != null) { + this.outFile.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, this.gpsLatitudeRef); + } + if (this.gpsLongitude != null) { + this.outFile.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, this.gpsLongitude); + } + if (this.gpsLongitudeRef != null) { + this.outFile.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, this.gpsLongitudeRef); + } + if (this.gpsProcessingMethod != null) { + this.outFile.setAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD, this.gpsProcessingMethod); + } + if (this.gpsTimestamp != null) { + this.outFile.setAttribute(ExifInterface.TAG_GPS_TIMESTAMP, this.gpsTimestamp); + } + if (this.iso != null) { + this.outFile.setAttribute(ExifInterface.TAG_ISO, this.iso); + } + if (this.make != null) { + this.outFile.setAttribute(ExifInterface.TAG_MAKE, this.make); + } + if (this.model != null) { + this.outFile.setAttribute(ExifInterface.TAG_MODEL, this.model); + } + if (this.orientation != null) { + this.outFile.setAttribute(ExifInterface.TAG_ORIENTATION, this.orientation); + } + if (this.whiteBalance != null) { + this.outFile.setAttribute(ExifInterface.TAG_WHITE_BALANCE, this.whiteBalance); + } + + this.outFile.saveAttributes(); + } + + public int getOrientation() { + int o = Integer.parseInt(this.orientation); + + if (o == ExifInterface.ORIENTATION_NORMAL) { + return 0; + } else if (o == ExifInterface.ORIENTATION_ROTATE_90) { + return 90; + } else if (o == ExifInterface.ORIENTATION_ROTATE_180) { + return 180; + } else if (o == ExifInterface.ORIENTATION_ROTATE_270) { + return 270; + } else { + return 0; + } + } + + public void resetOrientation() { + this.orientation = "" + ExifInterface.ORIENTATION_NORMAL; + } +} diff --git a/platforms/android/src/org/apache/cordova/camera/FileHelper.java b/platforms/android/src/org/apache/cordova/camera/FileHelper.java new file mode 100644 index 0000000..24ced59 --- /dev/null +++ b/platforms/android/src/org/apache/cordova/camera/FileHelper.java @@ -0,0 +1,158 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ +package org.apache.cordova.camera; + +import android.database.Cursor; +import android.net.Uri; +import android.webkit.MimeTypeMap; + +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.LOG; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Locale; + +public class FileHelper { + private static final String LOG_TAG = "FileUtils"; + private static final String _DATA = "_data"; + + /** + * Returns the real path of the given URI string. + * If the given URI string represents a content:// URI, the real path is retrieved from the media store. + * + * @param uriString the URI string of the audio/image/video + * @param cordova the current application context + * @return the full path to the file + */ + @SuppressWarnings("deprecation") + public static String getRealPath(String uriString, CordovaInterface cordova) { + String realPath = null; + + if (uriString.startsWith("content://")) { + String[] proj = { _DATA }; + Cursor cursor = cordova.getActivity().managedQuery(Uri.parse(uriString), proj, null, null, null); + int column_index = cursor.getColumnIndexOrThrow(_DATA); + cursor.moveToFirst(); + realPath = cursor.getString(column_index); + if (realPath == null) { + LOG.e(LOG_TAG, "Could get real path for URI string %s", uriString); + } + } else if (uriString.startsWith("file://")) { + realPath = uriString.substring(7); + if (realPath.startsWith("/android_asset/")) { + LOG.e(LOG_TAG, "Cannot get real path for URI string %s because it is a file:///android_asset/ URI.", uriString); + realPath = null; + } + } else { + realPath = uriString; + } + + return realPath; + } + + /** + * Returns the real path of the given URI. + * If the given URI is a content:// URI, the real path is retrieved from the media store. + * + * @param uri the URI of the audio/image/video + * @param cordova the current application context + * @return the full path to the file + */ + public static String getRealPath(Uri uri, CordovaInterface cordova) { + return FileHelper.getRealPath(uri.toString(), cordova); + } + + /** + * Returns an input stream based on given URI string. + * + * @param uriString the URI string from which to obtain the input stream + * @param cordova the current application context + * @return an input stream into the data at the given URI or null if given an invalid URI string + * @throws IOException + */ + public static InputStream getInputStreamFromUriString(String uriString, CordovaInterface cordova) throws IOException { + if (uriString.startsWith("content")) { + Uri uri = Uri.parse(uriString); + return cordova.getActivity().getContentResolver().openInputStream(uri); + } else if (uriString.startsWith("file://")) { + int question = uriString.indexOf("?"); + if (question > -1) { + uriString = uriString.substring(0,question); + } + if (uriString.startsWith("file:///android_asset/")) { + Uri uri = Uri.parse(uriString); + String relativePath = uri.getPath().substring(15); + return cordova.getActivity().getAssets().open(relativePath); + } else { + return new FileInputStream(getRealPath(uriString, cordova)); + } + } else { + return new FileInputStream(getRealPath(uriString, cordova)); + } + } + + /** + * Removes the "file://" prefix from the given URI string, if applicable. + * If the given URI string doesn't have a "file://" prefix, it is returned unchanged. + * + * @param uriString the URI string to operate on + * @return a path without the "file://" prefix + */ + public static String stripFileProtocol(String uriString) { + if (uriString.startsWith("file://")) { + uriString = uriString.substring(7); + } + return uriString; + } + + public static String getMimeTypeForExtension(String path) { + String extension = path; + int lastDot = extension.lastIndexOf('.'); + if (lastDot != -1) { + extension = extension.substring(lastDot + 1); + } + // Convert the URI string to lower case to ensure compatibility with MimeTypeMap (see CB-2185). + extension = extension.toLowerCase(Locale.getDefault()); + if (extension.equals("3ga")) { + return "audio/3gpp"; + } + return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); + } + + /** + * Returns the mime type of the data specified by the given URI string. + * + * @param uriString the URI string of the data + * @return the mime type of the specified data + */ + public static String getMimeType(String uriString, CordovaInterface cordova) { + String mimeType = null; + + Uri uri = Uri.parse(uriString); + if (uriString.startsWith("content://")) { + mimeType = cordova.getActivity().getContentResolver().getType(uri); + } else { + mimeType = getMimeTypeForExtension(uri.getPath()); + } + + return mimeType; + } +} diff --git a/platforms/android/src/org/apache/cordova/device/Device.java b/platforms/android/src/org/apache/cordova/device/Device.java new file mode 100644 index 0000000..2ce3cc7 --- /dev/null +++ b/platforms/android/src/org/apache/cordova/device/Device.java @@ -0,0 +1,200 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ +package org.apache.cordova.device; + +import java.util.TimeZone; + +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.LOG; +import org.apache.cordova.CordovaInterface; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.provider.Settings; +import android.telephony.TelephonyManager; + +public class Device extends CordovaPlugin { + public static final String TAG = "Device"; + + public static String cordovaVersion = "dev"; // Cordova version + public static String platform = "Android"; // Device OS + public static String uuid; // Device UUID + + BroadcastReceiver telephonyReceiver = null; + + /** + * Constructor. + */ + public Device() { + } + + /** + * Sets the context of the Command. This can then be used to do things like + * get file paths associated with the Activity. + * + * @param cordova The context of the main Activity. + * @param webView The CordovaWebView Cordova is running in. + */ + public void initialize(CordovaInterface cordova, CordovaWebView webView) { + super.initialize(cordova, webView); + Device.uuid = getUuid(); + this.initTelephonyReceiver(); + } + + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArry of arguments for the plugin. + * @param callbackContext The callback id used when calling back into JavaScript. + * @return True if the action was valid, false if not. + */ + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { + if (action.equals("getDeviceInfo")) { + JSONObject r = new JSONObject(); + r.put("uuid", Device.uuid); + r.put("version", this.getOSVersion()); + r.put("platform", Device.platform); + r.put("cordova", Device.cordovaVersion); + r.put("model", this.getModel()); + callbackContext.success(r); + } + else { + return false; + } + return true; + } + + /** + * Unregister receiver. + */ + public void onDestroy() { + this.cordova.getActivity().unregisterReceiver(this.telephonyReceiver); + } + + //-------------------------------------------------------------------------- + // LOCAL METHODS + //-------------------------------------------------------------------------- + + /** + * Listen for telephony events: RINGING, OFFHOOK and IDLE + * Send these events to all plugins using + * CordovaActivity.onMessage("telephone", "ringing" | "offhook" | "idle") + */ + private void initTelephonyReceiver() { + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); + //final CordovaInterface mycordova = this.cordova; + this.telephonyReceiver = new BroadcastReceiver() { + + @Override + public void onReceive(Context context, Intent intent) { + + // If state has changed + if ((intent != null) && intent.getAction().equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { + if (intent.hasExtra(TelephonyManager.EXTRA_STATE)) { + String extraData = intent.getStringExtra(TelephonyManager.EXTRA_STATE); + if (extraData.equals(TelephonyManager.EXTRA_STATE_RINGING)) { + LOG.i(TAG, "Telephone RINGING"); + webView.postMessage("telephone", "ringing"); + } + else if (extraData.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) { + LOG.i(TAG, "Telephone OFFHOOK"); + webView.postMessage("telephone", "offhook"); + } + else if (extraData.equals(TelephonyManager.EXTRA_STATE_IDLE)) { + LOG.i(TAG, "Telephone IDLE"); + webView.postMessage("telephone", "idle"); + } + } + } + } + }; + + // Register the receiver + this.cordova.getActivity().registerReceiver(this.telephonyReceiver, intentFilter); + } + + /** + * Get the OS name. + * + * @return + */ + public String getPlatform() { + return Device.platform; + } + + /** + * Get the device's Universally Unique Identifier (UUID). + * + * @return + */ + public String getUuid() { + String uuid = Settings.Secure.getString(this.cordova.getActivity().getContentResolver(), android.provider.Settings.Secure.ANDROID_ID); + return uuid; + } + + /** + * Get the Cordova version. + * + * @return + */ + public String getCordovaVersion() { + return Device.cordovaVersion; + } + + public String getModel() { + String model = android.os.Build.MODEL; + return model; + } + + public String getProductName() { + String productname = android.os.Build.PRODUCT; + return productname; + } + + /** + * Get the OS version. + * + * @return + */ + public String getOSVersion() { + String osversion = android.os.Build.VERSION.RELEASE; + return osversion; + } + + public String getSDKVersion() { + @SuppressWarnings("deprecation") + String sdkversion = android.os.Build.VERSION.SDK; + return sdkversion; + } + + public String getTimeZoneID() { + TimeZone tz = TimeZone.getDefault(); + return (tz.getID()); + } + +} diff --git a/plugins/android.json b/plugins/android.json new file mode 100644 index 0000000..00653d9 --- /dev/null +++ b/plugins/android.json @@ -0,0 +1 @@ +{"prepare_queue":{"installed":[],"uninstalled":[]},"config_munge":{"res/xml/config.xml":{"/*":{"":1,"":1}},"AndroidManifest.xml":{"/*":{"":1}}},"installed_plugins":{"org.apache.cordova.camera":{"PACKAGE_NAME":"com.accenture.flexsurvey"},"org.apache.cordova.device":{"PACKAGE_NAME":"com.accenture.flexsurvey"},"org.apache.cordova.console":{"PACKAGE_NAME":"com.accenture.flexsurvey"}},"dependent_plugins":{}} \ No newline at end of file diff --git a/plugins/org.apache.cordova.camera/.fetch.json b/plugins/org.apache.cordova.camera/.fetch.json new file mode 100644 index 0000000..567764e --- /dev/null +++ b/plugins/org.apache.cordova.camera/.fetch.json @@ -0,0 +1 @@ +{"source":{"type":"git","url":"https://git-wip-us.apache.org/repos/asf/cordova-plugin-camera.git","subdir":"."}} \ No newline at end of file diff --git a/plugins/org.apache.cordova.camera/LICENSE b/plugins/org.apache.cordova.camera/LICENSE new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/plugins/org.apache.cordova.camera/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/plugins/org.apache.cordova.camera/README.md b/plugins/org.apache.cordova.camera/README.md new file mode 100644 index 0000000..983346d --- /dev/null +++ b/plugins/org.apache.cordova.camera/README.md @@ -0,0 +1,5 @@ +cordova-plugin-camera +========================== +To install this plugin, follow the [Command-line Interface Guide](http://cordova.apache.org/docs/en/edge/guide_cli_index.md.html#The%20Command-line%20Interface). + +If you are not using the Cordova Command-line Interface, follow [Using Plugman to Manage Plugins](http://cordova.apache.org/docs/en/edge/plugin_ref_plugman.md.html). diff --git a/plugins/org.apache.cordova.camera/RELEASENOTES.md b/plugins/org.apache.cordova.camera/RELEASENOTES.md new file mode 100644 index 0000000..75b32d1 --- /dev/null +++ b/plugins/org.apache.cordova.camera/RELEASENOTES.md @@ -0,0 +1,49 @@ + +# Release Notes + +### 0.2.1 (Sept 5, 2013) +* [CB-4656] Don't add line-breaks to base64-encoded images (Fixes type=DataURI) +* [CB-4432] copyright notice change + +### 0.2.3 (Sept 25, 2013) +* CB-4889 bumping&resetting version +* CB-4889 forgot index.html +* CB-4889 renaming core inside cameraProxy +* [Windows8] commandProxy has moved +* [Windows8] commandProxy has moved +* added Camera API for FirefoxOS +* Rename CHANGELOG.md -> RELEASENOTES.md +* [CB-4823] Fix XCode 5 camera plugin warnings +* Fix compiler warnings +* [CB-4765] Move ExifHelper.java into Camera Plugin +* [CB-4764] Remove reference to DirectoryManager from CameraLauncher +* [CB-4763] Use a copy of FileHelper.java within camera-plugin. +* [CB-4752] Incremented plugin version on dev branch. +* CB-4633: We really should close cursors. It's just the right thing to do. +* No longer causes a stack trace, but it doesn't cause the error to be called. +* CB-4889 renaming org.apache.cordova.core.camera to org.apache.cordova.camera + + ### 0.2.4 (Oct 28, 2013) +* CB-5128: added repo + issue tag to plugin.xml for camera plugin +* CB-4958 - iOS - Camera plugin should not show the status bar +* [CB-4919] updated plugin.xml for FxOS +* [CB-4915] Incremented plugin version on dev branch. \ No newline at end of file diff --git a/plugins/org.apache.cordova.camera/docs/camera.cleanup.md b/plugins/org.apache.cordova.camera/docs/camera.cleanup.md new file mode 100644 index 0000000..fbf9e13 --- /dev/null +++ b/plugins/org.apache.cordova.camera/docs/camera.cleanup.md @@ -0,0 +1,50 @@ +--- +license: Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--- + +camera.cleanup +================= + +Cleans up the image files that were taken by the camera, that were stored in a temporary storage location. + + navigator.camera.cleanup( cameraSuccess, cameraError ); + +Description +----------- + +Cleans up the image files stored in the temporary storage location, when the function `camera.getPicture` is used with `Camera.sourceType = Camera.PictureSourceType.CAMERA` and `Camera.destinationType = Camera.DestinationType.FILE_URI` + + +Supported Platforms +------------------- + +- iOS + + +Example +------------- + + navigator.camera.cleanup(onSuccess, onFail); + + function onSuccess() { + console.log("Camera cleanup success.") + } + + function onFail(message) { + alert('Failed because: ' + message); + } diff --git a/plugins/org.apache.cordova.camera/docs/camera.getPicture.md b/plugins/org.apache.cordova.camera/docs/camera.getPicture.md new file mode 100644 index 0000000..181bb87 --- /dev/null +++ b/plugins/org.apache.cordova.camera/docs/camera.getPicture.md @@ -0,0 +1,220 @@ +--- +license: Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--- + +camera.getPicture +================= + +Takes a photo using the camera or retrieves a photo from the device's album. +The image is passed to the success callback as a base64 encoded `String` or as the URI of an image file. +The method itself returns a CameraPopoverHandle object, which can be used to reposition the file selection popover. + + navigator.camera.getPicture( cameraSuccess, cameraError, [ cameraOptions ] ); + +Description +----------- + +Function `camera.getPicture` opens the device's default camera application so that the user can take a picture (if `Camera.sourceType = Camera.PictureSourceType.CAMERA`, which is the default). Once the photo is taken, the camera application closes and your application is restored. + +If `Camera.sourceType = Camera.PictureSourceType.PHOTOLIBRARY` or `Camera.PictureSourceType.SAVEDPHOTOALBUM`, then a photo chooser dialog is shown, from which a photo from the album can be selected. A `CameraPopoverHandle` object, which can be used to reposition the photo chooser dialog (eg. when the device orientation changes) is returned by `camera.getPicture`. + +The return value will be sent to the `cameraSuccess` function, in one of the following formats, depending on the `cameraOptions` you specify: + +- A `String` containing the Base64 encoded photo image. +- A `String` representing the image file location on local storage (default). + +You can do whatever you want with the encoded image or URI, for example: + +- Render the image in an `` tag _(see example below)_ +- Save the data locally (`LocalStorage`, [Lawnchair](http://brianleroux.github.com/lawnchair/), etc) +- Post the data to a remote server + +__Note:__ The image quality of pictures taken using the camera on newer devices is quite good, and images from the Photo Album will not be downscaled to a lower quality, even if a quality parameter is specified. ___Encoding such images using Base64 has caused memory issues on many newer devices. Therefore, using FILE\_URI as the 'Camera.destinationType' is highly recommended.___ + +Supported Platforms +------------------- + +- Android +- Blackberry WebWorks (OS 5.0 and higher) +- iOS +- Windows Phone 7 and 8 +- Bada 1.2 +- webOS +- Tizen +- Windows 8 + +iOS Quirks +---------- + +Including a JavaScript alert() in either of the callback functions can cause problems. Wrap the alert in a setTimeout() to allow the iOS image picker or popover to fully close before the alert is displayed: + + setTimeout(function() { + // do your thing here! + }, 0); + +Windows Phone 7 Quirks +---------------------- + +Invoking the native camera application while your device is connected +via Zune will not work, and the error callback will be triggered. + +Tizen Quirks +---------------------- + +Only 'destinationType: Camera.DestinationType.FILE_URI' and 'sourceType: Camera.PictureSourceType.PHOTOLIBRARY' are supported. + +Quick Example +------------- + +Take photo and retrieve Base64-encoded image: + + navigator.camera.getPicture(onSuccess, onFail, { quality: 50, + destinationType: Camera.DestinationType.DATA_URL + }); + + function onSuccess(imageData) { + var image = document.getElementById('myImage'); + image.src = "data:image/jpeg;base64," + imageData; + } + + function onFail(message) { + alert('Failed because: ' + message); + } + +Take photo and retrieve image file location: + + navigator.camera.getPicture(onSuccess, onFail, { quality: 50, + destinationType: Camera.DestinationType.FILE_URI }); + + function onSuccess(imageURI) { + var image = document.getElementById('myImage'); + image.src = imageURI; + } + + function onFail(message) { + alert('Failed because: ' + message); + } + + +Full Example +------------ + + + + + Capture Photo + + + + + +
+
+
+
+ + + + diff --git a/plugins/org.apache.cordova.camera/docs/camera.md b/plugins/org.apache.cordova.camera/docs/camera.md new file mode 100644 index 0000000..026ccdb --- /dev/null +++ b/plugins/org.apache.cordova.camera/docs/camera.md @@ -0,0 +1,97 @@ +--- +license: Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--- + +Camera +====== + +> The `camera` object provides access to the device's default camera application. + +Methods +------- + +- camera.getPicture +- camera.cleanup + +Permissions +----------- + +### Android + +#### app/res/xml/config.xml + + + +#### app/AndroidManifest + + + +### Bada + +#### manifest.xml + + + CAMERA + + + RECORDING + + +### BlackBerry WebWorks + +#### www/plugins.xml + + + +#### www/config.xml + + + + + use_camera + + +### iOS + +#### config.xml + + + +### webOS + + No permissions are required. + +### Windows Phone + +#### Properties/WPAppManifest.xml + + + + + + +Reference: [Application Manifest for Windows Phone](http://msdn.microsoft.com/en-us/library/ff769509%28v=vs.92%29.aspx) + +### Tizen + +#### config.xml + + + + +Reference: [Application Manifest for Tizen Web Application](https://developer.tizen.org/help/topic/org.tizen.help.gs/Creating%20a%20Project.html?path=0_1_1_3#8814682_CreatingaProject-EditingconfigxmlFeatures) diff --git a/plugins/org.apache.cordova.camera/docs/parameter/CameraPopoverHandle.md b/plugins/org.apache.cordova.camera/docs/parameter/CameraPopoverHandle.md new file mode 100644 index 0000000..94ab149 --- /dev/null +++ b/plugins/org.apache.cordova.camera/docs/parameter/CameraPopoverHandle.md @@ -0,0 +1,68 @@ +--- +license: Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--- + +CameraPopoverHandle +=================== + +A handle to the popover dialog created by `camera.getPicture`. + +Methods +------- + +- __setPosition:__ Set the position of the popover. + +Supported Platforms +------------------- + +- iOS + +setPosition +----------- + +Set the position of the popover. + +__Parameters:__ +- cameraPopoverOptions - the CameraPopoverOptions specifying the new position + +Quick Example +------------- + + var cameraPopoverOptions = new CameraPopoverOptions(300, 300, 100, 100, Camera.PopoverArrowDirection.ARROW_ANY); + cameraPopoverHandle.setPosition(cameraPopoverOptions); + +Full Example +------------ + + function onSuccess(imageData) { + // Do stuff with the image! + } + + function onFail(message) { + alert('Failed to get the picture: ' + message); + } + + var cameraPopoverHandle = navigator.camera.getPicture(onSuccess, onFail, + { destinationType: Camera.DestinationType.FILE_URI, + sourceType: Camera.PictureSourceType.PHOTOLIBRARY }); + + // Reposition the popover if the orientation changes. + window.onorientationchange = function() { + var cameraPopoverOptions = new CameraPopoverOptions(0, 0, 100, 100, 0); + cameraPopoverHandle.setPosition(cameraPopoverOptions); + } diff --git a/plugins/org.apache.cordova.camera/docs/parameter/CameraPopoverOptions.md b/plugins/org.apache.cordova.camera/docs/parameter/CameraPopoverOptions.md new file mode 100644 index 0000000..6314e8a --- /dev/null +++ b/plugins/org.apache.cordova.camera/docs/parameter/CameraPopoverOptions.md @@ -0,0 +1,71 @@ +--- +license: Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--- + +CameraPopoverOptions +==================== + +Parameters only used by iOS to specify the anchor element location and arrow direction of popover used on iPad when selecting images from the library or album. + + { x : 0, + y : 32, + width : 320, + height : 480, + arrowDir : Camera.PopoverArrowDirection.ARROW_ANY + }; + +CameraPopoverOptions +-------------------- + +- __x:__ x pixel coordinate of element on the screen to anchor popover onto. (`Number`) + +- __y:__ y pixel coordinate of element on the screen to anchor popover onto. (`Number`) + +- __width:__ width, in pixels, of the element on the screen to anchor popover onto. (`Number`) + +- __height:__ height, in pixels, of the element on the screen to anchor popover onto. (`Number`) + +- __arrowDir:__ Direction the arrow on the popover should point. Defined in Camera.PopoverArrowDirection (`Number`) + + Camera.PopoverArrowDirection = { + ARROW_UP : 1, // matches iOS UIPopoverArrowDirection constants + ARROW_DOWN : 2, + ARROW_LEFT : 4, + ARROW_RIGHT : 8, + ARROW_ANY : 15 + }; + +Note that the size of the popover may change to adjust to the direction of the arrow and orientation of the screen. Make sure to account for orientation changes when specifying the anchor element location. + +Quick Example +------------- + + var popover = new CameraPopoverOptions(300,300,100,100,Camera.PopoverArrowDirection.ARROW_ANY); + var options = { quality: 50, destinationType: Camera.DestinationType.DATA_URL,sourceType: Camera.PictureSource.SAVEDPHOTOALBUM, popoverOptions : popover }; + + navigator.camera.getPicture(onSuccess, onFail, options); + + function onSuccess(imageData) { + var image = document.getElementById('myImage'); + image.src = "data:image/jpeg;base64," + imageData; + } + + function onFail(message) { + alert('Failed because: ' + message); + } + diff --git a/plugins/org.apache.cordova.camera/docs/parameter/cameraError.md b/plugins/org.apache.cordova.camera/docs/parameter/cameraError.md new file mode 100644 index 0000000..f36840f --- /dev/null +++ b/plugins/org.apache.cordova.camera/docs/parameter/cameraError.md @@ -0,0 +1,32 @@ +--- +license: Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--- + +cameraError +=========== + +onError callback function that provides an error message. + + function(message) { + // Show a helpful message + } + +Parameters +---------- + +- __message:__ The message is provided by the device's native code. (`String`) diff --git a/plugins/org.apache.cordova.camera/docs/parameter/cameraOptions.md b/plugins/org.apache.cordova.camera/docs/parameter/cameraOptions.md new file mode 100644 index 0000000..3e5ba0f --- /dev/null +++ b/plugins/org.apache.cordova.camera/docs/parameter/cameraOptions.md @@ -0,0 +1,136 @@ +--- +license: Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--- + +cameraOptions +============= + +Optional parameters to customize the camera settings. + + { quality : 75, + destinationType : Camera.DestinationType.DATA_URL, + sourceType : Camera.PictureSourceType.CAMERA, + allowEdit : true, + encodingType: Camera.EncodingType.JPEG, + targetWidth: 100, + targetHeight: 100, + popoverOptions: CameraPopoverOptions, + saveToPhotoAlbum: false }; + +Options +------- + +- __quality:__ Quality of saved image. Range is [0, 100]. (`Number`) + +- __destinationType:__ Choose the format of the return value. Defined in navigator.camera.DestinationType (`Number`) + + Camera.DestinationType = { + DATA_URL : 0, // Return image as base64 encoded string + FILE_URI : 1, // Return image file URI + NATIVE_URI : 2 // Return image native URI (eg. assets-library:// on iOS or content:// on Android) + }; + +- __sourceType:__ Set the source of the picture. Defined in nagivator.camera.PictureSourceType (`Number`) + + Camera.PictureSourceType = { + PHOTOLIBRARY : 0, + CAMERA : 1, + SAVEDPHOTOALBUM : 2 + }; + +- __allowEdit:__ Allow simple editing of image before selection. (`Boolean`) + +- __encodingType:__ Choose the encoding of the returned image file. Defined in navigator.camera.EncodingType (`Number`) + + Camera.EncodingType = { + JPEG : 0, // Return JPEG encoded image + PNG : 1 // Return PNG encoded image + }; + +- __targetWidth:__ Width in pixels to scale image. Must be used with targetHeight. Aspect ratio is maintained. (`Number`) +- __targetHeight:__ Height in pixels to scale image. Must be used with targetWidth. Aspect ratio is maintained. (`Number`) + +- __mediaType:__ Set the type of media to select from. Only works when PictureSourceType is PHOTOLIBRARY or SAVEDPHOTOALBUM. Defined in nagivator.camera.MediaType (`Number`) + + Camera.MediaType = { + PICTURE: 0, // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType + VIDEO: 1, // allow selection of video only, WILL ALWAYS RETURN FILE_URI + ALLMEDIA : 2 // allow selection from all media types +}; + +- __correctOrientation:__ Rotate the image to correct for the orientation of the device during capture. (`Boolean`) +- __saveToPhotoAlbum:__ Save the image to the photo album on the device after capture. (`Boolean`) +- __popoverOptions:__ iOS only options to specify popover location in iPad. Defined in CameraPopoverOptions. +- __cameraDirection:__ Choose the camera to use (front- or back-facing). Defined in navigator.camera.Direction (`Number`) + + Camera.Direction = { + BACK : 0, // Use the back-facing camera + FRONT : 1 // Use the front-facing camera + }; + +Android Quirks +-------------- + +- Ignores the `allowEdit` parameter. +- Camera.PictureSourceType.PHOTOLIBRARY and Camera.PictureSourceType.SAVEDPHOTOALBUM both display the same photo album. + +BlackBerry Quirks +----------------- + +- Ignores the `quality` parameter. +- Ignores the `sourceType` parameter. +- Ignores the `allowEdit` parameter. +- Application must have key injection permissions to close native Camera application after photo is taken. +- Using Large image sizes may result in inability to encode image on later model devices with high resolution cameras (e.g. Torch 9800). +- Camera.MediaType is not supported. +- Ignores the `correctOrientation` parameter. +- Ignores the `cameraDirection` parameter. + +webOS Quirks +----------- + +- Ignores the `quality` parameter. +- Ignores the `sourceType` parameter. +- Ignores the `allowEdit` parameter. +- Camera.MediaType is not supported. +- Ignores the `correctOrientation` parameter. +- Ignores the `saveToPhotoAlbum` parameter. +- Ignores the `cameraDirection` parameter. + +iOS Quirks +-------------- + +- Set `quality` below 50 to avoid memory error on some devices. +- When `destinationType.FILE_URI` is used, photos are saved in the application's temporary directory. + +Windows Phone 7 and 8 Quirks +-------------- + +- Ignores the `allowEdit` parameter. +- Ignores the `correctOrientation` parameter. +- Ignores the `cameraDirection` parameter. + +Bada 1.2 Quirks +-------------- +- options not supported +- always returns a FILE URI + +Tizen Quirks +-------------- +- options not supported +- always returns a FILE URI diff --git a/plugins/org.apache.cordova.camera/docs/parameter/cameraSuccess.md b/plugins/org.apache.cordova.camera/docs/parameter/cameraSuccess.md new file mode 100644 index 0000000..e57b296 --- /dev/null +++ b/plugins/org.apache.cordova.camera/docs/parameter/cameraSuccess.md @@ -0,0 +1,42 @@ +--- +license: Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--- + +cameraSuccess +============= + +onSuccess callback function that provides the image data. + + function(imageData) { + // Do something with the image + } + +Parameters +---------- + +- __imageData:__ Base64 encoding of the image data, OR the image file URI, depending on `cameraOptions` used. (`String`) + +Example +------- + + // Show image + // + function cameraCallback(imageData) { + var image = document.getElementById('myImage'); + image.src = "data:image/jpeg;base64," + imageData; + } diff --git a/plugins/org.apache.cordova.camera/plugin.xml b/plugins/org.apache.cordova.camera/plugin.xml new file mode 100644 index 0000000..3312571 --- /dev/null +++ b/plugins/org.apache.cordova.camera/plugin.xml @@ -0,0 +1,158 @@ + + + + Camera + Cordova Camera Plugin + Apache 2.0 + cordova,camera + https://git-wip-us.apache.org/repos/asf/cordova-plugin-camera.git + https://issues.apache.org/jira/browse/CB/component/12320645 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/org.apache.cordova.camera/src/android/CameraLauncher.java b/plugins/org.apache.cordova.camera/src/android/CameraLauncher.java new file mode 100644 index 0000000..ec8222d --- /dev/null +++ b/plugins/org.apache.cordova.camera/src/android/CameraLauncher.java @@ -0,0 +1,819 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ +package org.apache.cordova.camera; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.LOG; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; + +import android.app.Activity; +import android.content.ContentValues; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.graphics.Bitmap.CompressFormat; +import android.media.MediaScannerConnection; +import android.media.MediaScannerConnection.MediaScannerConnectionClient; +import android.net.Uri; +import android.os.Environment; +import android.provider.MediaStore; +import android.util.Base64; +import android.util.Log; + +/** + * This class launches the camera view, allows the user to take a picture, closes the camera view, + * and returns the captured image. When the camera view is closed, the screen displayed before + * the camera view was shown is redisplayed. + */ +public class CameraLauncher extends CordovaPlugin implements MediaScannerConnectionClient { + + private static final int DATA_URL = 0; // Return base64 encoded string + private static final int FILE_URI = 1; // Return file uri (content://media/external/images/media/2 for Android) + private static final int NATIVE_URI = 2; // On Android, this is the same as FILE_URI + + private static final int PHOTOLIBRARY = 0; // Choose image from picture library (same as SAVEDPHOTOALBUM for Android) + private static final int CAMERA = 1; // Take picture from camera + private static final int SAVEDPHOTOALBUM = 2; // Choose image from picture library (same as PHOTOLIBRARY for Android) + + private static final int PICTURE = 0; // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType + private static final int VIDEO = 1; // allow selection of video only, ONLY RETURNS URL + private static final int ALLMEDIA = 2; // allow selection from all media types + + private static final int JPEG = 0; // Take a picture of type JPEG + private static final int PNG = 1; // Take a picture of type PNG + private static final String GET_PICTURE = "Get Picture"; + private static final String GET_VIDEO = "Get Video"; + private static final String GET_All = "Get All"; + + private static final String LOG_TAG = "CameraLauncher"; + + private int mQuality; // Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality) + private int targetWidth; // desired width of the image + private int targetHeight; // desired height of the image + private Uri imageUri; // Uri of captured image + private int encodingType; // Type of encoding to use + private int mediaType; // What type of media to retrieve + private boolean saveToPhotoAlbum; // Should the picture be saved to the device's photo album + private boolean correctOrientation; // Should the pictures orientation be corrected + //private boolean allowEdit; // Should we allow the user to crop the image. UNUSED. + + public CallbackContext callbackContext; + private int numPics; + + private MediaScannerConnection conn; // Used to update gallery app with newly-written files + private Uri scanMe; // Uri of image to be added to content store + + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArry of arguments for the plugin. + * @param callbackContext The callback id used when calling back into JavaScript. + * @return A PluginResult object with a status and message. + */ + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { + this.callbackContext = callbackContext; + + if (action.equals("takePicture")) { + int srcType = CAMERA; + int destType = FILE_URI; + this.saveToPhotoAlbum = false; + this.targetHeight = 0; + this.targetWidth = 0; + this.encodingType = JPEG; + this.mediaType = PICTURE; + this.mQuality = 80; + + this.mQuality = args.getInt(0); + destType = args.getInt(1); + srcType = args.getInt(2); + this.targetWidth = args.getInt(3); + this.targetHeight = args.getInt(4); + this.encodingType = args.getInt(5); + this.mediaType = args.getInt(6); + //this.allowEdit = args.getBoolean(7); // This field is unused. + this.correctOrientation = args.getBoolean(8); + this.saveToPhotoAlbum = args.getBoolean(9); + + // If the user specifies a 0 or smaller width/height + // make it -1 so later comparisons succeed + if (this.targetWidth < 1) { + this.targetWidth = -1; + } + if (this.targetHeight < 1) { + this.targetHeight = -1; + } + + try { + if (srcType == CAMERA) { + this.takePicture(destType, encodingType); + } + else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) { + this.getImage(srcType, destType); + } + } + catch (IllegalArgumentException e) + { + callbackContext.error("Illegal Argument Exception"); + PluginResult r = new PluginResult(PluginResult.Status.ERROR); + callbackContext.sendPluginResult(r); + return true; + } + + PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT); + r.setKeepCallback(true); + callbackContext.sendPluginResult(r); + + return true; + } + return false; + } + + //-------------------------------------------------------------------------- + // LOCAL METHODS + //-------------------------------------------------------------------------- + + private String getTempDirectoryPath() { + File cache = null; + + // SD Card Mounted + if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + cache = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + + "/Android/data/" + cordova.getActivity().getPackageName() + "/cache/"); + } + // Use internal storage + else { + cache = cordova.getActivity().getCacheDir(); + } + + // Create the cache directory if it doesn't exist + cache.mkdirs(); + return cache.getAbsolutePath(); + } + + /** + * Take a picture with the camera. + * When an image is captured or the camera view is cancelled, the result is returned + * in CordovaActivity.onActivityResult, which forwards the result to this.onActivityResult. + * + * The image can either be returned as a base64 string or a URI that points to the file. + * To display base64 string in an img tag, set the source to: + * img.src="data:image/jpeg;base64,"+result; + * or to display URI in an img tag + * img.src=result; + * + * @param quality Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality) + * @param returnType Set the type of image to return. + */ + public void takePicture(int returnType, int encodingType) { + // Save the number of images currently on disk for later + this.numPics = queryImgDB(whichContentStore()).getCount(); + + // Display camera + Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); + + // Specify file so that large image is captured and returned + File photo = createCaptureFile(encodingType); + intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo)); + this.imageUri = Uri.fromFile(photo); + + if (this.cordova != null) { + this.cordova.startActivityForResult((CordovaPlugin) this, intent, (CAMERA + 1) * 16 + returnType + 1); + } +// else +// LOG.d(LOG_TAG, "ERROR: You must use the CordovaInterface for this to work correctly. Please implement it in your activity"); + } + + /** + * Create a file in the applications temporary directory based upon the supplied encoding. + * + * @param encodingType of the image to be taken + * @return a File object pointing to the temporary picture + */ + private File createCaptureFile(int encodingType) { + File photo = null; + if (encodingType == JPEG) { + photo = new File(getTempDirectoryPath(), ".Pic.jpg"); + } else if (encodingType == PNG) { + photo = new File(getTempDirectoryPath(), ".Pic.png"); + } else { + throw new IllegalArgumentException("Invalid Encoding Type: " + encodingType); + } + return photo; + } + + /** + * Get image from photo library. + * + * @param quality Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality) + * @param srcType The album to get image from. + * @param returnType Set the type of image to return. + */ + // TODO: Images selected from SDCARD don't display correctly, but from CAMERA ALBUM do! + public void getImage(int srcType, int returnType) { + Intent intent = new Intent(); + String title = GET_PICTURE; + if (this.mediaType == PICTURE) { + intent.setType("image/*"); + } + else if (this.mediaType == VIDEO) { + intent.setType("video/*"); + title = GET_VIDEO; + } + else if (this.mediaType == ALLMEDIA) { + // I wanted to make the type 'image/*, video/*' but this does not work on all versions + // of android so I had to go with the wildcard search. + intent.setType("*/*"); + title = GET_All; + } + + intent.setAction(Intent.ACTION_GET_CONTENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + if (this.cordova != null) { + this.cordova.startActivityForResult((CordovaPlugin) this, Intent.createChooser(intent, + new String(title)), (srcType + 1) * 16 + returnType + 1); + } + } + + /** + * Called when the camera view exits. + * + * @param requestCode The request code originally supplied to startActivityForResult(), + * allowing you to identify who this result came from. + * @param resultCode The integer result code returned by the child activity through its setResult(). + * @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras"). + */ + public void onActivityResult(int requestCode, int resultCode, Intent intent) { + + // Get src and dest types from request code + int srcType = (requestCode / 16) - 1; + int destType = (requestCode % 16) - 1; + int rotate = 0; + + // If CAMERA + if (srcType == CAMERA) { + // If image available + if (resultCode == Activity.RESULT_OK) { + try { + // Create an ExifHelper to save the exif data that is lost during compression + ExifHelper exif = new ExifHelper(); + try { + if (this.encodingType == JPEG) { + exif.createInFile(getTempDirectoryPath() + "/.Pic.jpg"); + exif.readExifData(); + rotate = exif.getOrientation(); + } + } catch (IOException e) { + e.printStackTrace(); + } + + Bitmap bitmap = null; + Uri uri = null; + + // If sending base64 image back + if (destType == DATA_URL) { + bitmap = getScaledBitmap(FileHelper.stripFileProtocol(imageUri.toString())); + if (bitmap == null) { + // Try to get the bitmap from intent. + bitmap = (Bitmap)intent.getExtras().get("data"); + } + + // Double-check the bitmap. + if (bitmap == null) { + Log.d(LOG_TAG, "I either have a null image path or bitmap"); + this.failPicture("Unable to create bitmap!"); + return; + } + + if (rotate != 0 && this.correctOrientation) { + bitmap = getRotatedBitmap(rotate, bitmap, exif); + } + + this.processPicture(bitmap); + checkForDuplicateImage(DATA_URL); + } + + // If sending filename back + else if (destType == FILE_URI || destType == NATIVE_URI) { + if (this.saveToPhotoAlbum) { + Uri inputUri = getUriFromMediaStore(); + //Just because we have a media URI doesn't mean we have a real file, we need to make it + uri = Uri.fromFile(new File(FileHelper.getRealPath(inputUri, this.cordova))); + } else { + uri = Uri.fromFile(new File(getTempDirectoryPath(), System.currentTimeMillis() + ".jpg")); + } + + if (uri == null) { + this.failPicture("Error capturing image - no media storage found."); + } + + // If all this is true we shouldn't compress the image. + if (this.targetHeight == -1 && this.targetWidth == -1 && this.mQuality == 100 && + !this.correctOrientation) { + writeUncompressedImage(uri); + + this.callbackContext.success(uri.toString()); + } else { + bitmap = getScaledBitmap(FileHelper.stripFileProtocol(imageUri.toString())); + + if (rotate != 0 && this.correctOrientation) { + bitmap = getRotatedBitmap(rotate, bitmap, exif); + } + + // Add compressed version of captured image to returned media store Uri + OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri); + bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os); + os.close(); + + // Restore exif data to file + if (this.encodingType == JPEG) { + String exifPath; + if (this.saveToPhotoAlbum) { + exifPath = FileHelper.getRealPath(uri, this.cordova); + } else { + exifPath = uri.getPath(); + } + exif.createOutFile(exifPath); + exif.writeExifData(); + } + + } + // Send Uri back to JavaScript for viewing image + this.callbackContext.success(uri.toString()); + } + + this.cleanup(FILE_URI, this.imageUri, uri, bitmap); + bitmap = null; + + } catch (IOException e) { + e.printStackTrace(); + this.failPicture("Error capturing image."); + } + } + + // If cancelled + else if (resultCode == Activity.RESULT_CANCELED) { + this.failPicture("Camera cancelled."); + } + + // If something else + else { + this.failPicture("Did not complete!"); + } + } + + // If retrieving photo from library + else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) { + if (resultCode == Activity.RESULT_OK) { + Uri uri = intent.getData(); + + // If you ask for video or all media type you will automatically get back a file URI + // and there will be no attempt to resize any returned data + if (this.mediaType != PICTURE) { + this.callbackContext.success(uri.toString()); + } + else { + // This is a special case to just return the path as no scaling, + // rotating, nor compressing needs to be done + if (this.targetHeight == -1 && this.targetWidth == -1 && + (destType == FILE_URI || destType == NATIVE_URI) && !this.correctOrientation) { + this.callbackContext.success(uri.toString()); + } else { + String uriString = uri.toString(); + // Get the path to the image. Makes loading so much easier. + String mimeType = FileHelper.getMimeType(uriString, this.cordova); + // If we don't have a valid image so quit. + if (!("image/jpeg".equalsIgnoreCase(mimeType) || "image/png".equalsIgnoreCase(mimeType))) { + Log.d(LOG_TAG, "I either have a null image path or bitmap"); + this.failPicture("Unable to retrieve path to picture!"); + return; + } + Bitmap bitmap = null; + try { + bitmap = getScaledBitmap(uriString); + } catch (IOException e) { + e.printStackTrace(); + } + if (bitmap == null) { + Log.d(LOG_TAG, "I either have a null image path or bitmap"); + this.failPicture("Unable to create bitmap!"); + return; + } + + if (this.correctOrientation) { + rotate = getImageOrientation(uri); + if (rotate != 0) { + Matrix matrix = new Matrix(); + matrix.setRotate(rotate); + bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); + } + } + + // If sending base64 image back + if (destType == DATA_URL) { + this.processPicture(bitmap); + } + + // If sending filename back + else if (destType == FILE_URI || destType == NATIVE_URI) { + // Do we need to scale the returned file + if (this.targetHeight > 0 && this.targetWidth > 0) { + try { + // Create an ExifHelper to save the exif data that is lost during compression + String resizePath = getTempDirectoryPath() + "/resize.jpg"; + // Some content: URIs do not map to file paths (e.g. picasa). + String realPath = FileHelper.getRealPath(uri, this.cordova); + ExifHelper exif = new ExifHelper(); + if (realPath != null && this.encodingType == JPEG) { + try { + exif.createInFile(realPath); + exif.readExifData(); + rotate = exif.getOrientation(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + OutputStream os = new FileOutputStream(resizePath); + bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os); + os.close(); + + // Restore exif data to file + if (realPath != null && this.encodingType == JPEG) { + exif.createOutFile(resizePath); + exif.writeExifData(); + } + + // The resized image is cached by the app in order to get around this and not have to delete you + // application cache I'm adding the current system time to the end of the file url. + this.callbackContext.success("file://" + resizePath + "?" + System.currentTimeMillis()); + } catch (Exception e) { + e.printStackTrace(); + this.failPicture("Error retrieving image."); + } + } + else { + this.callbackContext.success(uri.toString()); + } + } + if (bitmap != null) { + bitmap.recycle(); + bitmap = null; + } + System.gc(); + } + } + } + else if (resultCode == Activity.RESULT_CANCELED) { + this.failPicture("Selection cancelled."); + } + else { + this.failPicture("Selection did not complete!"); + } + } + } + + private int getImageOrientation(Uri uri) { + String[] cols = { MediaStore.Images.Media.ORIENTATION }; + Cursor cursor = cordova.getActivity().getContentResolver().query(uri, + cols, null, null, null); + int rotate = 0; + if (cursor != null) { + cursor.moveToPosition(0); + rotate = cursor.getInt(0); + cursor.close(); + } + return rotate; + } + + /** + * Figure out if the bitmap should be rotated. For instance if the picture was taken in + * portrait mode + * + * @param rotate + * @param bitmap + * @return rotated bitmap + */ + private Bitmap getRotatedBitmap(int rotate, Bitmap bitmap, ExifHelper exif) { + Matrix matrix = new Matrix(); + if (rotate == 180) { + matrix.setRotate(rotate); + } else { + matrix.setRotate(rotate, (float) bitmap.getWidth() / 2, (float) bitmap.getHeight() / 2); + } + bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); + exif.resetOrientation(); + return bitmap; + } + + /** + * In the special case where the default width, height and quality are unchanged + * we just write the file out to disk saving the expensive Bitmap.compress function. + * + * @param uri + * @throws FileNotFoundException + * @throws IOException + */ + private void writeUncompressedImage(Uri uri) throws FileNotFoundException, + IOException { + FileInputStream fis = new FileInputStream(FileHelper.stripFileProtocol(imageUri.toString())); + OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri); + byte[] buffer = new byte[4096]; + int len; + while ((len = fis.read(buffer)) != -1) { + os.write(buffer, 0, len); + } + os.flush(); + os.close(); + fis.close(); + } + + /** + * Create entry in media store for image + * + * @return uri + */ + private Uri getUriFromMediaStore() { + ContentValues values = new ContentValues(); + values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); + Uri uri; + try { + uri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); + } catch (UnsupportedOperationException e) { + LOG.d(LOG_TAG, "Can't write to external media storage."); + try { + uri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values); + } catch (UnsupportedOperationException ex) { + LOG.d(LOG_TAG, "Can't write to internal media storage."); + return null; + } + } + return uri; + } + + /** + * Return a scaled bitmap based on the target width and height + * + * @param imagePath + * @return + * @throws IOException + */ + private Bitmap getScaledBitmap(String imageUrl) throws IOException { + // If no new width or height were specified return the original bitmap + if (this.targetWidth <= 0 && this.targetHeight <= 0) { + return BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova)); + } + + // figure out the original width and height of the image + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova), null, options); + + //CB-2292: WTF? Why is the width null? + if(options.outWidth == 0 || options.outHeight == 0) + { + return null; + } + + // determine the correct aspect ratio + int[] widthHeight = calculateAspectRatio(options.outWidth, options.outHeight); + + // Load in the smallest bitmap possible that is closest to the size we want + options.inJustDecodeBounds = false; + options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, this.targetWidth, this.targetHeight); + Bitmap unscaledBitmap = BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova), null, options); + if (unscaledBitmap == null) { + return null; + } + + return Bitmap.createScaledBitmap(unscaledBitmap, widthHeight[0], widthHeight[1], true); + } + + /** + * Maintain the aspect ratio so the resulting image does not look smooshed + * + * @param origWidth + * @param origHeight + * @return + */ + public int[] calculateAspectRatio(int origWidth, int origHeight) { + int newWidth = this.targetWidth; + int newHeight = this.targetHeight; + + // If no new width or height were specified return the original bitmap + if (newWidth <= 0 && newHeight <= 0) { + newWidth = origWidth; + newHeight = origHeight; + } + // Only the width was specified + else if (newWidth > 0 && newHeight <= 0) { + newHeight = (newWidth * origHeight) / origWidth; + } + // only the height was specified + else if (newWidth <= 0 && newHeight > 0) { + newWidth = (newHeight * origWidth) / origHeight; + } + // If the user specified both a positive width and height + // (potentially different aspect ratio) then the width or height is + // scaled so that the image fits while maintaining aspect ratio. + // Alternatively, the specified width and height could have been + // kept and Bitmap.SCALE_TO_FIT specified when scaling, but this + // would result in whitespace in the new image. + else { + double newRatio = newWidth / (double) newHeight; + double origRatio = origWidth / (double) origHeight; + + if (origRatio > newRatio) { + newHeight = (newWidth * origHeight) / origWidth; + } else if (origRatio < newRatio) { + newWidth = (newHeight * origWidth) / origHeight; + } + } + + int[] retval = new int[2]; + retval[0] = newWidth; + retval[1] = newHeight; + return retval; + } + + /** + * Figure out what ratio we can load our image into memory at while still being bigger than + * our desired width and height + * + * @param srcWidth + * @param srcHeight + * @param dstWidth + * @param dstHeight + * @return + */ + public static int calculateSampleSize(int srcWidth, int srcHeight, int dstWidth, int dstHeight) { + final float srcAspect = (float)srcWidth / (float)srcHeight; + final float dstAspect = (float)dstWidth / (float)dstHeight; + + if (srcAspect > dstAspect) { + return srcWidth / dstWidth; + } else { + return srcHeight / dstHeight; + } + } + + /** + * Creates a cursor that can be used to determine how many images we have. + * + * @return a cursor + */ + private Cursor queryImgDB(Uri contentStore) { + return this.cordova.getActivity().getContentResolver().query( + contentStore, + new String[] { MediaStore.Images.Media._ID }, + null, + null, + null); + } + + /** + * Cleans up after picture taking. Checking for duplicates and that kind of stuff. + * @param newImage + */ + private void cleanup(int imageType, Uri oldImage, Uri newImage, Bitmap bitmap) { + if (bitmap != null) { + bitmap.recycle(); + } + + // Clean up initial camera-written image file. + (new File(FileHelper.stripFileProtocol(oldImage.toString()))).delete(); + + checkForDuplicateImage(imageType); + // Scan for the gallery to update pic refs in gallery + if (this.saveToPhotoAlbum && newImage != null) { + this.scanForGallery(newImage); + } + + System.gc(); + } + + /** + * Used to find out if we are in a situation where the Camera Intent adds to images + * to the content store. If we are using a FILE_URI and the number of images in the DB + * increases by 2 we have a duplicate, when using a DATA_URL the number is 1. + * + * @param type FILE_URI or DATA_URL + */ + private void checkForDuplicateImage(int type) { + int diff = 1; + Uri contentStore = whichContentStore(); + Cursor cursor = queryImgDB(contentStore); + int currentNumOfImages = cursor.getCount(); + + if (type == FILE_URI && this.saveToPhotoAlbum) { + diff = 2; + } + + // delete the duplicate file if the difference is 2 for file URI or 1 for Data URL + if ((currentNumOfImages - numPics) == diff) { + cursor.moveToLast(); + int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID))); + if (diff == 2) { + id--; + } + Uri uri = Uri.parse(contentStore + "/" + id); + this.cordova.getActivity().getContentResolver().delete(uri, null, null); + cursor.close(); + } + } + + /** + * Determine if we are storing the images in internal or external storage + * @return Uri + */ + private Uri whichContentStore() { + if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + return android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + } else { + return android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI; + } + } + + /** + * Compress bitmap using jpeg, convert to Base64 encoded string, and return to JavaScript. + * + * @param bitmap + */ + public void processPicture(Bitmap bitmap) { + ByteArrayOutputStream jpeg_data = new ByteArrayOutputStream(); + try { + if (bitmap.compress(CompressFormat.JPEG, mQuality, jpeg_data)) { + byte[] code = jpeg_data.toByteArray(); + byte[] output = Base64.encode(code, Base64.NO_WRAP); + String js_out = new String(output); + this.callbackContext.success(js_out); + js_out = null; + output = null; + code = null; + } + } catch (Exception e) { + this.failPicture("Error compressing image."); + } + jpeg_data = null; + } + + /** + * Send error message to JavaScript. + * + * @param err + */ + public void failPicture(String err) { + this.callbackContext.error(err); + } + + private void scanForGallery(Uri newImage) { + this.scanMe = newImage; + if(this.conn != null) { + this.conn.disconnect(); + } + this.conn = new MediaScannerConnection(this.cordova.getActivity().getApplicationContext(), this); + conn.connect(); + } + + public void onMediaScannerConnected() { + try{ + this.conn.scanFile(this.scanMe.toString(), "image/*"); + } catch (java.lang.IllegalStateException e){ + LOG.e(LOG_TAG, "Can't scan file in MediaScanner after taking picture"); + } + + } + + public void onScanCompleted(String path, Uri uri) { + this.conn.disconnect(); + } +} diff --git a/plugins/org.apache.cordova.camera/src/android/ExifHelper.java b/plugins/org.apache.cordova.camera/src/android/ExifHelper.java new file mode 100644 index 0000000..5160a2f --- /dev/null +++ b/plugins/org.apache.cordova.camera/src/android/ExifHelper.java @@ -0,0 +1,185 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ +package org.apache.cordova.camera; + +import java.io.IOException; + +import android.media.ExifInterface; + +public class ExifHelper { + private String aperture = null; + private String datetime = null; + private String exposureTime = null; + private String flash = null; + private String focalLength = null; + private String gpsAltitude = null; + private String gpsAltitudeRef = null; + private String gpsDateStamp = null; + private String gpsLatitude = null; + private String gpsLatitudeRef = null; + private String gpsLongitude = null; + private String gpsLongitudeRef = null; + private String gpsProcessingMethod = null; + private String gpsTimestamp = null; + private String iso = null; + private String make = null; + private String model = null; + private String orientation = null; + private String whiteBalance = null; + + private ExifInterface inFile = null; + private ExifInterface outFile = null; + + /** + * The file before it is compressed + * + * @param filePath + * @throws IOException + */ + public void createInFile(String filePath) throws IOException { + this.inFile = new ExifInterface(filePath); + } + + /** + * The file after it has been compressed + * + * @param filePath + * @throws IOException + */ + public void createOutFile(String filePath) throws IOException { + this.outFile = new ExifInterface(filePath); + } + + /** + * Reads all the EXIF data from the input file. + */ + public void readExifData() { + this.aperture = inFile.getAttribute(ExifInterface.TAG_APERTURE); + this.datetime = inFile.getAttribute(ExifInterface.TAG_DATETIME); + this.exposureTime = inFile.getAttribute(ExifInterface.TAG_EXPOSURE_TIME); + this.flash = inFile.getAttribute(ExifInterface.TAG_FLASH); + this.focalLength = inFile.getAttribute(ExifInterface.TAG_FOCAL_LENGTH); + this.gpsAltitude = inFile.getAttribute(ExifInterface.TAG_GPS_ALTITUDE); + this.gpsAltitudeRef = inFile.getAttribute(ExifInterface.TAG_GPS_ALTITUDE_REF); + this.gpsDateStamp = inFile.getAttribute(ExifInterface.TAG_GPS_DATESTAMP); + this.gpsLatitude = inFile.getAttribute(ExifInterface.TAG_GPS_LATITUDE); + this.gpsLatitudeRef = inFile.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF); + this.gpsLongitude = inFile.getAttribute(ExifInterface.TAG_GPS_LONGITUDE); + this.gpsLongitudeRef = inFile.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF); + this.gpsProcessingMethod = inFile.getAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD); + this.gpsTimestamp = inFile.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP); + this.iso = inFile.getAttribute(ExifInterface.TAG_ISO); + this.make = inFile.getAttribute(ExifInterface.TAG_MAKE); + this.model = inFile.getAttribute(ExifInterface.TAG_MODEL); + this.orientation = inFile.getAttribute(ExifInterface.TAG_ORIENTATION); + this.whiteBalance = inFile.getAttribute(ExifInterface.TAG_WHITE_BALANCE); + } + + /** + * Writes the previously stored EXIF data to the output file. + * + * @throws IOException + */ + public void writeExifData() throws IOException { + // Don't try to write to a null file + if (this.outFile == null) { + return; + } + + if (this.aperture != null) { + this.outFile.setAttribute(ExifInterface.TAG_APERTURE, this.aperture); + } + if (this.datetime != null) { + this.outFile.setAttribute(ExifInterface.TAG_DATETIME, this.datetime); + } + if (this.exposureTime != null) { + this.outFile.setAttribute(ExifInterface.TAG_EXPOSURE_TIME, this.exposureTime); + } + if (this.flash != null) { + this.outFile.setAttribute(ExifInterface.TAG_FLASH, this.flash); + } + if (this.focalLength != null) { + this.outFile.setAttribute(ExifInterface.TAG_FOCAL_LENGTH, this.focalLength); + } + if (this.gpsAltitude != null) { + this.outFile.setAttribute(ExifInterface.TAG_GPS_ALTITUDE, this.gpsAltitude); + } + if (this.gpsAltitudeRef != null) { + this.outFile.setAttribute(ExifInterface.TAG_GPS_ALTITUDE_REF, this.gpsAltitudeRef); + } + if (this.gpsDateStamp != null) { + this.outFile.setAttribute(ExifInterface.TAG_GPS_DATESTAMP, this.gpsDateStamp); + } + if (this.gpsLatitude != null) { + this.outFile.setAttribute(ExifInterface.TAG_GPS_LATITUDE, this.gpsLatitude); + } + if (this.gpsLatitudeRef != null) { + this.outFile.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, this.gpsLatitudeRef); + } + if (this.gpsLongitude != null) { + this.outFile.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, this.gpsLongitude); + } + if (this.gpsLongitudeRef != null) { + this.outFile.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, this.gpsLongitudeRef); + } + if (this.gpsProcessingMethod != null) { + this.outFile.setAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD, this.gpsProcessingMethod); + } + if (this.gpsTimestamp != null) { + this.outFile.setAttribute(ExifInterface.TAG_GPS_TIMESTAMP, this.gpsTimestamp); + } + if (this.iso != null) { + this.outFile.setAttribute(ExifInterface.TAG_ISO, this.iso); + } + if (this.make != null) { + this.outFile.setAttribute(ExifInterface.TAG_MAKE, this.make); + } + if (this.model != null) { + this.outFile.setAttribute(ExifInterface.TAG_MODEL, this.model); + } + if (this.orientation != null) { + this.outFile.setAttribute(ExifInterface.TAG_ORIENTATION, this.orientation); + } + if (this.whiteBalance != null) { + this.outFile.setAttribute(ExifInterface.TAG_WHITE_BALANCE, this.whiteBalance); + } + + this.outFile.saveAttributes(); + } + + public int getOrientation() { + int o = Integer.parseInt(this.orientation); + + if (o == ExifInterface.ORIENTATION_NORMAL) { + return 0; + } else if (o == ExifInterface.ORIENTATION_ROTATE_90) { + return 90; + } else if (o == ExifInterface.ORIENTATION_ROTATE_180) { + return 180; + } else if (o == ExifInterface.ORIENTATION_ROTATE_270) { + return 270; + } else { + return 0; + } + } + + public void resetOrientation() { + this.orientation = "" + ExifInterface.ORIENTATION_NORMAL; + } +} diff --git a/plugins/org.apache.cordova.camera/src/android/FileHelper.java b/plugins/org.apache.cordova.camera/src/android/FileHelper.java new file mode 100644 index 0000000..24ced59 --- /dev/null +++ b/plugins/org.apache.cordova.camera/src/android/FileHelper.java @@ -0,0 +1,158 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ +package org.apache.cordova.camera; + +import android.database.Cursor; +import android.net.Uri; +import android.webkit.MimeTypeMap; + +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.LOG; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Locale; + +public class FileHelper { + private static final String LOG_TAG = "FileUtils"; + private static final String _DATA = "_data"; + + /** + * Returns the real path of the given URI string. + * If the given URI string represents a content:// URI, the real path is retrieved from the media store. + * + * @param uriString the URI string of the audio/image/video + * @param cordova the current application context + * @return the full path to the file + */ + @SuppressWarnings("deprecation") + public static String getRealPath(String uriString, CordovaInterface cordova) { + String realPath = null; + + if (uriString.startsWith("content://")) { + String[] proj = { _DATA }; + Cursor cursor = cordova.getActivity().managedQuery(Uri.parse(uriString), proj, null, null, null); + int column_index = cursor.getColumnIndexOrThrow(_DATA); + cursor.moveToFirst(); + realPath = cursor.getString(column_index); + if (realPath == null) { + LOG.e(LOG_TAG, "Could get real path for URI string %s", uriString); + } + } else if (uriString.startsWith("file://")) { + realPath = uriString.substring(7); + if (realPath.startsWith("/android_asset/")) { + LOG.e(LOG_TAG, "Cannot get real path for URI string %s because it is a file:///android_asset/ URI.", uriString); + realPath = null; + } + } else { + realPath = uriString; + } + + return realPath; + } + + /** + * Returns the real path of the given URI. + * If the given URI is a content:// URI, the real path is retrieved from the media store. + * + * @param uri the URI of the audio/image/video + * @param cordova the current application context + * @return the full path to the file + */ + public static String getRealPath(Uri uri, CordovaInterface cordova) { + return FileHelper.getRealPath(uri.toString(), cordova); + } + + /** + * Returns an input stream based on given URI string. + * + * @param uriString the URI string from which to obtain the input stream + * @param cordova the current application context + * @return an input stream into the data at the given URI or null if given an invalid URI string + * @throws IOException + */ + public static InputStream getInputStreamFromUriString(String uriString, CordovaInterface cordova) throws IOException { + if (uriString.startsWith("content")) { + Uri uri = Uri.parse(uriString); + return cordova.getActivity().getContentResolver().openInputStream(uri); + } else if (uriString.startsWith("file://")) { + int question = uriString.indexOf("?"); + if (question > -1) { + uriString = uriString.substring(0,question); + } + if (uriString.startsWith("file:///android_asset/")) { + Uri uri = Uri.parse(uriString); + String relativePath = uri.getPath().substring(15); + return cordova.getActivity().getAssets().open(relativePath); + } else { + return new FileInputStream(getRealPath(uriString, cordova)); + } + } else { + return new FileInputStream(getRealPath(uriString, cordova)); + } + } + + /** + * Removes the "file://" prefix from the given URI string, if applicable. + * If the given URI string doesn't have a "file://" prefix, it is returned unchanged. + * + * @param uriString the URI string to operate on + * @return a path without the "file://" prefix + */ + public static String stripFileProtocol(String uriString) { + if (uriString.startsWith("file://")) { + uriString = uriString.substring(7); + } + return uriString; + } + + public static String getMimeTypeForExtension(String path) { + String extension = path; + int lastDot = extension.lastIndexOf('.'); + if (lastDot != -1) { + extension = extension.substring(lastDot + 1); + } + // Convert the URI string to lower case to ensure compatibility with MimeTypeMap (see CB-2185). + extension = extension.toLowerCase(Locale.getDefault()); + if (extension.equals("3ga")) { + return "audio/3gpp"; + } + return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); + } + + /** + * Returns the mime type of the data specified by the given URI string. + * + * @param uriString the URI string of the data + * @return the mime type of the specified data + */ + public static String getMimeType(String uriString, CordovaInterface cordova) { + String mimeType = null; + + Uri uri = Uri.parse(uriString); + if (uriString.startsWith("content://")) { + mimeType = cordova.getActivity().getContentResolver().getType(uri); + } else { + mimeType = getMimeTypeForExtension(uri.getPath()); + } + + return mimeType; + } +} diff --git a/plugins/org.apache.cordova.camera/src/blackberry10/index.js b/plugins/org.apache.cordova.camera/src/blackberry10/index.js new file mode 100644 index 0000000..9a5ebf3 --- /dev/null +++ b/plugins/org.apache.cordova.camera/src/blackberry10/index.js @@ -0,0 +1,129 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ +var PictureSourceType = { + PHOTOLIBRARY : 0, // Choose image from picture library (same as SAVEDPHOTOALBUM for Android) + CAMERA : 1, // Take picture from camera + SAVEDPHOTOALBUM : 2 // Choose image from picture library (same as PHOTOLIBRARY for Android) + }, + DestinationType = { + DATA_URL: 0, // Return base64 encoded string + FILE_URI: 1, // Return file uri (content://media/external/images/media/2 for Android) + NATIVE_URI: 2 // Return native uri (eg. asset-library://... for iOS) + }; + +function encodeBase64(filePath, callback) { + var sandbox = window.qnx.webplatform.getController().setFileSystemSandbox, // save original sandbox value + errorHandler = function (err) { + var msg = "An error occured: "; + + switch (err.code) { + case FileError.NOT_FOUND_ERR: + msg += "File or directory not found"; + break; + + case FileError.NOT_READABLE_ERR: + msg += "File or directory not readable"; + break; + + case FileError.PATH_EXISTS_ERR: + msg += "File or directory already exists"; + break; + + case FileError.TYPE_MISMATCH_ERR: + msg += "Invalid file type"; + break; + + default: + msg += "Unknown Error"; + break; + }; + + // set it back to original value + window.qnx.webplatform.getController().setFileSystemSandbox = sandbox; + callback(msg); + }, + gotFile = function (fileEntry) { + fileEntry.file(function (file) { + var reader = new FileReader(); + + reader.onloadend = function (e) { + // set it back to original value + window.qnx.webplatform.getController().setFileSystemSandbox = sandbox; + callback(this.result); + }; + + reader.readAsDataURL(file); + }, errorHandler); + }, + onInitFs = function (fs) { + window.qnx.webplatform.getController().setFileSystemSandbox = false; + fs.root.getFile(filePath, {create: false}, gotFile, errorHandler); + }; + + window.webkitRequestFileSystem(window.TEMPORARY, 10 * 1024 * 1024, onInitFs, errorHandler); // set size to 10MB max +} + +module.exports = { + takePicture: function (success, fail, args, env) { + var destinationType = JSON.parse(decodeURIComponent(args[1])), + sourceType = JSON.parse(decodeURIComponent(args[2])), + result = new PluginResult(args, env), + done = function (data) { + if (destinationType === DestinationType.FILE_URI) { + data = "file://" + data; + result.callbackOk(data, false); + } else { + encodeBase64(data, function (data) { + if (/^data:/.test(data)) { + data = data.slice(data.indexOf(",") + 1); + result.callbackOk(data, false); + } else { + result.callbackError(data, false); + } + }); + } + }, + cancel = function (reason) { + result.callbackError(reason, false); + }, + invoked = function (error) { + if (error) { + result.callbackError(error, false); + } + }; + + switch(sourceType) { + case PictureSourceType.CAMERA: + window.qnx.webplatform.getApplication().cards.camera.open("photo", done, cancel, invoked); + break; + + case PictureSourceType.PHOTOLIBRARY: + case PictureSourceType.SAVEDPHOTOALBUM: + window.qnx.webplatform.getApplication().cards.filePicker.open({ + mode: "Picker", + type: ["picture"] + }, done, cancel, invoked); + break; + } + + result.noResult(true); + } +}; diff --git a/plugins/org.apache.cordova.camera/src/firefoxos/CameraProxy.js b/plugins/org.apache.cordova.camera/src/firefoxos/CameraProxy.js new file mode 100644 index 0000000..bbed304 --- /dev/null +++ b/plugins/org.apache.cordova.camera/src/firefoxos/CameraProxy.js @@ -0,0 +1,52 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + + + +function getPicture(cameraSuccess, cameraError, cameraOptions) { + cameraError = cameraError || function(){}; + var pick = new MozActivity({ + name: "pick", + data: { + type: ["image/png", "image/jpg", "image/jpeg"] + } + }); + pick.onerror = cameraError; + pick.onsuccess = function() { + // image is returned as Blob in this.result.blob + // we need to call cameraSuccess with url or base64 encoded image + if (cameraOptions && cameraOptions.destinationType == 0) { + // TODO: base64 + return; + } + if (!cameraOptions || !cameraOptions.destinationType || cameraOptions.destinationType > 0) { + // url + return cameraSuccess(window.URL.createObjectURL(this.result.blob)); + } + }; +} + +module.exports = { + getPicture: getPicture, + cleanup: function(){} +}; + +require("cordova/firefoxos/commandProxy").add("Camera", module.exports); \ No newline at end of file diff --git a/plugins/org.apache.cordova.camera/src/ios/CDVCamera.h b/plugins/org.apache.cordova.camera/src/ios/CDVCamera.h new file mode 100644 index 0000000..744ae7f --- /dev/null +++ b/plugins/org.apache.cordova.camera/src/ios/CDVCamera.h @@ -0,0 +1,102 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import +#import +#import +#import + +enum CDVDestinationType { + DestinationTypeDataUrl = 0, + DestinationTypeFileUri, + DestinationTypeNativeUri +}; +typedef NSUInteger CDVDestinationType; + +enum CDVEncodingType { + EncodingTypeJPEG = 0, + EncodingTypePNG +}; +typedef NSUInteger CDVEncodingType; + +enum CDVMediaType { + MediaTypePicture = 0, + MediaTypeVideo, + MediaTypeAll +}; +typedef NSUInteger CDVMediaType; + +@interface CDVCameraPicker : UIImagePickerController +{} + +@property (assign) NSInteger quality; +@property (copy) NSString* callbackId; +@property (copy) NSString* postUrl; +@property (nonatomic) enum CDVDestinationType returnType; +@property (nonatomic) enum CDVEncodingType encodingType; +@property (strong) UIPopoverController* popoverController; +@property (assign) CGSize targetSize; +@property (assign) bool correctOrientation; +@property (assign) bool saveToPhotoAlbum; +@property (assign) bool cropToSize; +@property (strong) UIWebView* webView; +@property (assign) BOOL popoverSupported; + +@end + +// ======================================================================= // + +@interface CDVCamera : CDVPlugin +{} + +@property (strong) CDVCameraPicker* pickerController; +@property (strong) NSMutableDictionary *metadata; +@property (strong, nonatomic) CLLocationManager *locationManager; +@property (strong) NSData* data; + +/* + * getPicture + * + * arguments: + * 1: this is the javascript function that will be called with the results, the first parameter passed to the + * javascript function is the picture as a Base64 encoded string + * 2: this is the javascript function to be called if there was an error + * options: + * quality: integer between 1 and 100 + */ +- (void)takePicture:(CDVInvokedUrlCommand*)command; +- (void)postImage:(UIImage*)anImage withFilename:(NSString*)filename toUrl:(NSURL*)url; +- (void)cleanup:(CDVInvokedUrlCommand*)command; +- (void)repositionPopover:(CDVInvokedUrlCommand*)command; + +- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info; +- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingImage:(UIImage*)image editingInfo:(NSDictionary*)editingInfo; +- (void)imagePickerControllerDidCancel:(UIImagePickerController*)picker; +- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated; +- (UIImage*)imageByScalingAndCroppingForSize:(UIImage*)anImage toSize:(CGSize)targetSize; +- (UIImage*)imageByScalingNotCroppingForSize:(UIImage*)anImage toSize:(CGSize)frameSize; +- (UIImage*)imageCorrectedForCaptureOrientation:(UIImage*)anImage; + +- (void)locationManager:(CLLocationManager*)manager didUpdateToLocation:(CLLocation*)newLocation fromLocation:(CLLocation*)oldLocation; +- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error; + +@end diff --git a/plugins/org.apache.cordova.camera/src/ios/CDVCamera.m b/plugins/org.apache.cordova.camera/src/ios/CDVCamera.m new file mode 100644 index 0000000..42c5237 --- /dev/null +++ b/plugins/org.apache.cordova.camera/src/ios/CDVCamera.m @@ -0,0 +1,756 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVCamera.h" +#import "CDVJpegHeaderWriter.h" +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#define CDV_PHOTO_PREFIX @"cdv_photo_" + +static NSSet* org_apache_cordova_validArrowDirections; + +@interface CDVCamera () + +@property (readwrite, assign) BOOL hasPendingOperation; + +@end + +@implementation CDVCamera + ++ (void)initialize +{ + org_apache_cordova_validArrowDirections = [[NSSet alloc] initWithObjects:[NSNumber numberWithInt:UIPopoverArrowDirectionUp], [NSNumber numberWithInt:UIPopoverArrowDirectionDown], [NSNumber numberWithInt:UIPopoverArrowDirectionLeft], [NSNumber numberWithInt:UIPopoverArrowDirectionRight], [NSNumber numberWithInt:UIPopoverArrowDirectionAny], nil]; +} + +@synthesize hasPendingOperation, pickerController, locationManager; + +- (BOOL)popoverSupported +{ + return (NSClassFromString(@"UIPopoverController") != nil) && + (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad); +} + +/* takePicture arguments: + * INDEX ARGUMENT + * 0 quality + * 1 destination type + * 2 source type + * 3 targetWidth + * 4 targetHeight + * 5 encodingType + * 6 mediaType + * 7 allowsEdit + * 8 correctOrientation + * 9 saveToPhotoAlbum + * 10 popoverOptions + * 11 cameraDirection + */ +- (void)takePicture:(CDVInvokedUrlCommand*)command +{ + NSString* callbackId = command.callbackId; + NSArray* arguments = command.arguments; + + self.hasPendingOperation = NO; + + NSString* sourceTypeString = [arguments objectAtIndex:2]; + UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera; // default + if (sourceTypeString != nil) { + sourceType = (UIImagePickerControllerSourceType)[sourceTypeString intValue]; + } + + bool hasCamera = [UIImagePickerController isSourceTypeAvailable:sourceType]; + if (!hasCamera) { + NSLog(@"Camera.getPicture: source type %d not available.", sourceType); + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"no camera available"]; + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + return; + } + + bool allowEdit = [[arguments objectAtIndex:7] boolValue]; + NSNumber* targetWidth = [arguments objectAtIndex:3]; + NSNumber* targetHeight = [arguments objectAtIndex:4]; + NSNumber* mediaValue = [arguments objectAtIndex:6]; + CDVMediaType mediaType = (mediaValue) ? [mediaValue intValue] : MediaTypePicture; + + CGSize targetSize = CGSizeMake(0, 0); + if ((targetWidth != nil) && (targetHeight != nil)) { + targetSize = CGSizeMake([targetWidth floatValue], [targetHeight floatValue]); + } + + // If a popover is already open, close it; we only want one at a time. + if (([[self pickerController] popoverController] != nil) && [[[self pickerController] popoverController] isPopoverVisible]) { + [[[self pickerController] popoverController] dismissPopoverAnimated:YES]; + [[[self pickerController] popoverController] setDelegate:nil]; + [[self pickerController] setPopoverController:nil]; + } + + CDVCameraPicker* cameraPicker = [[CDVCameraPicker alloc] init]; + self.pickerController = cameraPicker; + + cameraPicker.delegate = self; + cameraPicker.sourceType = sourceType; + cameraPicker.allowsEditing = allowEdit; // THIS IS ALL IT TAKES FOR CROPPING - jm + cameraPicker.callbackId = callbackId; + cameraPicker.targetSize = targetSize; + cameraPicker.cropToSize = NO; + // we need to capture this state for memory warnings that dealloc this object + cameraPicker.webView = self.webView; + cameraPicker.popoverSupported = [self popoverSupported]; + + cameraPicker.correctOrientation = [[arguments objectAtIndex:8] boolValue]; + cameraPicker.saveToPhotoAlbum = [[arguments objectAtIndex:9] boolValue]; + + cameraPicker.encodingType = ([arguments objectAtIndex:5]) ? [[arguments objectAtIndex:5] intValue] : EncodingTypeJPEG; + + cameraPicker.quality = ([arguments objectAtIndex:0]) ? [[arguments objectAtIndex:0] intValue] : 50; + cameraPicker.returnType = ([arguments objectAtIndex:1]) ? [[arguments objectAtIndex:1] intValue] : DestinationTypeFileUri; + + if (sourceType == UIImagePickerControllerSourceTypeCamera) { + // We only allow taking pictures (no video) in this API. + cameraPicker.mediaTypes = [NSArray arrayWithObjects:(NSString*)kUTTypeImage, nil]; + + // We can only set the camera device if we're actually using the camera. + NSNumber* cameraDirection = [command argumentAtIndex:11 withDefault:[NSNumber numberWithInteger:UIImagePickerControllerCameraDeviceRear]]; + cameraPicker.cameraDevice = (UIImagePickerControllerCameraDevice)[cameraDirection intValue]; + } else if (mediaType == MediaTypeAll) { + cameraPicker.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:sourceType]; + } else { + NSArray* mediaArray = [NSArray arrayWithObjects:(NSString*)(mediaType == MediaTypeVideo ? kUTTypeMovie : kUTTypeImage), nil]; + cameraPicker.mediaTypes = mediaArray; + } + + if ([self popoverSupported] && (sourceType != UIImagePickerControllerSourceTypeCamera)) { + if (cameraPicker.popoverController == nil) { + cameraPicker.popoverController = [[NSClassFromString(@"UIPopoverController")alloc] initWithContentViewController:cameraPicker]; + } + NSDictionary* options = [command.arguments objectAtIndex:10 withDefault:nil]; + [self displayPopover:options]; + } else { + SEL selector = NSSelectorFromString(@"presentViewController:animated:completion:"); + if ([self.viewController respondsToSelector:selector]) { + [self.viewController presentViewController:cameraPicker animated:YES completion:nil]; + } else { + // deprecated as of iOS >= 6.0 + [self.viewController presentModalViewController:cameraPicker animated:YES]; + } + } + self.hasPendingOperation = YES; +} + +- (void)repositionPopover:(CDVInvokedUrlCommand*)command +{ + NSDictionary* options = [command.arguments objectAtIndex:0 withDefault:nil]; + + [self displayPopover:options]; +} + +- (void)displayPopover:(NSDictionary*)options +{ + int x = 0; + int y = 32; + int width = 320; + int height = 480; + UIPopoverArrowDirection arrowDirection = UIPopoverArrowDirectionAny; + + if (options) { + x = [options integerValueForKey:@"x" defaultValue:0]; + y = [options integerValueForKey:@"y" defaultValue:32]; + width = [options integerValueForKey:@"width" defaultValue:320]; + height = [options integerValueForKey:@"height" defaultValue:480]; + arrowDirection = [options integerValueForKey:@"arrowDir" defaultValue:UIPopoverArrowDirectionAny]; + if (![org_apache_cordova_validArrowDirections containsObject:[NSNumber numberWithInt:arrowDirection]]) { + arrowDirection = UIPopoverArrowDirectionAny; + } + } + + [[[self pickerController] popoverController] setDelegate:self]; + [[[self pickerController] popoverController] presentPopoverFromRect:CGRectMake(x, y, width, height) + inView:[self.webView superview] + permittedArrowDirections:arrowDirection + animated:YES]; +} + +- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated +{ + if([navigationController isKindOfClass:[UIImagePickerController class]]){ + UIImagePickerController * cameraPicker = (UIImagePickerController*)navigationController; + + if(![cameraPicker.mediaTypes containsObject:(NSString*) kUTTypeImage]){ + [viewController.navigationItem setTitle:NSLocalizedString(@"Videos title", nil)]; + } + } +} + +- (void)cleanup:(CDVInvokedUrlCommand*)command +{ + // empty the tmp directory + NSFileManager* fileMgr = [[NSFileManager alloc] init]; + NSError* err = nil; + BOOL hasErrors = NO; + + // clear contents of NSTemporaryDirectory + NSString* tempDirectoryPath = NSTemporaryDirectory(); + NSDirectoryEnumerator* directoryEnumerator = [fileMgr enumeratorAtPath:tempDirectoryPath]; + NSString* fileName = nil; + BOOL result; + + while ((fileName = [directoryEnumerator nextObject])) { + // only delete the files we created + if (![fileName hasPrefix:CDV_PHOTO_PREFIX]) { + continue; + } + NSString* filePath = [tempDirectoryPath stringByAppendingPathComponent:fileName]; + result = [fileMgr removeItemAtPath:filePath error:&err]; + if (!result && err) { + NSLog(@"Failed to delete: %@ (error: %@)", filePath, err); + hasErrors = YES; + } + } + + CDVPluginResult* pluginResult; + if (hasErrors) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:@"One or more files failed to be deleted."]; + } else { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + +- (void)popoverControllerDidDismissPopover:(id)popoverController +{ + // [ self imagePickerControllerDidCancel:self.pickerController ]; ' + UIPopoverController* pc = (UIPopoverController*)popoverController; + + [pc dismissPopoverAnimated:YES]; + pc.delegate = nil; + if (self.pickerController && self.pickerController.callbackId && self.pickerController.popoverController) { + self.pickerController.popoverController = nil; + NSString* callbackId = self.pickerController.callbackId; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"no image selected"]; // error callback expects string ATM + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + } + self.hasPendingOperation = NO; +} + +- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info +{ + CDVCameraPicker* cameraPicker = (CDVCameraPicker*)picker; + + if (cameraPicker.popoverSupported && (cameraPicker.popoverController != nil)) { + [cameraPicker.popoverController dismissPopoverAnimated:YES]; + cameraPicker.popoverController.delegate = nil; + cameraPicker.popoverController = nil; + } else { + if ([cameraPicker respondsToSelector:@selector(presentingViewController)]) { + [[cameraPicker presentingViewController] dismissModalViewControllerAnimated:YES]; + } else { + [[cameraPicker parentViewController] dismissModalViewControllerAnimated:YES]; + } + } + + CDVPluginResult* result = nil; + + NSString* mediaType = [info objectForKey:UIImagePickerControllerMediaType]; + // IMAGE TYPE + if ([mediaType isEqualToString:(NSString*)kUTTypeImage]) { + if (cameraPicker.returnType == DestinationTypeNativeUri) { + NSString* nativeUri = [(NSURL*)[info objectForKey:UIImagePickerControllerReferenceURL] absoluteString]; + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nativeUri]; + } else { + // get the image + UIImage* image = nil; + if (cameraPicker.allowsEditing && [info objectForKey:UIImagePickerControllerEditedImage]) { + image = [info objectForKey:UIImagePickerControllerEditedImage]; + } else { + image = [info objectForKey:UIImagePickerControllerOriginalImage]; + } + + if (cameraPicker.correctOrientation) { + image = [self imageCorrectedForCaptureOrientation:image]; + } + + UIImage* scaledImage = nil; + + if ((cameraPicker.targetSize.width > 0) && (cameraPicker.targetSize.height > 0)) { + // if cropToSize, resize image and crop to target size, otherwise resize to fit target without cropping + if (cameraPicker.cropToSize) { + scaledImage = [self imageByScalingAndCroppingForSize:image toSize:cameraPicker.targetSize]; + } else { + scaledImage = [self imageByScalingNotCroppingForSize:image toSize:cameraPicker.targetSize]; + } + } + + NSData* data = nil; + // returnedImage is the image that is returned to caller and (optionally) saved to photo album + UIImage* returnedImage = (scaledImage == nil ? image : scaledImage); + + if (cameraPicker.encodingType == EncodingTypePNG) { + data = UIImagePNGRepresentation(returnedImage); + } else if ((cameraPicker.allowsEditing==false) && (cameraPicker.targetSize.width <= 0) && (cameraPicker.targetSize.height <= 0) && (cameraPicker.correctOrientation==false)){ + // use image unedited as requested , don't resize + data = UIImageJPEGRepresentation(returnedImage, 1.0); + } else { + data = UIImageJPEGRepresentation(returnedImage, cameraPicker.quality / 100.0f); + + NSDictionary *controllerMetadata = [info objectForKey:@"UIImagePickerControllerMediaMetadata"]; + if (controllerMetadata) { + self.data = data; + self.metadata = [[NSMutableDictionary alloc] init]; + + NSMutableDictionary *EXIFDictionary = [[controllerMetadata objectForKey:(NSString *)kCGImagePropertyExifDictionary]mutableCopy]; + if (EXIFDictionary) [self.metadata setObject:EXIFDictionary forKey:(NSString *)kCGImagePropertyExifDictionary]; + + [[self locationManager] startUpdatingLocation]; + return; + } + } + + if (cameraPicker.saveToPhotoAlbum) { + ALAssetsLibrary *library = [ALAssetsLibrary new]; + [library writeImageToSavedPhotosAlbum:returnedImage.CGImage orientation:(ALAssetOrientation)(returnedImage.imageOrientation) completionBlock:nil]; + } + + if (cameraPicker.returnType == DestinationTypeFileUri) { + // write to temp directory and return URI + // get the temp directory path + NSString* docsPath = [NSTemporaryDirectory()stringByStandardizingPath]; + NSError* err = nil; + NSFileManager* fileMgr = [[NSFileManager alloc] init]; // recommended by apple (vs [NSFileManager defaultManager]) to be threadsafe + // generate unique file name + NSString* filePath; + + int i = 1; + do { + filePath = [NSString stringWithFormat:@"%@/%@%03d.%@", docsPath, CDV_PHOTO_PREFIX, i++, cameraPicker.encodingType == EncodingTypePNG ? @"png":@"jpg"]; + } while ([fileMgr fileExistsAtPath:filePath]); + + // save file + if (![data writeToFile:filePath options:NSAtomicWrite error:&err]) { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]]; + } else { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[[NSURL fileURLWithPath:filePath] absoluteString]]; + } + } else { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[data base64EncodedString]]; + } + } + } + // NOT IMAGE TYPE (MOVIE) + else { + NSString* moviePath = [[info objectForKey:UIImagePickerControllerMediaURL] absoluteString]; + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:moviePath]; + } + + if (result) { + [self.commandDelegate sendPluginResult:result callbackId:cameraPicker.callbackId]; + } + + self.hasPendingOperation = NO; + self.pickerController = nil; +} + +// older api calls newer didFinishPickingMediaWithInfo +- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingImage:(UIImage*)image editingInfo:(NSDictionary*)editingInfo +{ + NSDictionary* imageInfo = [NSDictionary dictionaryWithObject:image forKey:UIImagePickerControllerOriginalImage]; + + [self imagePickerController:picker didFinishPickingMediaWithInfo:imageInfo]; +} + +- (void)imagePickerControllerDidCancel:(UIImagePickerController*)picker +{ + CDVCameraPicker* cameraPicker = (CDVCameraPicker*)picker; + + if ([cameraPicker respondsToSelector:@selector(presentingViewController)]) { + [[cameraPicker presentingViewController] dismissModalViewControllerAnimated:YES]; + } else { + [[cameraPicker parentViewController] dismissModalViewControllerAnimated:YES]; + } + // popoverControllerDidDismissPopover:(id)popoverController is called if popover is cancelled + + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"no image selected"]; // error callback expects string ATM + [self.commandDelegate sendPluginResult:result callbackId:cameraPicker.callbackId]; + + self.hasPendingOperation = NO; + self.pickerController = nil; +} + +- (UIImage*)imageByScalingAndCroppingForSize:(UIImage*)anImage toSize:(CGSize)targetSize +{ + UIImage* sourceImage = anImage; + UIImage* newImage = nil; + CGSize imageSize = sourceImage.size; + CGFloat width = imageSize.width; + CGFloat height = imageSize.height; + CGFloat targetWidth = targetSize.width; + CGFloat targetHeight = targetSize.height; + CGFloat scaleFactor = 0.0; + CGFloat scaledWidth = targetWidth; + CGFloat scaledHeight = targetHeight; + CGPoint thumbnailPoint = CGPointMake(0.0, 0.0); + + if (CGSizeEqualToSize(imageSize, targetSize) == NO) { + CGFloat widthFactor = targetWidth / width; + CGFloat heightFactor = targetHeight / height; + + if (widthFactor > heightFactor) { + scaleFactor = widthFactor; // scale to fit height + } else { + scaleFactor = heightFactor; // scale to fit width + } + scaledWidth = width * scaleFactor; + scaledHeight = height * scaleFactor; + + // center the image + if (widthFactor > heightFactor) { + thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5; + } else if (widthFactor < heightFactor) { + thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5; + } + } + + UIGraphicsBeginImageContext(targetSize); // this will crop + + CGRect thumbnailRect = CGRectZero; + thumbnailRect.origin = thumbnailPoint; + thumbnailRect.size.width = scaledWidth; + thumbnailRect.size.height = scaledHeight; + + [sourceImage drawInRect:thumbnailRect]; + + newImage = UIGraphicsGetImageFromCurrentImageContext(); + if (newImage == nil) { + NSLog(@"could not scale image"); + } + + // pop the context to get back to the default + UIGraphicsEndImageContext(); + return newImage; +} + +- (UIImage*)imageCorrectedForCaptureOrientation:(UIImage*)anImage +{ + float rotation_radians = 0; + bool perpendicular = false; + + switch ([anImage imageOrientation]) { + case UIImageOrientationUp : + rotation_radians = 0.0; + break; + + case UIImageOrientationDown: + rotation_radians = M_PI; // don't be scared of radians, if you're reading this, you're good at math + break; + + case UIImageOrientationRight: + rotation_radians = M_PI_2; + perpendicular = true; + break; + + case UIImageOrientationLeft: + rotation_radians = -M_PI_2; + perpendicular = true; + break; + + default: + break; + } + + UIGraphicsBeginImageContext(CGSizeMake(anImage.size.width, anImage.size.height)); + CGContextRef context = UIGraphicsGetCurrentContext(); + + // Rotate around the center point + CGContextTranslateCTM(context, anImage.size.width / 2, anImage.size.height / 2); + CGContextRotateCTM(context, rotation_radians); + + CGContextScaleCTM(context, 1.0, -1.0); + float width = perpendicular ? anImage.size.height : anImage.size.width; + float height = perpendicular ? anImage.size.width : anImage.size.height; + CGContextDrawImage(context, CGRectMake(-width / 2, -height / 2, width, height), [anImage CGImage]); + + // Move the origin back since the rotation might've change it (if its 90 degrees) + if (perpendicular) { + CGContextTranslateCTM(context, -anImage.size.height / 2, -anImage.size.width / 2); + } + + UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return newImage; +} + +- (UIImage*)imageByScalingNotCroppingForSize:(UIImage*)anImage toSize:(CGSize)frameSize +{ + UIImage* sourceImage = anImage; + UIImage* newImage = nil; + CGSize imageSize = sourceImage.size; + CGFloat width = imageSize.width; + CGFloat height = imageSize.height; + CGFloat targetWidth = frameSize.width; + CGFloat targetHeight = frameSize.height; + CGFloat scaleFactor = 0.0; + CGSize scaledSize = frameSize; + + if (CGSizeEqualToSize(imageSize, frameSize) == NO) { + CGFloat widthFactor = targetWidth / width; + CGFloat heightFactor = targetHeight / height; + + // opposite comparison to imageByScalingAndCroppingForSize in order to contain the image within the given bounds + if (widthFactor > heightFactor) { + scaleFactor = heightFactor; // scale to fit height + } else { + scaleFactor = widthFactor; // scale to fit width + } + scaledSize = CGSizeMake(MIN(width * scaleFactor, targetWidth), MIN(height * scaleFactor, targetHeight)); + } + + UIGraphicsBeginImageContext(scaledSize); // this will resize + + [sourceImage drawInRect:CGRectMake(0, 0, scaledSize.width, scaledSize.height)]; + + newImage = UIGraphicsGetImageFromCurrentImageContext(); + if (newImage == nil) { + NSLog(@"could not scale image"); + } + + // pop the context to get back to the default + UIGraphicsEndImageContext(); + return newImage; +} + +- (void)postImage:(UIImage*)anImage withFilename:(NSString*)filename toUrl:(NSURL*)url +{ + self.hasPendingOperation = YES; + + NSString* boundary = @"----BOUNDARY_IS_I"; + + NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL:url]; + [req setHTTPMethod:@"POST"]; + + NSString* contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary]; + [req setValue:contentType forHTTPHeaderField:@"Content-type"]; + + NSData* imageData = UIImagePNGRepresentation(anImage); + + // adding the body + NSMutableData* postBody = [NSMutableData data]; + + // first parameter an image + [postBody appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; + [postBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"upload\"; filename=\"%@\"\r\n", filename] dataUsingEncoding:NSUTF8StringEncoding]]; + [postBody appendData:[@"Content-Type: image/png\r\n\r\n" dataUsingEncoding : NSUTF8StringEncoding]]; + [postBody appendData:imageData]; + + // // second parameter information + // [postBody appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; + // [postBody appendData:[@"Content-Disposition: form-data; name=\"some_other_name\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; + // [postBody appendData:[@"some_other_value" dataUsingEncoding:NSUTF8StringEncoding]]; + // [postBody appendData:[[NSString stringWithFormat:@"\r\n--%@--\r \n",boundary] dataUsingEncoding:NSUTF8StringEncoding]]; + + [req setHTTPBody:postBody]; + + NSURLResponse* response; + NSError* error; + [NSURLConnection sendSynchronousRequest:req returningResponse:&response error:&error]; + + // NSData* result = [NSURLConnection sendSynchronousRequest:req returningResponse:&response error:&error]; + // NSString * resultStr = [[[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding] autorelease]; + + self.hasPendingOperation = NO; +} + + +- (CLLocationManager *)locationManager { + + if (locationManager != nil) { + return locationManager; + } + + locationManager = [[CLLocationManager alloc] init]; + [locationManager setDesiredAccuracy:kCLLocationAccuracyNearestTenMeters]; + [locationManager setDelegate:self]; + + return locationManager; +} + +- (void)locationManager:(CLLocationManager*)manager didUpdateToLocation:(CLLocation*)newLocation fromLocation:(CLLocation*)oldLocation +{ + if (locationManager != nil) { + [self.locationManager stopUpdatingLocation]; + self.locationManager = nil; + + NSMutableDictionary *GPSDictionary = [[NSMutableDictionary dictionary] init]; + + CLLocationDegrees latitude = newLocation.coordinate.latitude; + CLLocationDegrees longitude = newLocation.coordinate.longitude; + + // latitude + if (latitude < 0.0) { + latitude = latitude * -1.0f; + [GPSDictionary setObject:@"S" forKey:(NSString*)kCGImagePropertyGPSLatitudeRef]; + } else { + [GPSDictionary setObject:@"N" forKey:(NSString*)kCGImagePropertyGPSLatitudeRef]; + } + [GPSDictionary setObject:[NSNumber numberWithFloat:latitude] forKey:(NSString*)kCGImagePropertyGPSLatitude]; + + // longitude + if (longitude < 0.0) { + longitude = longitude * -1.0f; + [GPSDictionary setObject:@"W" forKey:(NSString*)kCGImagePropertyGPSLongitudeRef]; + } + else { + [GPSDictionary setObject:@"E" forKey:(NSString*)kCGImagePropertyGPSLongitudeRef]; + } + [GPSDictionary setObject:[NSNumber numberWithFloat:longitude] forKey:(NSString*)kCGImagePropertyGPSLongitude]; + + // altitude + CGFloat altitude = newLocation.altitude; + if (!isnan(altitude)){ + if (altitude < 0) { + altitude = -altitude; + [GPSDictionary setObject:@"1" forKey:(NSString *)kCGImagePropertyGPSAltitudeRef]; + } else { + [GPSDictionary setObject:@"0" forKey:(NSString *)kCGImagePropertyGPSAltitudeRef]; + } + [GPSDictionary setObject:[NSNumber numberWithFloat:altitude] forKey:(NSString *)kCGImagePropertyGPSAltitude]; + } + + // Time and date + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + [formatter setDateFormat:@"HH:mm:ss.SSSSSS"]; + [formatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]]; + [GPSDictionary setObject:[formatter stringFromDate:newLocation.timestamp] forKey:(NSString *)kCGImagePropertyGPSTimeStamp]; + [formatter setDateFormat:@"yyyy:MM:dd"]; + [GPSDictionary setObject:[formatter stringFromDate:newLocation.timestamp] forKey:(NSString *)kCGImagePropertyGPSDateStamp]; + + [self.metadata setObject:GPSDictionary forKey:(NSString *)kCGImagePropertyGPSDictionary]; + [self imagePickerControllerReturnImageResult]; + } +} + +- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { + if (locationManager != nil) { + [self.locationManager stopUpdatingLocation]; + self.locationManager = nil; + + [self imagePickerControllerReturnImageResult]; + } +} + +- (void)imagePickerControllerReturnImageResult +{ + CDVPluginResult* result = nil; + + if (self.metadata) { + CGImageSourceRef sourceImage = CGImageSourceCreateWithData((__bridge_retained CFDataRef)self.data, NULL); + CFStringRef sourceType = CGImageSourceGetType(sourceImage); + + CGImageDestinationRef destinationImage = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)self.data, sourceType, 1, NULL); + CGImageDestinationAddImageFromSource(destinationImage, sourceImage, 0, (__bridge CFDictionaryRef)self.metadata); + CGImageDestinationFinalize(destinationImage); + + CFRelease(sourceImage); + CFRelease(destinationImage); + } + + if (self.pickerController.saveToPhotoAlbum) { + ALAssetsLibrary *library = [ALAssetsLibrary new]; + [library writeImageDataToSavedPhotosAlbum:self.data metadata:self.metadata completionBlock:nil]; + } + + if (self.pickerController.returnType == DestinationTypeFileUri) { + // write to temp directory and return URI + // get the temp directory path + NSString* docsPath = [NSTemporaryDirectory()stringByStandardizingPath]; + NSError* err = nil; + NSFileManager* fileMgr = [[NSFileManager alloc] init]; // recommended by apple (vs [NSFileManager defaultManager]) to be threadsafe + // generate unique file name + NSString* filePath; + + int i = 1; + do { + filePath = [NSString stringWithFormat:@"%@/%@%03d.%@", docsPath, CDV_PHOTO_PREFIX, i++, self.pickerController.encodingType == EncodingTypePNG ? @"png":@"jpg"]; + } while ([fileMgr fileExistsAtPath:filePath]); + + // save file + if (![self.data writeToFile:filePath options:NSAtomicWrite error:&err]) { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]]; + } + else { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[[NSURL fileURLWithPath:filePath] absoluteString]]; + } + } + else { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[self.data base64EncodedString]]; + } + if (result) { + [self.commandDelegate sendPluginResult:result callbackId:self.pickerController.callbackId]; + } + + if (result) { + [self.commandDelegate sendPluginResult:result callbackId:self.pickerController.callbackId]; + } + + self.hasPendingOperation = NO; + self.pickerController = nil; + self.data = nil; + self.metadata = nil; +} + +@end + +@implementation CDVCameraPicker + +@synthesize quality, postUrl; +@synthesize returnType; +@synthesize callbackId; +@synthesize popoverController; +@synthesize targetSize; +@synthesize correctOrientation; +@synthesize saveToPhotoAlbum; +@synthesize encodingType; +@synthesize cropToSize; +@synthesize webView; +@synthesize popoverSupported; + +- (BOOL)prefersStatusBarHidden { + return YES; +} + +- (UIViewController*)childViewControllerForStatusBarHidden { + return nil; +} + +- (void)viewWillAppear:(BOOL)animated { + SEL sel = NSSelectorFromString(@"setNeedsStatusBarAppearanceUpdate"); + if ([self respondsToSelector:sel]) { + [self performSelector:sel withObject:nil afterDelay:0]; + } + + [super viewWillAppear:animated]; +} + +@end diff --git a/plugins/org.apache.cordova.camera/src/ios/CDVExif.h b/plugins/org.apache.cordova.camera/src/ios/CDVExif.h new file mode 100644 index 0000000..3e8adbd --- /dev/null +++ b/plugins/org.apache.cordova.camera/src/ios/CDVExif.h @@ -0,0 +1,43 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#ifndef CordovaLib_ExifData_h +#define CordovaLib_ExifData_h + +// exif data types +typedef enum exifDataTypes { + EDT_UBYTE = 1, // 8 bit unsigned integer + EDT_ASCII_STRING, // 8 bits containing 7 bit ASCII code, null terminated + EDT_USHORT, // 16 bit unsigned integer + EDT_ULONG, // 32 bit unsigned integer + EDT_URATIONAL, // 2 longs, first is numerator and second is denominator + EDT_SBYTE, + EDT_UNDEFINED, // 8 bits + EDT_SSHORT, + EDT_SLONG, // 32bit signed integer (2's complement) + EDT_SRATIONAL, // 2 SLONGS, first long is numerator, second is denominator + EDT_SINGLEFLOAT, + EDT_DOUBLEFLOAT +} ExifDataTypes; + +// maps integer code for exif data types to width in bytes +static const int DataTypeToWidth[] = {1,1,2,4,8,1,1,2,4,8,4,8}; + +static const int RECURSE_HORIZON = 8; +#endif diff --git a/plugins/org.apache.cordova.camera/src/ios/CDVJpegHeaderWriter.h b/plugins/org.apache.cordova.camera/src/ios/CDVJpegHeaderWriter.h new file mode 100644 index 0000000..3b43ef0 --- /dev/null +++ b/plugins/org.apache.cordova.camera/src/ios/CDVJpegHeaderWriter.h @@ -0,0 +1,62 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import + +@interface CDVJpegHeaderWriter : NSObject { + NSDictionary * SubIFDTagFormatDict; + NSDictionary * IFD0TagFormatDict; +} + +- (NSData*) spliceExifBlockIntoJpeg: (NSData*) jpegdata + withExifBlock: (NSString*) exifstr; +- (NSString*) createExifAPP1 : (NSDictionary*) datadict; +- (NSString*) formattedHexStringFromDecimalNumber: (NSNumber*) numb + withPlaces: (NSNumber*) width; +- (NSString*) formatNumberWithLeadingZeroes: (NSNumber*) numb + withPlaces: (NSNumber*) places; +- (NSString*) decimalToUnsignedRational: (NSNumber*) numb + withResultNumerator: (NSNumber**) numerator + withResultDenominator: (NSNumber**) denominator; +- (void) continuedFraction: (double) val + withFractionList: (NSMutableArray*) fractionlist + withHorizon: (int) horizon; +//- (void) expandContinuedFraction: (NSArray*) fractionlist; +- (void) splitDouble: (double) val + withIntComponent: (int*) rightside + withFloatRemainder: (double*) leftside; +- (NSString*) formatRationalWithNumerator: (NSNumber*) numerator + withDenominator: (NSNumber*) denominator + asSigned: (Boolean) signedFlag; +- (NSString*) hexStringFromData : (NSData*) data; +- (NSNumber*) numericFromHexString : (NSString *) hexstring; + +/* +- (void) readExifMetaData : (NSData*) imgdata; +- (void) spliceImageData : (NSData*) imgdata withExifData: (NSDictionary*) exifdata; +- (void) locateExifMetaData : (NSData*) imgdata; +- (NSString*) createExifAPP1 : (NSDictionary*) datadict; +- (void) createExifDataString : (NSDictionary*) datadict; +- (NSString*) createDataElement : (NSString*) element + withElementData: (NSString*) data + withExternalDataBlock: (NSDictionary*) memblock; +- (NSString*) hexStringFromData : (NSData*) data; +- (NSNumber*) numericFromHexString : (NSString *) hexstring; +*/ +@end diff --git a/plugins/org.apache.cordova.camera/src/ios/CDVJpegHeaderWriter.m b/plugins/org.apache.cordova.camera/src/ios/CDVJpegHeaderWriter.m new file mode 100644 index 0000000..93cafb8 --- /dev/null +++ b/plugins/org.apache.cordova.camera/src/ios/CDVJpegHeaderWriter.m @@ -0,0 +1,547 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVJpegHeaderWriter.h" +#include "CDVExif.h" + +/* macros for tag info shorthand: + tagno : tag number + typecode : data type + components : number of components + appendString (TAGINF_W_APPEND only) : string to append to data + Exif date data format include an extra 0x00 to the end of the data + */ +#define TAGINF(tagno, typecode, components) [NSArray arrayWithObjects: tagno, typecode, components, nil] +#define TAGINF_W_APPEND(tagno, typecode, components, appendString) [NSArray arrayWithObjects: tagno, typecode, components, appendString, nil] + +const uint mJpegId = 0xffd8; // JPEG format marker +const uint mExifMarker = 0xffe1; // APP1 jpeg header marker +const uint mExif = 0x45786966; // ASCII 'Exif', first characters of valid exif header after size +const uint mMotorallaByteAlign = 0x4d4d; // 'MM', motorola byte align, msb first or 'sane' +const uint mIntelByteAlgin = 0x4949; // 'II', Intel byte align, lsb first or 'batshit crazy reverso world' +const uint mTiffLength = 0x2a; // after byte align bits, next to bits are 0x002a(MM) or 0x2a00(II), tiff version number + + +@implementation CDVJpegHeaderWriter + +- (id) init { + self = [super init]; + // supported tags for exif IFD + IFD0TagFormatDict = [[NSDictionary alloc] initWithObjectsAndKeys: + // TAGINF(@"010e", [NSNumber numberWithInt:EDT_ASCII_STRING], @0), @"ImageDescription", + TAGINF_W_APPEND(@"0132", [NSNumber numberWithInt:EDT_ASCII_STRING], @20, @"00"), @"DateTime", + TAGINF(@"010f", [NSNumber numberWithInt:EDT_ASCII_STRING], @0), @"Make", + TAGINF(@"0110", [NSNumber numberWithInt:EDT_ASCII_STRING], @0), @"Model", + TAGINF(@"0131", [NSNumber numberWithInt:EDT_ASCII_STRING], @0), @"Software", + TAGINF(@"011a", [NSNumber numberWithInt:EDT_URATIONAL], @1), @"XResolution", + TAGINF(@"011b", [NSNumber numberWithInt:EDT_URATIONAL], @1), @"YResolution", + // currently supplied outside of Exif data block by UIImagePickerControllerMediaMetadata, this is set manually in CDVCamera.m + /* TAGINF(@"0112", [NSNumber numberWithInt:EDT_USHORT], @1), @"Orientation", + + // rest of the tags are supported by exif spec, but are not specified by UIImagePickerControllerMediaMedadata + // should camera hardware supply these values in future versions, or if they can be derived, ImageHeaderWriter will include them gracefully + TAGINF(@"0128", [NSNumber numberWithInt:EDT_USHORT], @1), @"ResolutionUnit", + TAGINF(@"013e", [NSNumber numberWithInt:EDT_URATIONAL], @2), @"WhitePoint", + TAGINF(@"013f", [NSNumber numberWithInt:EDT_URATIONAL], @6), @"PrimaryChromaticities", + TAGINF(@"0211", [NSNumber numberWithInt:EDT_URATIONAL], @3), @"YCbCrCoefficients", + TAGINF(@"0213", [NSNumber numberWithInt:EDT_USHORT], @1), @"YCbCrPositioning", + TAGINF(@"0214", [NSNumber numberWithInt:EDT_URATIONAL], @6), @"ReferenceBlackWhite", + TAGINF(@"8298", [NSNumber numberWithInt:EDT_URATIONAL], @0), @"Copyright", + + // offset to exif subifd, we determine this dynamically based on the size of the main exif IFD + TAGINF(@"8769", [NSNumber numberWithInt:EDT_ULONG], @1), @"ExifOffset",*/ + nil]; + + + // supported tages for exif subIFD + SubIFDTagFormatDict = [[NSDictionary alloc] initWithObjectsAndKeys: + //TAGINF(@"9000", [NSNumber numberWithInt:], @), @"ExifVersion", + //TAGINF(@"9202",[NSNumber numberWithInt:EDT_URATIONAL],@1), @"ApertureValue", + //TAGINF(@"9203",[NSNumber numberWithInt:EDT_SRATIONAL],@1), @"BrightnessValue", + TAGINF(@"a001",[NSNumber numberWithInt:EDT_USHORT],@1), @"ColorSpace", + TAGINF_W_APPEND(@"9004",[NSNumber numberWithInt:EDT_ASCII_STRING],@20,@"00"), @"DateTimeDigitized", + TAGINF_W_APPEND(@"9003",[NSNumber numberWithInt:EDT_ASCII_STRING],@20,@"00"), @"DateTimeOriginal", + TAGINF(@"a402", [NSNumber numberWithInt:EDT_USHORT], @1), @"ExposureMode", + TAGINF(@"8822", [NSNumber numberWithInt:EDT_USHORT], @1), @"ExposureProgram", + //TAGINF(@"829a", [NSNumber numberWithInt:EDT_URATIONAL], @1), @"ExposureTime", + //TAGINF(@"829d", [NSNumber numberWithInt:EDT_URATIONAL], @1), @"FNumber", + TAGINF(@"9209", [NSNumber numberWithInt:EDT_USHORT], @1), @"Flash", + // FocalLengthIn35mmFilm + TAGINF(@"a405", [NSNumber numberWithInt:EDT_USHORT], @1), @"FocalLenIn35mmFilm", + //TAGINF(@"920a", [NSNumber numberWithInt:EDT_URATIONAL], @1), @"FocalLength", + //TAGINF(@"8827", [NSNumber numberWithInt:EDT_USHORT], @2), @"ISOSpeedRatings", + TAGINF(@"9207", [NSNumber numberWithInt:EDT_USHORT],@1), @"MeteringMode", + // specific to compressed data + TAGINF(@"a002", [NSNumber numberWithInt:EDT_ULONG],@1), @"PixelXDimension", + TAGINF(@"a003", [NSNumber numberWithInt:EDT_ULONG],@1), @"PixelYDimension", + // data type undefined, but this is a DSC camera, so value is always 1, treat as ushort + TAGINF(@"a301", [NSNumber numberWithInt:EDT_USHORT],@1), @"SceneType", + TAGINF(@"a217",[NSNumber numberWithInt:EDT_USHORT],@1), @"SensingMethod", + //TAGINF(@"9201", [NSNumber numberWithInt:EDT_SRATIONAL], @1), @"ShutterSpeedValue", + // specifies location of main subject in scene (x,y,wdith,height) expressed before rotation processing + //TAGINF(@"9214", [NSNumber numberWithInt:EDT_USHORT], @4), @"SubjectArea", + TAGINF(@"a403", [NSNumber numberWithInt:EDT_USHORT], @1), @"WhiteBalance", + nil]; + return self; +} + +- (NSData*) spliceExifBlockIntoJpeg: (NSData*) jpegdata withExifBlock: (NSString*) exifstr { + + CDVJpegHeaderWriter * exifWriter = [[CDVJpegHeaderWriter alloc] init]; + + NSMutableData * exifdata = [NSMutableData dataWithCapacity: [exifstr length]/2]; + int idx; + for (idx = 0; idx+1 < [exifstr length]; idx+=2) { + NSRange range = NSMakeRange(idx, 2); + NSString* hexStr = [exifstr substringWithRange:range]; + NSScanner* scanner = [NSScanner scannerWithString:hexStr]; + unsigned int intValue; + [scanner scanHexInt:&intValue]; + [exifdata appendBytes:&intValue length:1]; + } + + NSMutableData * ddata = [NSMutableData dataWithCapacity: [jpegdata length]]; + NSMakeRange(0,4); + int loc = 0; + bool done = false; + // read the jpeg data until we encounter the app1==0xFFE1 marker + while (loc+1 < [jpegdata length]) { + NSData * blag = [jpegdata subdataWithRange: NSMakeRange(loc,2)]; + if( [[blag description] isEqualToString : @""]) { + // read the APP1 block size bits + NSString * the = [exifWriter hexStringFromData:[jpegdata subdataWithRange: NSMakeRange(loc+2,2)]]; + NSNumber * app1width = [exifWriter numericFromHexString:the]; + //consume the original app1 block + [ddata appendData:exifdata]; + // advance our loc marker past app1 + loc += [app1width intValue] + 2; + done = true; + } else { + if(!done) { + [ddata appendData:blag]; + loc += 2; + } else { + break; + } + } + } + // copy the remaining data + [ddata appendData:[jpegdata subdataWithRange: NSMakeRange(loc,[jpegdata length]-loc)]]; + return ddata; +} + + + +/** + * Create the Exif data block as a hex string + * jpeg uses Application Markers (APP's) as markers for application data + * APP1 is the application marker reserved for exif data + * + * (NSDictionary*) datadict - with subdictionaries marked '{TIFF}' and '{EXIF}' as returned by imagePickerController with a valid + * didFinishPickingMediaWithInfo data dict, under key @"UIImagePickerControllerMediaMetadata" + * + * the following constructs a hex string to Exif specifications, and is therefore brittle + * altering the order of arguments to the string constructors, modifying field sizes or formats, + * and any other minor change will likely prevent the exif data from being read + */ +- (NSString*) createExifAPP1 : (NSDictionary*) datadict { + NSMutableString * app1; // holds finalized product + NSString * exifIFD; // exif information file directory + NSString * subExifIFD; // subexif information file directory + + // FFE1 is the hex APP1 marker code, and will allow client apps to read the data + NSString * app1marker = @"ffe1"; + // SSSS size, to be determined + // EXIF ascii characters followed by 2bytes of zeros + NSString * exifmarker = @"457869660000"; + // Tiff header: 4d4d is motorolla byte align (big endian), 002a is hex for 42 + NSString * tiffheader = @"4d4d002a"; + //first IFD offset from the Tiff header to IFD0. Since we are writing it, we know it's address 0x08 + NSString * ifd0offset = @"00000008"; + // current offset to next data area + int currentDataOffset = 0; + + //data labeled as TIFF in UIImagePickerControllerMediaMetaData is part of the EXIF IFD0 portion of APP1 + exifIFD = [self createExifIFDFromDict: [datadict objectForKey:@"{TIFF}"] withFormatDict: IFD0TagFormatDict isIFD0:YES currentDataOffset:¤tDataOffset]; + + //data labeled as EXIF in UIImagePickerControllerMediaMetaData is part of the EXIF Sub IFD portion of APP1 + subExifIFD = [self createExifIFDFromDict: [datadict objectForKey:@"{Exif}"] withFormatDict: SubIFDTagFormatDict isIFD0:NO currentDataOffset:¤tDataOffset]; + /* + NSLog(@"SUB EXIF IFD %@ WITH SIZE: %d",exifIFD,[exifIFD length]); + + NSLog(@"SUB EXIF IFD %@ WITH SIZE: %d",subExifIFD,[subExifIFD length]); + */ + // construct the complete app1 data block + app1 = [[NSMutableString alloc] initWithFormat: @"%@%04x%@%@%@%@%@", + app1marker, + 16 + ([exifIFD length]/2) + ([subExifIFD length]/2) /*16+[exifIFD length]/2*/, + exifmarker, + tiffheader, + ifd0offset, + exifIFD, + subExifIFD]; + + return app1; +} + +// returns hex string representing a valid exif information file directory constructed from the datadict and formatdict +- (NSString*) createExifIFDFromDict : (NSDictionary*) datadict + withFormatDict : (NSDictionary*) formatdict + isIFD0 : (BOOL) ifd0flag + currentDataOffset : (int*) dataoffset { + NSArray * datakeys = [datadict allKeys]; // all known data keys + NSArray * knownkeys = [formatdict allKeys]; // only keys in knowkeys are considered for entry in this IFD + NSMutableArray * ifdblock = [[NSMutableArray alloc] initWithCapacity: [datadict count]]; // all ifd entries + NSMutableArray * ifddatablock = [[NSMutableArray alloc] initWithCapacity: [datadict count]]; // data block entries + // ifd0flag = NO; // ifd0 requires a special flag and has offset to next ifd appended to end + + // iterate through known provided data keys + for (int i = 0; i < [datakeys count]; i++) { + NSString * key = [datakeys objectAtIndex:i]; + // don't muck about with unknown keys + if ([knownkeys indexOfObject: key] != NSNotFound) { + // create new IFD entry + NSString * entry = [self createIFDElement: key + withFormat: [formatdict objectForKey:key] + withElementData: [datadict objectForKey:key]]; + // create the IFD entry's data block + NSString * data = [self createIFDElementDataWithFormat: [formatdict objectForKey:key] + withData: [datadict objectForKey:key]]; + if (entry) { + [ifdblock addObject:entry]; + if(!data) { + [ifdblock addObject:@""]; + } else { + [ifddatablock addObject:data]; + } + } + } + } + + NSMutableString * exifstr = [[NSMutableString alloc] initWithCapacity: [ifdblock count] * 24]; + NSMutableString * dbstr = [[NSMutableString alloc] initWithCapacity: 100]; + + int addr=*dataoffset; // current offset/address in datablock + if (ifd0flag) { + // calculate offset to datablock based on ifd file entry count + addr += 14+(12*([ifddatablock count]+1)); // +1 for tag 0x8769, exifsubifd offset + } else { + // current offset + numSubIFDs (2-bytes) + 12*numSubIFDs + endMarker (4-bytes) + addr += 2+(12*[ifddatablock count])+4; + } + + for (int i = 0; i < [ifdblock count]; i++) { + NSString * entry = [ifdblock objectAtIndex:i]; + NSString * data = [ifddatablock objectAtIndex:i]; + + // check if the data fits into 4 bytes + if( [data length] <= 8) { + // concatenate the entry and the (4byte) data entry into the final IFD entry and append to exif ifd string + [exifstr appendFormat : @"%@%@", entry, data]; + } else { + [exifstr appendFormat : @"%@%08x", entry, addr]; + [dbstr appendFormat: @"%@", data]; + addr+= [data length] / 2; + /* + NSLog(@"=====data-length[%i]=======",[data length]); + NSLog(@"addr-offset[%i]",addr); + NSLog(@"entry[%@]",entry); + NSLog(@"data[%@]",data); + */ + } + } + + // calculate IFD0 terminal offset tags, currently ExifSubIFD + int entrycount = [ifdblock count]; + if (ifd0flag) { + // 18 accounts for 8769's width + offset to next ifd, 8 accounts for start of header + NSNumber * offset = [NSNumber numberWithInt:[exifstr length] / 2 + [dbstr length] / 2 + 18+8]; + + [self appendExifOffsetTagTo: exifstr + withOffset : offset]; + entrycount++; + } + *dataoffset = addr; + return [[NSString alloc] initWithFormat: @"%04x%@%@%@", + entrycount, + exifstr, + @"00000000", // offset to next IFD, 0 since there is none + dbstr]; // lastly, the datablock +} + +// Creates an exif formatted exif information file directory entry +- (NSString*) createIFDElement: (NSString*) elementName withFormat: (NSArray*) formtemplate withElementData: (NSString*) data { + //NSArray * fielddata = [formatdict objectForKey: elementName];// format data of desired field + if (formtemplate) { + // format string @"%@%@%@%@", tag number, data format, components, value + NSNumber * dataformat = [formtemplate objectAtIndex:1]; + NSNumber * components = [formtemplate objectAtIndex:2]; + if([components intValue] == 0) { + components = [NSNumber numberWithInt: [data length] * DataTypeToWidth[[dataformat intValue]-1]]; + } + + return [[NSString alloc] initWithFormat: @"%@%@%08x", + [formtemplate objectAtIndex:0], // the field code + [self formatNumberWithLeadingZeroes: dataformat withPlaces: @4], // the data type code + [components intValue]]; // number of components + } + return NULL; +} + +/** + * appends exif IFD0 tag 8769 "ExifOffset" to the string provided + * (NSMutableString*) str - string you wish to append the 8769 tag to: APP1 or IFD0 hex data string + * // TAGINF(@"8769", [NSNumber numberWithInt:EDT_ULONG], @1), @"ExifOffset", + */ +- (void) appendExifOffsetTagTo: (NSMutableString*) str withOffset : (NSNumber*) offset { + NSArray * format = TAGINF(@"8769", [NSNumber numberWithInt:EDT_ULONG], @1); + + NSString * entry = [self createIFDElement: @"ExifOffset" + withFormat: format + withElementData: [offset stringValue]]; + + NSString * data = [self createIFDElementDataWithFormat: format + withData: [offset stringValue]]; + [str appendFormat:@"%@%@", entry, data]; +} + +// formats the Information File Directory Data to exif format +- (NSString*) createIFDElementDataWithFormat: (NSArray*) dataformat withData: (NSString*) data { + NSMutableString * datastr = nil; + NSNumber * tmp = nil; + NSNumber * formatcode = [dataformat objectAtIndex:1]; + NSUInteger formatItemsCount = [dataformat count]; + NSNumber * num = @0; + NSNumber * denom = @0; + + switch ([formatcode intValue]) { + case EDT_UBYTE: + break; + case EDT_ASCII_STRING: + datastr = [[NSMutableString alloc] init]; + for (int i = 0; i < [data length]; i++) { + [datastr appendFormat:@"%02x",[data characterAtIndex:i]]; + } + if (formatItemsCount > 3) { + // We have additional data to append. + // currently used by Date format to append final 0x00 but can be used by other data types as well in the future + [datastr appendString:[dataformat objectAtIndex:3]]; + } + if ([datastr length] < 8) { + NSString * format = [NSString stringWithFormat:@"%%0%dd", 8 - [datastr length]]; + [datastr appendFormat:format,0]; + } + return datastr; + case EDT_USHORT: + return [[NSString alloc] initWithFormat : @"%@%@", + [self formattedHexStringFromDecimalNumber: [NSNumber numberWithInt: [data intValue]] withPlaces: @4], + @"0000"]; + case EDT_ULONG: + tmp = [NSNumber numberWithUnsignedLong:[data intValue]]; + return [NSString stringWithFormat : @"%@", + [self formattedHexStringFromDecimalNumber: tmp withPlaces: @8]]; + case EDT_URATIONAL: + return [self decimalToUnsignedRational: [NSNumber numberWithDouble:[data doubleValue]] + withResultNumerator: &num + withResultDenominator: &denom]; + case EDT_SBYTE: + + break; + case EDT_UNDEFINED: + break; // 8 bits + case EDT_SSHORT: + break; + case EDT_SLONG: + break; // 32bit signed integer (2's complement) + case EDT_SRATIONAL: + break; // 2 SLONGS, first long is numerator, second is denominator + case EDT_SINGLEFLOAT: + break; + case EDT_DOUBLEFLOAT: + break; + } + return datastr; +} + +//====================================================================================================================== +// Utility Methods +//====================================================================================================================== + +// creates a formatted little endian hex string from a number and width specifier +- (NSString*) formattedHexStringFromDecimalNumber: (NSNumber*) numb withPlaces: (NSNumber*) width { + NSMutableString * str = [[NSMutableString alloc] initWithCapacity:[width intValue]]; + NSString * formatstr = [[NSString alloc] initWithFormat: @"%%%@%dx", @"0", [width intValue]]; + [str appendFormat:formatstr, [numb intValue]]; + return str; +} + +// format number as string with leading 0's +- (NSString*) formatNumberWithLeadingZeroes: (NSNumber *) numb withPlaces: (NSNumber *) places { + NSNumberFormatter * formatter = [[NSNumberFormatter alloc] init]; + NSString *formatstr = [@"" stringByPaddingToLength:[places unsignedIntegerValue] withString:@"0" startingAtIndex:0]; + [formatter setPositiveFormat:formatstr]; + return [formatter stringFromNumber:numb]; +} + +// approximate a decimal with a rational by method of continued fraction +// can be collasped into decimalToUnsignedRational after testing +- (void) decimalToRational: (NSNumber *) numb + withResultNumerator: (NSNumber**) numerator + withResultDenominator: (NSNumber**) denominator { + NSMutableArray * fractionlist = [[NSMutableArray alloc] initWithCapacity:8]; + + [self continuedFraction: [numb doubleValue] + withFractionList: fractionlist + withHorizon: 8]; + + // simplify complex fraction represented by partial fraction list + [self expandContinuedFraction: fractionlist + withResultNumerator: numerator + withResultDenominator: denominator]; + +} + +// approximate a decimal with an unsigned rational by method of continued fraction +- (NSString*) decimalToUnsignedRational: (NSNumber *) numb + withResultNumerator: (NSNumber**) numerator + withResultDenominator: (NSNumber**) denominator { + NSMutableArray * fractionlist = [[NSMutableArray alloc] initWithCapacity:8]; + + // generate partial fraction list + [self continuedFraction: [numb doubleValue] + withFractionList: fractionlist + withHorizon: 8]; + + // simplify complex fraction represented by partial fraction list + [self expandContinuedFraction: fractionlist + withResultNumerator: numerator + withResultDenominator: denominator]; + + return [self formatFractionList: fractionlist]; +} + +// recursive implementation of decimal approximation by continued fraction +- (void) continuedFraction: (double) val + withFractionList: (NSMutableArray*) fractionlist + withHorizon: (int) horizon { + int whole; + double remainder; + // 1. split term + [self splitDouble: val withIntComponent: &whole withFloatRemainder: &remainder]; + [fractionlist addObject: [NSNumber numberWithInt:whole]]; + + // 2. calculate reciprocal of remainder + if (!remainder) return; // early exit, exact fraction found, avoids recip/0 + double recip = 1 / remainder; + + // 3. exit condition + if ([fractionlist count] > horizon) { + return; + } + + // 4. recurse + [self continuedFraction:recip withFractionList: fractionlist withHorizon: horizon]; + +} + +// expand continued fraction list, creating a single level rational approximation +-(void) expandContinuedFraction: (NSArray*) fractionlist + withResultNumerator: (NSNumber**) numerator + withResultDenominator: (NSNumber**) denominator { + int i = 0; + int den = 0; + int num = 0; + if ([fractionlist count] == 1) { + *numerator = [NSNumber numberWithInt:[[fractionlist objectAtIndex:0] intValue]]; + *denominator = @1; + return; + } + + //begin at the end of the list + i = [fractionlist count] - 1; + num = 1; + den = [[fractionlist objectAtIndex:i] intValue]; + + while (i > 0) { + int t = [[fractionlist objectAtIndex: i-1] intValue]; + num = t * den + num; + if (i==1) { + break; + } else { + t = num; + num = den; + den = t; + } + i--; + } + // set result parameters values + *numerator = [NSNumber numberWithInt: num]; + *denominator = [NSNumber numberWithInt: den]; +} + +// formats expanded fraction list to string matching exif specification +- (NSString*) formatFractionList: (NSArray *) fractionlist { + NSMutableString * str = [[NSMutableString alloc] initWithCapacity:16]; + + if ([fractionlist count] == 1){ + [str appendFormat: @"%08x00000001", [[fractionlist objectAtIndex:0] intValue]]; + } + return str; +} + +// format rational as +- (NSString*) formatRationalWithNumerator: (NSNumber*) numerator withDenominator: (NSNumber*) denominator asSigned: (Boolean) signedFlag { + NSMutableString * str = [[NSMutableString alloc] initWithCapacity:16]; + if (signedFlag) { + long num = [numerator longValue]; + long den = [denominator longValue]; + [str appendFormat: @"%08lx%08lx", num >= 0 ? num : ~ABS(num) + 1, num >= 0 ? den : ~ABS(den) + 1]; + } else { + [str appendFormat: @"%08lx%08lx", [numerator unsignedLongValue], [denominator unsignedLongValue]]; + } + return str; +} + +// split a floating point number into two integer values representing the left and right side of the decimal +- (void) splitDouble: (double) val withIntComponent: (int*) rightside withFloatRemainder: (double*) leftside { + *rightside = val; // convert numb to int representation, which truncates the decimal portion + *leftside = val - *rightside; +} + + +// +- (NSString*) hexStringFromData : (NSData*) data { + //overflow detection + const unsigned char *dataBuffer = [data bytes]; + return [[NSString alloc] initWithFormat: @"%02x%02x", + (unsigned char)dataBuffer[0], + (unsigned char)dataBuffer[1]]; +} + +// convert a hex string to a number +- (NSNumber*) numericFromHexString : (NSString *) hexstring { + NSScanner * scan = NULL; + unsigned int numbuf= 0; + + scan = [NSScanner scannerWithString:hexstring]; + [scan scanHexInt:&numbuf]; + return [NSNumber numberWithInt:numbuf]; +} + +@end diff --git a/plugins/org.apache.cordova.camera/src/windows8/CameraProxy.js b/plugins/org.apache.cordova.camera/src/windows8/CameraProxy.js new file mode 100644 index 0000000..b63efb3 --- /dev/null +++ b/plugins/org.apache.cordova.camera/src/windows8/CameraProxy.js @@ -0,0 +1,341 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + +/*global Windows:true, URL:true */ + + +var cordova = require('cordova'), + Camera = require('./Camera'), + FileEntry = require('org.apache.cordova.file.FileEntry'), + FileError = require('org.apache.cordova.file.FileError'), + FileReader = require('org.apache.cordova.file.FileReader'); + +module.exports = { + + // args will contain : + // ... it is an array, so be careful + // 0 quality:50, + // 1 destinationType:Camera.DestinationType.FILE_URI, + // 2 sourceType:Camera.PictureSourceType.CAMERA, + // 3 targetWidth:-1, + // 4 targetHeight:-1, + // 5 encodingType:Camera.EncodingType.JPEG, + // 6 mediaType:Camera.MediaType.PICTURE, + // 7 allowEdit:false, + // 8 correctOrientation:false, + // 9 saveToPhotoAlbum:false, + // 10 popoverOptions:null + + takePicture: function (successCallback, errorCallback, args) { + var encodingType = args[5]; + var targetWidth = args[3]; + var targetHeight = args[4]; + var sourceType = args[2]; + var destinationType = args[1]; + var mediaType = args[6]; + var saveToPhotoAlbum = args[9]; + + var pkg = Windows.ApplicationModel.Package.current; + var packageId = pkg.installedLocation; + + var fail = function (fileError) { + errorCallback("FileError, code:" + fileError.code); + }; + + // resize method :) + var resizeImage = function (file) { + var tempPhotoFileName = ""; + if (encodingType == Camera.EncodingType.PNG) { + tempPhotoFileName = "camera_cordova_temp_return.png"; + } else { + tempPhotoFileName = "camera_cordova_temp_return.jpg"; + } + var imgObj = new Image(); + var success = function (fileEntry) { + var successCB = function (filePhoto) { + var fileType = file.contentType, + reader = new FileReader(); + reader.onloadend = function () { + var image = new Image(); + image.src = reader.result; + image.onload = function () { + var imageWidth = targetWidth, + imageHeight = targetHeight; + var canvas = document.createElement('canvas'); + + canvas.width = imageWidth; + canvas.height = imageHeight; + + var ctx = canvas.getContext("2d"); + ctx.drawImage(this, 0, 0, imageWidth, imageHeight); + + // The resized file ready for upload + var _blob = canvas.msToBlob(); + var _stream = _blob.msDetachStream(); + Windows.Storage.StorageFolder.getFolderFromPathAsync(packageId.path).done(function (storageFolder) { + storageFolder.createFileAsync(tempPhotoFileName, Windows.Storage.CreationCollisionOption.generateUniqueName).done(function (file) { + file.openAsync(Windows.Storage.FileAccessMode.readWrite).done(function (fileStream) { + Windows.Storage.Streams.RandomAccessStream.copyAndCloseAsync(_stream, fileStream).done(function () { + var _imageUrl = URL.createObjectURL(file); + successCallback(_imageUrl); + }, function () { errorCallback("Resize picture error."); }); + }, function () { errorCallback("Resize picture error."); }); + }, function () { errorCallback("Resize picture error."); }); + }); + }; + }; + + reader.readAsDataURL(filePhoto); + }; + + var failCB = function () { + errorCallback("File not found."); + }; + fileEntry.file(successCB, failCB); + }; + + Windows.Storage.StorageFolder.getFolderFromPathAsync(packageId.path).done(function (storageFolder) { + file.copyAsync(storageFolder, file.name, Windows.Storage.NameCollisionOption.replaceExisting).then(function (storageFile) { + success(new FileEntry(storageFile.name, storageFile.path)); + }, function () { + fail(FileError.INVALID_MODIFICATION_ERR); + }, function () { + errorCallback("Folder not access."); + }); + }); + + }; + + // because of asynchronous method, so let the successCallback be called in it. + var resizeImageBase64 = function (file) { + var imgObj = new Image(); + var success = function (fileEntry) { + var successCB = function (filePhoto) { + var fileType = file.contentType, + reader = new FileReader(); + reader.onloadend = function () { + var image = new Image(); + image.src = reader.result; + + image.onload = function () { + var imageWidth = targetWidth, + imageHeight = targetHeight; + var canvas = document.createElement('canvas'); + + canvas.width = imageWidth; + canvas.height = imageHeight; + + var ctx = canvas.getContext("2d"); + ctx.drawImage(this, 0, 0, imageWidth, imageHeight); + + // The resized file ready for upload + var finalFile = canvas.toDataURL(fileType); + + // Remove the prefix such as "data:" + contentType + ";base64," , in order to meet the Cordova API. + var arr = finalFile.split(","); + var newStr = finalFile.substr(arr[0].length + 1); + successCallback(newStr); + }; + }; + + reader.readAsDataURL(filePhoto); + + }; + var failCB = function () { + errorCallback("File not found."); + }; + fileEntry.file(successCB, failCB); + }; + + Windows.Storage.StorageFolder.getFolderFromPathAsync(packageId.path).done(function (storageFolder) { + file.copyAsync(storageFolder, file.name, Windows.Storage.NameCollisionOption.replaceExisting).then(function (storageFile) { + success(new FileEntry(storageFile.name, storageFile.path)); + }, function () { + fail(FileError.INVALID_MODIFICATION_ERR); + }, function () { + errorCallback("Folder not access."); + }); + }); + + }; + + if (sourceType != Camera.PictureSourceType.CAMERA) { + var fileOpenPicker = new Windows.Storage.Pickers.FileOpenPicker(); + fileOpenPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.picturesLibrary; + if (mediaType == Camera.MediaType.PICTURE) { + fileOpenPicker.fileTypeFilter.replaceAll([".png", ".jpg", ".jpeg"]); + } else if (mediaType == Camera.MediaType.VIDEO) { + fileOpenPicker.fileTypeFilter.replaceAll([".avi", ".flv", ".asx", ".asf", ".mov", ".mp4", ".mpg", ".rm", ".srt", ".swf", ".wmv", ".vob"]); + } else { + fileOpenPicker.fileTypeFilter.replaceAll(["*"]); + } + + fileOpenPicker.pickSingleFileAsync().then(function (file) { + if (file) { + if (destinationType == Camera.DestinationType.FILE_URI) { + if (targetHeight > 0 && targetWidth > 0) { + resizeImage(file); + } else { + Windows.Storage.StorageFolder.getFolderFromPathAsync(packageId.path).done(function (storageFolder) { + file.copyAsync(storageFolder, file.name, Windows.Storage.NameCollisionOption.replaceExisting).then(function (storageFile) { + var _imageUrl = URL.createObjectURL(storageFile); + successCallback(_imageUrl); + }, function () { + fail(FileError.INVALID_MODIFICATION_ERR); + }, function () { + errorCallback("Folder not access."); + }); + }); + + } + } + else { + if (targetHeight > 0 && targetWidth > 0) { + resizeImageBase64(file); + } else { + Windows.Storage.FileIO.readBufferAsync(file).done(function (buffer) { + var strBase64 = Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer); + successCallback(strBase64); + }); + } + + } + + } else { + errorCallback("User didn't choose a file."); + } + }, function () { + errorCallback("User didn't choose a file."); + }); + } + else { + + var cameraCaptureUI = new Windows.Media.Capture.CameraCaptureUI(); + cameraCaptureUI.photoSettings.allowCropping = true; + var allowCrop = !!args[7]; + if (!allowCrop) { + cameraCaptureUI.photoSettings.allowCropping = false; + } + + if (encodingType == Camera.EncodingType.PNG) { + cameraCaptureUI.photoSettings.format = Windows.Media.Capture.CameraCaptureUIPhotoFormat.png; + } else { + cameraCaptureUI.photoSettings.format = Windows.Media.Capture.CameraCaptureUIPhotoFormat.jpeg; + } + // decide which max pixels should be supported by targetWidth or targetHeight. + if (targetWidth >= 1280 || targetHeight >= 960) { + cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.large3M; + } else if (targetWidth >= 1024 || targetHeight >= 768) { + cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.mediumXga; + } else if (targetWidth >= 800 || targetHeight >= 600) { + cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.mediumXga; + } else if (targetWidth >= 640 || targetHeight >= 480) { + cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.smallVga; + } else if (targetWidth >= 320 || targetHeight >= 240) { + cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.verySmallQvga; + } else { + cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.highestAvailable; + } + + cameraCaptureUI.captureFileAsync(Windows.Media.Capture.CameraCaptureUIMode.photo).then(function (picture) { + if (picture) { + // save to photo album successCallback + var success = function (fileEntry) { + if (destinationType == Camera.DestinationType.FILE_URI) { + if (targetHeight > 0 && targetWidth > 0) { + resizeImage(picture); + } else { + Windows.Storage.StorageFolder.getFolderFromPathAsync(packageId.path).done(function (storageFolder) { + picture.copyAsync(storageFolder, picture.name, Windows.Storage.NameCollisionOption.replaceExisting).then(function (storageFile) { + var _imageUrl = URL.createObjectURL(storageFile); + successCallback(_imageUrl); + }, function () { + fail(FileError.INVALID_MODIFICATION_ERR); + }, function () { + errorCallback("Folder not access."); + }); + }); + } + } else { + if (targetHeight > 0 && targetWidth > 0) { + resizeImageBase64(picture); + } else { + Windows.Storage.FileIO.readBufferAsync(picture).done(function (buffer) { + var strBase64 = Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer); + successCallback(strBase64); + }); + } + } + }; + // save to photo album errorCallback + var fail = function () { + //errorCallback("FileError, code:" + fileError.code); + errorCallback("Save fail."); + }; + + if (saveToPhotoAlbum) { + Windows.Storage.StorageFile.getFileFromPathAsync(picture.path).then(function (storageFile) { + storageFile.copyAsync(Windows.Storage.KnownFolders.picturesLibrary, picture.name, Windows.Storage.NameCollisionOption.generateUniqueName).then(function (storageFile) { + success(storageFile); + }, function () { + fail(); + }); + }); + //var directory = new DirectoryEntry("Pictures", parentPath); + //new FileEntry(picture.name, picture.path).copyTo(directory, null, success, fail); + } else { + if (destinationType == Camera.DestinationType.FILE_URI) { + if (targetHeight > 0 && targetWidth > 0) { + resizeImage(picture); + } else { + Windows.Storage.StorageFolder.getFolderFromPathAsync(packageId.path).done(function (storageFolder) { + picture.copyAsync(storageFolder, picture.name, Windows.Storage.NameCollisionOption.replaceExisting).then(function (storageFile) { + var _imageUrl = URL.createObjectURL(storageFile); + successCallback(_imageUrl); + }, function () { + fail(FileError.INVALID_MODIFICATION_ERR); + }, function () { + errorCallback("Folder not access."); + }); + }); + } + } else { + if (targetHeight > 0 && targetWidth > 0) { + resizeImageBase64(picture); + } else { + Windows.Storage.FileIO.readBufferAsync(picture).done(function (buffer) { + var strBase64 = Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer); + successCallback(strBase64); + }); + } + } + } + } else { + errorCallback("User didn't capture a photo."); + } + }, function () { + errorCallback("Fail to capture a photo."); + }); + } + } +}; + +require("cordova/windows8/commandProxy").add("Camera",module.exports); diff --git a/plugins/org.apache.cordova.camera/src/wp/Camera.cs b/plugins/org.apache.cordova.camera/src/wp/Camera.cs new file mode 100644 index 0000000..5ff8045 --- /dev/null +++ b/plugins/org.apache.cordova.camera/src/wp/Camera.cs @@ -0,0 +1,490 @@ +/* + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Collections.Generic; +using Microsoft.Phone.Tasks; +using System.Runtime.Serialization; +using System.IO; +using System.IO.IsolatedStorage; +using System.Windows.Media.Imaging; +using Microsoft.Phone; +using Microsoft.Xna.Framework.Media; +using System.Diagnostics; + +namespace WPCordovaClassLib.Cordova.Commands +{ + public class Camera : BaseCommand + { + + /// + /// Return base64 encoded string + /// + private const int DATA_URL = 0; + + /// + /// Return file uri + /// + private const int FILE_URI = 1; + + /// + /// Choose image from picture library + /// + private const int PHOTOLIBRARY = 0; + + /// + /// Take picture from camera + /// + + private const int CAMERA = 1; + + /// + /// Choose image from picture library + /// + private const int SAVEDPHOTOALBUM = 2; + + /// + /// Take a picture of type JPEG + /// + private const int JPEG = 0; + + /// + /// Take a picture of type PNG + /// + private const int PNG = 1; + + /// + /// Folder to store captured images + /// + private const string isoFolder = "CapturedImagesCache"; + + /// + /// Represents captureImage action options. + /// + [DataContract] + public class CameraOptions + { + /// + /// Source to getPicture from. + /// + [DataMember(IsRequired = false, Name = "sourceType")] + public int PictureSourceType { get; set; } + + /// + /// Format of image that returned from getPicture. + /// + [DataMember(IsRequired = false, Name = "destinationType")] + public int DestinationType { get; set; } + + /// + /// Quality of saved image + /// + [DataMember(IsRequired = false, Name = "quality")] + public int Quality { get; set; } + + /// + /// Controls whether or not the image is also added to the device photo album. + /// + [DataMember(IsRequired = false, Name = "saveToPhotoAlbum")] + public bool SaveToPhotoAlbum { get; set; } + + /// + /// Ignored + /// + [DataMember(IsRequired = false, Name = "correctOrientation")] + public bool CorrectOrientation { get; set; } + + + + /// + /// Ignored + /// + [DataMember(IsRequired = false, Name = "allowEdit")] + public bool AllowEdit { get; set; } + + /// + /// Height in pixels to scale image + /// + [DataMember(IsRequired = false, Name = "encodingType")] + public int EncodingType { get; set; } + + /// + /// Height in pixels to scale image + /// + [DataMember(IsRequired = false, Name = "mediaType")] + public int MediaType { get; set; } + + + /// + /// Height in pixels to scale image + /// + [DataMember(IsRequired = false, Name = "targetHeight")] + public int TargetHeight { get; set; } + + + /// + /// Width in pixels to scale image + /// + [DataMember(IsRequired = false, Name = "targetWidth")] + public int TargetWidth { get; set; } + + /// + /// Creates options object with default parameters + /// + public CameraOptions() + { + this.SetDefaultValues(new StreamingContext()); + } + + /// + /// Initializes default values for class fields. + /// Implemented in separate method because default constructor is not invoked during deserialization. + /// + /// + [OnDeserializing()] + public void SetDefaultValues(StreamingContext context) + { + PictureSourceType = CAMERA; + DestinationType = FILE_URI; + Quality = 80; + TargetHeight = -1; + TargetWidth = -1; + SaveToPhotoAlbum = false; + CorrectOrientation = true; + AllowEdit = false; + MediaType = -1; + EncodingType = -1; + } + } + + /// + /// Used to open photo library + /// + PhotoChooserTask photoChooserTask; + + /// + /// Used to open camera application + /// + CameraCaptureTask cameraTask; + + /// + /// Camera options + /// + CameraOptions cameraOptions; + + public void takePicture(string options) + { + try + { + string[] args = JSON.JsonHelper.Deserialize(options); + // ["quality", "destinationType", "sourceType", "targetWidth", "targetHeight", "encodingType", + // "mediaType", "allowEdit", "correctOrientation", "saveToPhotoAlbum" ] + this.cameraOptions = new CameraOptions(); + this.cameraOptions.Quality = int.Parse(args[0]); + this.cameraOptions.DestinationType = int.Parse(args[1]); + this.cameraOptions.PictureSourceType = int.Parse(args[2]); + this.cameraOptions.TargetWidth = int.Parse(args[3]); + this.cameraOptions.TargetHeight = int.Parse(args[4]); + this.cameraOptions.EncodingType = int.Parse(args[5]); + this.cameraOptions.MediaType = int.Parse(args[6]); + this.cameraOptions.AllowEdit = bool.Parse(args[7]); + this.cameraOptions.CorrectOrientation = bool.Parse(args[8]); + this.cameraOptions.SaveToPhotoAlbum = bool.Parse(args[9]); + + //this.cameraOptions = String.IsNullOrEmpty(options) ? + // new CameraOptions() : JSON.JsonHelper.Deserialize(options); + } + catch (Exception ex) + { + this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message)); + return; + } + + //TODO Check if all the options are acceptable + + + if (cameraOptions.PictureSourceType == CAMERA) + { + cameraTask = new CameraCaptureTask(); + cameraTask.Completed += onCameraTaskCompleted; + cameraTask.Show(); + } + else + { + if ((cameraOptions.PictureSourceType == PHOTOLIBRARY) || (cameraOptions.PictureSourceType == SAVEDPHOTOALBUM)) + { + photoChooserTask = new PhotoChooserTask(); + photoChooserTask.Completed += onPickerTaskCompleted; + photoChooserTask.Show(); + } + else + { + DispatchCommandResult(new PluginResult(PluginResult.Status.NO_RESULT)); + } + } + + } + + public void onCameraTaskCompleted(object sender, PhotoResult e) + { + if (e.Error != null) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR)); + return; + } + + switch (e.TaskResult) + { + case TaskResult.OK: + try + { + string imagePathOrContent = string.Empty; + + if (cameraOptions.DestinationType == FILE_URI) + { + // Save image in media library + if (cameraOptions.SaveToPhotoAlbum) + { + MediaLibrary library = new MediaLibrary(); + Picture pict = library.SavePicture(e.OriginalFileName, e.ChosenPhoto); // to save to photo-roll ... + } + + int orient = ImageExifHelper.getImageOrientationFromStream(e.ChosenPhoto); + int newAngle = 0; + switch (orient) + { + case ImageExifOrientation.LandscapeLeft: + newAngle = 90; + break; + case ImageExifOrientation.PortraitUpsideDown: + newAngle = 180; + break; + case ImageExifOrientation.LandscapeRight: + newAngle = 270; + break; + case ImageExifOrientation.Portrait: + default: break; // 0 default already set + } + + Stream rotImageStream = ImageExifHelper.RotateStream(e.ChosenPhoto, newAngle); + + // we should return stream position back after saving stream to media library + rotImageStream.Seek(0, SeekOrigin.Begin); + + WriteableBitmap image = PictureDecoder.DecodeJpeg(rotImageStream); + + imagePathOrContent = this.SaveImageToLocalStorage(image, Path.GetFileName(e.OriginalFileName)); + + + } + else if (cameraOptions.DestinationType == DATA_URL) + { + imagePathOrContent = this.GetImageContent(e.ChosenPhoto); + } + else + { + // TODO: shouldn't this happen before we launch the camera-picker? + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Incorrect option: destinationType")); + return; + } + + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, imagePathOrContent)); + + } + catch (Exception) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error retrieving image.")); + } + break; + + case TaskResult.Cancel: + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection cancelled.")); + break; + + default: + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection did not complete!")); + break; + } + + } + + public void onPickerTaskCompleted(object sender, PhotoResult e) + { + if (e.Error != null) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR)); + return; + } + + switch (e.TaskResult) + { + case TaskResult.OK: + try + { + string imagePathOrContent = string.Empty; + + if (cameraOptions.DestinationType == FILE_URI) + { + WriteableBitmap image = PictureDecoder.DecodeJpeg(e.ChosenPhoto); + imagePathOrContent = this.SaveImageToLocalStorage(image, Path.GetFileName(e.OriginalFileName)); + } + else if (cameraOptions.DestinationType == DATA_URL) + { + imagePathOrContent = this.GetImageContent(e.ChosenPhoto); + + } + else + { + // TODO: shouldn't this happen before we launch the camera-picker? + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Incorrect option: destinationType")); + return; + } + + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, imagePathOrContent)); + + } + catch (Exception) + { + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error retrieving image.")); + } + break; + + case TaskResult.Cancel: + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection cancelled.")); + break; + + default: + DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection did not complete!")); + break; + } + } + + /// + /// Returns image content in a form of base64 string + /// + /// Image stream + /// Base64 representation of the image + private string GetImageContent(Stream stream) + { + int streamLength = (int)stream.Length; + byte[] fileData = new byte[streamLength + 1]; + stream.Read(fileData, 0, streamLength); + + //use photo's actual width & height if user doesn't provide width & height + if (cameraOptions.TargetWidth < 0 && cameraOptions.TargetHeight < 0) + { + stream.Close(); + return Convert.ToBase64String(fileData); + } + else + { + // resize photo + byte[] resizedFile = ResizePhoto(stream, fileData); + stream.Close(); + return Convert.ToBase64String(resizedFile); + } + } + + /// + /// Resize image + /// + /// Image stream + /// File data + /// resized image + private byte[] ResizePhoto(Stream stream, byte[] fileData) + { + int streamLength = (int)stream.Length; + int intResult = 0; + + byte[] resizedFile; + + stream.Read(fileData, 0, streamLength); + + BitmapImage objBitmap = new BitmapImage(); + MemoryStream objBitmapStream = new MemoryStream(fileData); + MemoryStream objBitmapStreamResized = new MemoryStream(); + WriteableBitmap objWB; + objBitmap.SetSource(stream); + objWB = new WriteableBitmap(objBitmap); + + // resize the photo with user defined TargetWidth & TargetHeight + Extensions.SaveJpeg(objWB, objBitmapStreamResized, cameraOptions.TargetWidth, cameraOptions.TargetHeight, 0, cameraOptions.Quality); + + //Convert the resized stream to a byte array. + streamLength = (int)objBitmapStreamResized.Length; + resizedFile = new Byte[streamLength]; //-1 + objBitmapStreamResized.Position = 0; + //for some reason we have to set Position to zero, but we don't have to earlier when we get the bytes from the chosen photo... + intResult = objBitmapStreamResized.Read(resizedFile, 0, streamLength); + + return resizedFile; + } + + /// + /// Saves captured image in isolated storage + /// + /// image file name + /// Image path + private string SaveImageToLocalStorage(WriteableBitmap image, string imageFileName) + { + + if (image == null) + { + throw new ArgumentNullException("imageBytes"); + } + try + { + + + var isoFile = IsolatedStorageFile.GetUserStoreForApplication(); + + if (!isoFile.DirectoryExists(isoFolder)) + { + isoFile.CreateDirectory(isoFolder); + } + + string filePath = System.IO.Path.Combine("///" + isoFolder + "/", imageFileName); + + using (var stream = isoFile.CreateFile(filePath)) + { + // resize image if Height and Width defined via options + if (cameraOptions.TargetHeight > 0 && cameraOptions.TargetWidth > 0) + { + image.SaveJpeg(stream, cameraOptions.TargetWidth, cameraOptions.TargetHeight, 0, cameraOptions.Quality); + } + else + { + image.SaveJpeg(stream, image.PixelWidth, image.PixelHeight, 0, cameraOptions.Quality); + } + } + + return new Uri(filePath, UriKind.Relative).ToString(); + } + catch (Exception) + { + //TODO: log or do something else + throw; + } + } + + } +} diff --git a/plugins/org.apache.cordova.camera/test/autotest/html/HtmlReporter.js b/plugins/org.apache.cordova.camera/test/autotest/html/HtmlReporter.js new file mode 100644 index 0000000..7d9d924 --- /dev/null +++ b/plugins/org.apache.cordova.camera/test/autotest/html/HtmlReporter.js @@ -0,0 +1,101 @@ +jasmine.HtmlReporter = function(_doc) { + var self = this; + var doc = _doc || window.document; + + var reporterView; + + var dom = {}; + + // Jasmine Reporter Public Interface + self.logRunningSpecs = false; + + self.reportRunnerStarting = function(runner) { + var specs = runner.specs() || []; + + if (specs.length == 0) { + return; + } + + createReporterDom(runner.env.versionString()); + doc.body.appendChild(dom.reporter); + + reporterView = new jasmine.HtmlReporter.ReporterView(dom); + reporterView.addSpecs(specs, self.specFilter); + }; + + self.reportRunnerResults = function(runner) { + reporterView && reporterView.complete(); + }; + + self.reportSuiteResults = function(suite) { + reporterView.suiteComplete(suite); + }; + + self.reportSpecStarting = function(spec) { + if (self.logRunningSpecs) { + self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); + } + }; + + self.reportSpecResults = function(spec) { + reporterView.specComplete(spec); + }; + + self.log = function() { + var console = jasmine.getGlobal().console; + if (console && console.log) { + if (console.log.apply) { + console.log.apply(console, arguments); + } else { + console.log(arguments); // ie fix: console.log.apply doesn't exist on ie + } + } + }; + + self.specFilter = function(spec) { + if (!focusedSpecName()) { + return true; + } + + return spec.getFullName().indexOf(focusedSpecName()) === 0; + }; + + return self; + + function focusedSpecName() { + var specName; + + (function memoizeFocusedSpec() { + if (specName) { + return; + } + + var paramMap = []; + var params = doc.location.search.substring(1).split('&'); + + for (var i = 0; i < params.length; i++) { + var p = params[i].split('='); + paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); + } + + specName = paramMap.spec; + })(); + + return specName; + } + + function createReporterDom(version) { + dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' }, + dom.banner = self.createDom('div', { className: 'banner' }, + self.createDom('span', { className: 'title' }, "Jasmine "), + self.createDom('span', { className: 'version' }, version)), + + dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}), + dom.alert = self.createDom('div', {className: 'alert'}), + dom.results = self.createDom('div', {className: 'results'}, + dom.summary = self.createDom('div', { className: 'summary' }), + dom.details = self.createDom('div', { id: 'details' })) + ); + } +}; +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter); diff --git a/plugins/org.apache.cordova.camera/test/autotest/html/HtmlReporterHelpers.js b/plugins/org.apache.cordova.camera/test/autotest/html/HtmlReporterHelpers.js new file mode 100644 index 0000000..745e1e0 --- /dev/null +++ b/plugins/org.apache.cordova.camera/test/autotest/html/HtmlReporterHelpers.js @@ -0,0 +1,60 @@ +jasmine.HtmlReporterHelpers = {}; + +jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) { + var el = document.createElement(type); + + for (var i = 2; i < arguments.length; i++) { + var child = arguments[i]; + + if (typeof child === 'string') { + el.appendChild(document.createTextNode(child)); + } else { + if (child) { + el.appendChild(child); + } + } + } + + for (var attr in attrs) { + if (attr == "className") { + el[attr] = attrs[attr]; + } else { + el.setAttribute(attr, attrs[attr]); + } + } + + return el; +}; + +jasmine.HtmlReporterHelpers.getSpecStatus = function(child) { + var results = child.results(); + var status = results.passed() ? 'passed' : 'failed'; + if (results.skipped) { + status = 'skipped'; + } + + return status; +}; + +jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) { + var parentDiv = this.dom.summary; + var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite'; + var parent = child[parentSuite]; + + if (parent) { + if (typeof this.views.suites[parent.id] == 'undefined') { + this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views); + } + parentDiv = this.views.suites[parent.id].element; + } + + parentDiv.appendChild(childElement); +}; + + +jasmine.HtmlReporterHelpers.addHelpers = function(ctor) { + for(var fn in jasmine.HtmlReporterHelpers) { + ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn]; + } +}; + diff --git a/plugins/org.apache.cordova.camera/test/autotest/html/ReporterView.js b/plugins/org.apache.cordova.camera/test/autotest/html/ReporterView.js new file mode 100644 index 0000000..6a6d005 --- /dev/null +++ b/plugins/org.apache.cordova.camera/test/autotest/html/ReporterView.js @@ -0,0 +1,164 @@ +jasmine.HtmlReporter.ReporterView = function(dom) { + this.startedAt = new Date(); + this.runningSpecCount = 0; + this.completeSpecCount = 0; + this.passedCount = 0; + this.failedCount = 0; + this.skippedCount = 0; + + this.createResultsMenu = function() { + this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'}, + this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'), + ' | ', + this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing')); + + this.summaryMenuItem.onclick = function() { + dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, ''); + }; + + this.detailsMenuItem.onclick = function() { + showDetails(); + }; + }; + + this.addSpecs = function(specs, specFilter) { + this.totalSpecCount = specs.length; + + this.views = { + specs: {}, + suites: {} + }; + + for (var i = 0; i < specs.length; i++) { + var spec = specs[i]; + this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views); + if (specFilter(spec)) { + this.runningSpecCount++; + } + } + }; + + this.specComplete = function(spec) { + this.completeSpecCount++; + + if (isUndefined(this.views.specs[spec.id])) { + this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom); + } + + var specView = this.views.specs[spec.id]; + + switch (specView.status()) { + case 'passed': + this.passedCount++; + break; + + case 'failed': + this.failedCount++; + break; + + case 'skipped': + this.skippedCount++; + break; + } + + specView.refresh(); + this.refresh(); + }; + + this.suiteComplete = function(suite) { + var suiteView = this.views.suites[suite.id]; + if (isUndefined(suiteView)) { + return; + } + suiteView.refresh(); + }; + + this.refresh = function() { + + if (isUndefined(this.resultsMenu)) { + this.createResultsMenu(); + } + + // currently running UI + if (isUndefined(this.runningAlert)) { + this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"}); + dom.alert.appendChild(this.runningAlert); + } + this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount); + + // skipped specs UI + if (isUndefined(this.skippedAlert)) { + this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"}); + } + + this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; + + if (this.skippedCount === 1 && isDefined(dom.alert)) { + dom.alert.appendChild(this.skippedAlert); + } + + // passing specs UI + if (isUndefined(this.passedAlert)) { + this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"}); + } + this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount); + + // failing specs UI + if (isUndefined(this.failedAlert)) { + this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"}); + } + this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount); + + if (this.failedCount === 1 && isDefined(dom.alert)) { + dom.alert.appendChild(this.failedAlert); + dom.alert.appendChild(this.resultsMenu); + } + + // summary info + this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount); + this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing"; + }; + + this.complete = function() { + dom.alert.removeChild(this.runningAlert); + + this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; + + if (this.failedCount === 0) { + dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount))); + } else { + showDetails(); + } + + dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s")); + }; + + return this; + + function showDetails() { + if (dom.reporter.className.search(/showDetails/) === -1) { + dom.reporter.className += " showDetails"; + } + } + + function isUndefined(obj) { + return typeof obj === 'undefined'; + } + + function isDefined(obj) { + return !isUndefined(obj); + } + + function specPluralizedFor(count) { + var str = count + " spec"; + if (count > 1) { + str += "s" + } + return str; + } + +}; + +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView); + + diff --git a/plugins/org.apache.cordova.camera/test/autotest/html/SpecView.js b/plugins/org.apache.cordova.camera/test/autotest/html/SpecView.js new file mode 100644 index 0000000..e8a3c23 --- /dev/null +++ b/plugins/org.apache.cordova.camera/test/autotest/html/SpecView.js @@ -0,0 +1,79 @@ +jasmine.HtmlReporter.SpecView = function(spec, dom, views) { + this.spec = spec; + this.dom = dom; + this.views = views; + + this.symbol = this.createDom('li', { className: 'pending' }); + this.dom.symbolSummary.appendChild(this.symbol); + + this.summary = this.createDom('div', { className: 'specSummary' }, + this.createDom('a', { + className: 'description', + href: '?spec=' + encodeURIComponent(this.spec.getFullName()), + title: this.spec.getFullName() + }, this.spec.description) + ); + + this.detail = this.createDom('div', { className: 'specDetail' }, + this.createDom('a', { + className: 'description', + href: '?spec=' + encodeURIComponent(this.spec.getFullName()), + title: this.spec.getFullName() + }, this.spec.getFullName()) + ); +}; + +jasmine.HtmlReporter.SpecView.prototype.status = function() { + return this.getSpecStatus(this.spec); +}; + +jasmine.HtmlReporter.SpecView.prototype.refresh = function() { + this.symbol.className = this.status(); + + switch (this.status()) { + case 'skipped': + break; + + case 'passed': + this.appendSummaryToSuiteDiv(); + break; + + case 'failed': + this.appendSummaryToSuiteDiv(); + this.appendFailureDetail(); + break; + } +}; + +jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() { + this.summary.className += ' ' + this.status(); + this.appendToSummary(this.spec, this.summary); +}; + +jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() { + this.detail.className += ' ' + this.status(); + + var resultItems = this.spec.results().getItems(); + var messagesDiv = this.createDom('div', { className: 'messages' }); + + for (var i = 0; i < resultItems.length; i++) { + var result = resultItems[i]; + + if (result.type == 'log') { + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); + } else if (result.type == 'expect' && result.passed && !result.passed()) { + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); + + if (result.trace.stack) { + messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); + } + } + } + + if (messagesDiv.childNodes.length > 0) { + this.detail.appendChild(messagesDiv); + this.dom.details.appendChild(this.detail); + } +}; + +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView); diff --git a/plugins/org.apache.cordova.camera/test/autotest/html/SuiteView.js b/plugins/org.apache.cordova.camera/test/autotest/html/SuiteView.js new file mode 100644 index 0000000..19a1efa --- /dev/null +++ b/plugins/org.apache.cordova.camera/test/autotest/html/SuiteView.js @@ -0,0 +1,22 @@ +jasmine.HtmlReporter.SuiteView = function(suite, dom, views) { + this.suite = suite; + this.dom = dom; + this.views = views; + + this.element = this.createDom('div', { className: 'suite' }, + this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description) + ); + + this.appendToSummary(this.suite, this.element); +}; + +jasmine.HtmlReporter.SuiteView.prototype.status = function() { + return this.getSpecStatus(this.suite); +}; + +jasmine.HtmlReporter.SuiteView.prototype.refresh = function() { + this.element.className += " " + this.status(); +}; + +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView); + diff --git a/plugins/org.apache.cordova.camera/test/autotest/html/TrivialReporter.js b/plugins/org.apache.cordova.camera/test/autotest/html/TrivialReporter.js new file mode 100644 index 0000000..167ac50 --- /dev/null +++ b/plugins/org.apache.cordova.camera/test/autotest/html/TrivialReporter.js @@ -0,0 +1,192 @@ +/* @deprecated Use jasmine.HtmlReporter instead + */ +jasmine.TrivialReporter = function(doc) { + this.document = doc || document; + this.suiteDivs = {}; + this.logRunningSpecs = false; +}; + +jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) { + var el = document.createElement(type); + + for (var i = 2; i < arguments.length; i++) { + var child = arguments[i]; + + if (typeof child === 'string') { + el.appendChild(document.createTextNode(child)); + } else { + if (child) { el.appendChild(child); } + } + } + + for (var attr in attrs) { + if (attr == "className") { + el[attr] = attrs[attr]; + } else { + el.setAttribute(attr, attrs[attr]); + } + } + + return el; +}; + +jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) { + var showPassed, showSkipped; + + this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' }, + this.createDom('div', { className: 'banner' }, + this.createDom('div', { className: 'logo' }, + this.createDom('span', { className: 'title' }, "Jasmine"), + this.createDom('span', { className: 'version' }, runner.env.versionString())), + this.createDom('div', { className: 'options' }, + "Show ", + showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }), + this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "), + showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }), + this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped") + ) + ), + + this.runnerDiv = this.createDom('div', { className: 'runner running' }, + this.createDom('a', { className: 'run_spec', href: '?' }, "run all"), + this.runnerMessageSpan = this.createDom('span', {}, "Running..."), + this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, "")) + ); + + this.document.body.appendChild(this.outerDiv); + + var suites = runner.suites(); + for (var i = 0; i < suites.length; i++) { + var suite = suites[i]; + var suiteDiv = this.createDom('div', { className: 'suite' }, + this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"), + this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description)); + this.suiteDivs[suite.id] = suiteDiv; + var parentDiv = this.outerDiv; + if (suite.parentSuite) { + parentDiv = this.suiteDivs[suite.parentSuite.id]; + } + parentDiv.appendChild(suiteDiv); + } + + this.startedAt = new Date(); + + var self = this; + showPassed.onclick = function(evt) { + if (showPassed.checked) { + self.outerDiv.className += ' show-passed'; + } else { + self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, ''); + } + }; + + showSkipped.onclick = function(evt) { + if (showSkipped.checked) { + self.outerDiv.className += ' show-skipped'; + } else { + self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, ''); + } + }; +}; + +jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) { + var results = runner.results(); + var className = (results.failedCount > 0) ? "runner failed" : "runner passed"; + this.runnerDiv.setAttribute("class", className); + //do it twice for IE + this.runnerDiv.setAttribute("className", className); + var specs = runner.specs(); + var specCount = 0; + for (var i = 0; i < specs.length; i++) { + if (this.specFilter(specs[i])) { + specCount++; + } + } + var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s"); + message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"; + this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild); + + this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString())); +}; + +jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) { + var results = suite.results(); + var status = results.passed() ? 'passed' : 'failed'; + if (results.totalCount === 0) { // todo: change this to check results.skipped + status = 'skipped'; + } + this.suiteDivs[suite.id].className += " " + status; +}; + +jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) { + if (this.logRunningSpecs) { + this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); + } +}; + +jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) { + var results = spec.results(); + var status = results.passed() ? 'passed' : 'failed'; + if (results.skipped) { + status = 'skipped'; + } + var specDiv = this.createDom('div', { className: 'spec ' + status }, + this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"), + this.createDom('a', { + className: 'description', + href: '?spec=' + encodeURIComponent(spec.getFullName()), + title: spec.getFullName() + }, spec.description)); + + + var resultItems = results.getItems(); + var messagesDiv = this.createDom('div', { className: 'messages' }); + for (var i = 0; i < resultItems.length; i++) { + var result = resultItems[i]; + + if (result.type == 'log') { + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); + } else if (result.type == 'expect' && result.passed && !result.passed()) { + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); + + if (result.trace.stack) { + messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); + } + } + } + + if (messagesDiv.childNodes.length > 0) { + specDiv.appendChild(messagesDiv); + } + + this.suiteDivs[spec.suite.id].appendChild(specDiv); +}; + +jasmine.TrivialReporter.prototype.log = function() { + var console = jasmine.getGlobal().console; + if (console && console.log) { + if (console.log.apply) { + console.log.apply(console, arguments); + } else { + console.log(arguments); // ie fix: console.log.apply doesn't exist on ie + } + } +}; + +jasmine.TrivialReporter.prototype.getLocation = function() { + return this.document.location; +}; + +jasmine.TrivialReporter.prototype.specFilter = function(spec) { + var paramMap = {}; + var params = this.getLocation().search.substring(1).split('&'); + for (var i = 0; i < params.length; i++) { + var p = params[i].split('='); + paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); + } + + if (!paramMap.spec) { + return true; + } + return spec.getFullName().indexOf(paramMap.spec) === 0; +}; diff --git a/plugins/org.apache.cordova.camera/test/autotest/index.html b/plugins/org.apache.cordova.camera/test/autotest/index.html new file mode 100644 index 0000000..6ebccbd --- /dev/null +++ b/plugins/org.apache.cordova.camera/test/autotest/index.html @@ -0,0 +1,59 @@ + + + + + + + + + + Cordova API Specs + + + + + +

Cordova API Specs

+ + Run All Tests + Run Accelerometer Tests + Run Battery Tests + Run Camera Tests + Run Capture Tests + Run Compass Tests + Run Contacts Tests + Run Data URI Tests + Run Device Tests + Run File Tests + Run FileTransfer Tests + Run Geolocation Tests + Run Globalization Tests + Run Media Tests + Run Network Tests + Run Notification Tests + Run Platform Tests + Run Storage Tests + Run Bridge Tests + +

Back
+ + diff --git a/plugins/org.apache.cordova.camera/test/autotest/jasmine.css b/plugins/org.apache.cordova.camera/test/autotest/jasmine.css new file mode 100644 index 0000000..826e575 --- /dev/null +++ b/plugins/org.apache.cordova.camera/test/autotest/jasmine.css @@ -0,0 +1,81 @@ +body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } + +#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } +#HTMLReporter a { text-decoration: none; } +#HTMLReporter a:hover { text-decoration: underline; } +#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; } +#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; } +#HTMLReporter #jasmine_content { position: fixed; right: 100%; } +#HTMLReporter .version { color: #aaaaaa; } +#HTMLReporter .banner { margin-top: 14px; } +#HTMLReporter .duration { color: #aaaaaa; float: right; } +#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; } +#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; } +#HTMLReporter .symbolSummary li.passed { font-size: 14px; } +#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; } +#HTMLReporter .symbolSummary li.failed { line-height: 9px; } +#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } +#HTMLReporter .symbolSummary li.skipped { font-size: 14px; } +#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; } +#HTMLReporter .symbolSummary li.pending { line-height: 11px; } +#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; } +#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } +#HTMLReporter .runningAlert { background-color: #666666; } +#HTMLReporter .skippedAlert { background-color: #aaaaaa; } +#HTMLReporter .skippedAlert:first-child { background-color: #333333; } +#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; } +#HTMLReporter .passingAlert { background-color: #a6b779; } +#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; } +#HTMLReporter .failingAlert { background-color: #cf867e; } +#HTMLReporter .failingAlert:first-child { background-color: #b03911; } +#HTMLReporter .results { margin-top: 14px; } +#HTMLReporter #details { display: none; } +#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; } +#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } +#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } +#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } +#HTMLReporter.showDetails .summary { display: none; } +#HTMLReporter.showDetails #details { display: block; } +#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } +#HTMLReporter .summary { margin-top: 14px; } +#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; } +#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; } +#HTMLReporter .summary .specSummary.failed a { color: #b03911; } +#HTMLReporter .description + .suite { margin-top: 0; } +#HTMLReporter .suite { margin-top: 14px; } +#HTMLReporter .suite a { color: #333333; } +#HTMLReporter #details .specDetail { margin-bottom: 28px; } +#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; } +#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; } +#HTMLReporter .resultMessage span.result { display: block; } +#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } + +#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ } +#TrivialReporter a:visited, #TrivialReporter a { color: #303; } +#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; } +#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; } +#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; } +#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; } +#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; } +#TrivialReporter .runner.running { background-color: yellow; } +#TrivialReporter .options { text-align: right; font-size: .8em; } +#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; } +#TrivialReporter .suite .suite { margin: 5px; } +#TrivialReporter .suite.passed { background-color: #dfd; } +#TrivialReporter .suite.failed { background-color: #fdd; } +#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; } +#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; } +#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; } +#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; } +#TrivialReporter .spec.skipped { background-color: #bbb; } +#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; } +#TrivialReporter .passed { background-color: #cfc; display: none; } +#TrivialReporter .failed { background-color: #fbb; } +#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; } +#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; } +#TrivialReporter .resultMessage .mismatch { color: black; } +#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; } +#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; } +#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; } +#TrivialReporter #jasmine_content { position: fixed; right: 100%; } +#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; } diff --git a/plugins/org.apache.cordova.camera/test/autotest/jasmine.js b/plugins/org.apache.cordova.camera/test/autotest/jasmine.js new file mode 100644 index 0000000..bccb66c --- /dev/null +++ b/plugins/org.apache.cordova.camera/test/autotest/jasmine.js @@ -0,0 +1,2530 @@ +var isCommonJS = typeof window == "undefined"; + +/** + * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework. + * + * @namespace + */ +var jasmine = {}; +if (isCommonJS) exports.jasmine = jasmine; +/** + * @private + */ +jasmine.unimplementedMethod_ = function() { + throw new Error("unimplemented method"); +}; + +/** + * Use jasmine.undefined instead of undefined, since undefined is just + * a plain old variable and may be redefined by somebody else. + * + * @private + */ +jasmine.undefined = jasmine.___undefined___; + +/** + * Show diagnostic messages in the console if set to true + * + */ +jasmine.VERBOSE = false; + +/** + * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed. + * + */ +jasmine.DEFAULT_UPDATE_INTERVAL = 250; + +/** + * Default timeout interval in milliseconds for waitsFor() blocks. + */ +jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; + +jasmine.getGlobal = function() { + function getGlobal() { + return this; + } + + return getGlobal(); +}; + +/** + * Allows for bound functions to be compared. Internal use only. + * + * @ignore + * @private + * @param base {Object} bound 'this' for the function + * @param name {Function} function to find + */ +jasmine.bindOriginal_ = function(base, name) { + var original = base[name]; + if (original.apply) { + return function() { + return original.apply(base, arguments); + }; + } else { + // IE support + return jasmine.getGlobal()[name]; + } +}; + +jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout'); +jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout'); +jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval'); +jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval'); + +jasmine.MessageResult = function(values) { + this.type = 'log'; + this.values = values; + this.trace = new Error(); // todo: test better +}; + +jasmine.MessageResult.prototype.toString = function() { + var text = ""; + for (var i = 0; i < this.values.length; i++) { + if (i > 0) text += " "; + if (jasmine.isString_(this.values[i])) { + text += this.values[i]; + } else { + text += jasmine.pp(this.values[i]); + } + } + return text; +}; + +jasmine.ExpectationResult = function(params) { + this.type = 'expect'; + this.matcherName = params.matcherName; + this.passed_ = params.passed; + this.expected = params.expected; + this.actual = params.actual; + this.message = this.passed_ ? 'Passed.' : params.message; + + var trace = (params.trace || new Error(this.message)); + this.trace = this.passed_ ? '' : trace; +}; + +jasmine.ExpectationResult.prototype.toString = function () { + return this.message; +}; + +jasmine.ExpectationResult.prototype.passed = function () { + return this.passed_; +}; + +/** + * Getter for the Jasmine environment. Ensures one gets created + */ +jasmine.getEnv = function() { + var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env(); + return env; +}; + +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ +jasmine.isArray_ = function(value) { + return jasmine.isA_("Array", value); +}; + +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ +jasmine.isString_ = function(value) { + return jasmine.isA_("String", value); +}; + +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ +jasmine.isNumber_ = function(value) { + return jasmine.isA_("Number", value); +}; + +/** + * @ignore + * @private + * @param {String} typeName + * @param value + * @returns {Boolean} + */ +jasmine.isA_ = function(typeName, value) { + return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; +}; + +/** + * Pretty printer for expecations. Takes any object and turns it into a human-readable string. + * + * @param value {Object} an object to be outputted + * @returns {String} + */ +jasmine.pp = function(value) { + var stringPrettyPrinter = new jasmine.StringPrettyPrinter(); + stringPrettyPrinter.format(value); + return stringPrettyPrinter.string; +}; + +/** + * Returns true if the object is a DOM Node. + * + * @param {Object} obj object to check + * @returns {Boolean} + */ +jasmine.isDomNode = function(obj) { + return obj.nodeType > 0; +}; + +/** + * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter. + * + * @example + * // don't care about which function is passed in, as long as it's a function + * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function)); + * + * @param {Class} clazz + * @returns matchable object of the type clazz + */ +jasmine.any = function(clazz) { + return new jasmine.Matchers.Any(clazz); +}; + +/** + * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the + * attributes on the object. + * + * @example + * // don't care about any other attributes than foo. + * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"}); + * + * @param sample {Object} sample + * @returns matchable object for the sample + */ +jasmine.objectContaining = function (sample) { + return new jasmine.Matchers.ObjectContaining(sample); +}; + +/** + * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks. + * + * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine + * expectation syntax. Spies can be checked if they were called or not and what the calling params were. + * + * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs). + * + * Spies are torn down at the end of every spec. + * + * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj. + * + * @example + * // a stub + * var myStub = jasmine.createSpy('myStub'); // can be used anywhere + * + * // spy example + * var foo = { + * not: function(bool) { return !bool; } + * } + * + * // actual foo.not will not be called, execution stops + * spyOn(foo, 'not'); + + // foo.not spied upon, execution will continue to implementation + * spyOn(foo, 'not').andCallThrough(); + * + * // fake example + * var foo = { + * not: function(bool) { return !bool; } + * } + * + * // foo.not(val) will return val + * spyOn(foo, 'not').andCallFake(function(value) {return value;}); + * + * // mock example + * foo.not(7 == 7); + * expect(foo.not).toHaveBeenCalled(); + * expect(foo.not).toHaveBeenCalledWith(true); + * + * @constructor + * @see spyOn, jasmine.createSpy, jasmine.createSpyObj + * @param {String} name + */ +jasmine.Spy = function(name) { + /** + * The name of the spy, if provided. + */ + this.identity = name || 'unknown'; + /** + * Is this Object a spy? + */ + this.isSpy = true; + /** + * The actual function this spy stubs. + */ + this.plan = function() { + }; + /** + * Tracking of the most recent call to the spy. + * @example + * var mySpy = jasmine.createSpy('foo'); + * mySpy(1, 2); + * mySpy.mostRecentCall.args = [1, 2]; + */ + this.mostRecentCall = {}; + + /** + * Holds arguments for each call to the spy, indexed by call count + * @example + * var mySpy = jasmine.createSpy('foo'); + * mySpy(1, 2); + * mySpy(7, 8); + * mySpy.mostRecentCall.args = [7, 8]; + * mySpy.argsForCall[0] = [1, 2]; + * mySpy.argsForCall[1] = [7, 8]; + */ + this.argsForCall = []; + this.calls = []; +}; + +/** + * Tells a spy to call through to the actual implemenatation. + * + * @example + * var foo = { + * bar: function() { // do some stuff } + * } + * + * // defining a spy on an existing property: foo.bar + * spyOn(foo, 'bar').andCallThrough(); + */ +jasmine.Spy.prototype.andCallThrough = function() { + this.plan = this.originalValue; + return this; +}; + +/** + * For setting the return value of a spy. + * + * @example + * // defining a spy from scratch: foo() returns 'baz' + * var foo = jasmine.createSpy('spy on foo').andReturn('baz'); + * + * // defining a spy on an existing property: foo.bar() returns 'baz' + * spyOn(foo, 'bar').andReturn('baz'); + * + * @param {Object} value + */ +jasmine.Spy.prototype.andReturn = function(value) { + this.plan = function() { + return value; + }; + return this; +}; + +/** + * For throwing an exception when a spy is called. + * + * @example + * // defining a spy from scratch: foo() throws an exception w/ message 'ouch' + * var foo = jasmine.createSpy('spy on foo').andThrow('baz'); + * + * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch' + * spyOn(foo, 'bar').andThrow('baz'); + * + * @param {String} exceptionMsg + */ +jasmine.Spy.prototype.andThrow = function(exceptionMsg) { + this.plan = function() { + throw exceptionMsg; + }; + return this; +}; + +/** + * Calls an alternate implementation when a spy is called. + * + * @example + * var baz = function() { + * // do some stuff, return something + * } + * // defining a spy from scratch: foo() calls the function baz + * var foo = jasmine.createSpy('spy on foo').andCall(baz); + * + * // defining a spy on an existing property: foo.bar() calls an anonymnous function + * spyOn(foo, 'bar').andCall(function() { return 'baz';} ); + * + * @param {Function} fakeFunc + */ +jasmine.Spy.prototype.andCallFake = function(fakeFunc) { + this.plan = fakeFunc; + return this; +}; + +/** + * Resets all of a spy's the tracking variables so that it can be used again. + * + * @example + * spyOn(foo, 'bar'); + * + * foo.bar(); + * + * expect(foo.bar.callCount).toEqual(1); + * + * foo.bar.reset(); + * + * expect(foo.bar.callCount).toEqual(0); + */ +jasmine.Spy.prototype.reset = function() { + this.wasCalled = false; + this.callCount = 0; + this.argsForCall = []; + this.calls = []; + this.mostRecentCall = {}; +}; + +jasmine.createSpy = function(name) { + + var spyObj = function() { + spyObj.wasCalled = true; + spyObj.callCount++; + var args = jasmine.util.argsToArray(arguments); + spyObj.mostRecentCall.object = this; + spyObj.mostRecentCall.args = args; + spyObj.argsForCall.push(args); + spyObj.calls.push({object: this, args: args}); + return spyObj.plan.apply(this, arguments); + }; + + var spy = new jasmine.Spy(name); + + for (var prop in spy) { + spyObj[prop] = spy[prop]; + } + + spyObj.reset(); + + return spyObj; +}; + +/** + * Determines whether an object is a spy. + * + * @param {jasmine.Spy|Object} putativeSpy + * @returns {Boolean} + */ +jasmine.isSpy = function(putativeSpy) { + return putativeSpy && putativeSpy.isSpy; +}; + +/** + * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something + * large in one call. + * + * @param {String} baseName name of spy class + * @param {Array} methodNames array of names of methods to make spies + */ +jasmine.createSpyObj = function(baseName, methodNames) { + if (!jasmine.isArray_(methodNames) || methodNames.length === 0) { + throw new Error('createSpyObj requires a non-empty array of method names to create spies for'); + } + var obj = {}; + for (var i = 0; i < methodNames.length; i++) { + obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]); + } + return obj; +}; + +/** + * All parameters are pretty-printed and concatenated together, then written to the current spec's output. + * + * Be careful not to leave calls to jasmine.log in production code. + */ +jasmine.log = function() { + var spec = jasmine.getEnv().currentSpec; + spec.log.apply(spec, arguments); +}; + +/** + * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy. + * + * @example + * // spy example + * var foo = { + * not: function(bool) { return !bool; } + * } + * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops + * + * @see jasmine.createSpy + * @param obj + * @param methodName + * @returns a Jasmine spy that can be chained with all spy methods + */ +var spyOn = function(obj, methodName) { + return jasmine.getEnv().currentSpec.spyOn(obj, methodName); +}; +if (isCommonJS) exports.spyOn = spyOn; + +/** + * Creates a Jasmine spec that will be added to the current suite. + * + * // TODO: pending tests + * + * @example + * it('should be true', function() { + * expect(true).toEqual(true); + * }); + * + * @param {String} desc description of this specification + * @param {Function} func defines the preconditions and expectations of the spec + */ +var it = function(desc, func) { + return jasmine.getEnv().it(desc, func); +}; +if (isCommonJS) exports.it = it; + +/** + * Creates a disabled Jasmine spec. + * + * A convenience method that allows existing specs to be disabled temporarily during development. + * + * @param {String} desc description of this specification + * @param {Function} func defines the preconditions and expectations of the spec + */ +var xit = function(desc, func) { + return jasmine.getEnv().xit(desc, func); +}; +if (isCommonJS) exports.xit = xit; + +/** + * Starts a chain for a Jasmine expectation. + * + * It is passed an Object that is the actual value and should chain to one of the many + * jasmine.Matchers functions. + * + * @param {Object} actual Actual value to test against and expected value + */ +var expect = function(actual) { + return jasmine.getEnv().currentSpec.expect(actual); +}; +if (isCommonJS) exports.expect = expect; + +/** + * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs. + * + * @param {Function} func Function that defines part of a jasmine spec. + */ +var runs = function(func) { + jasmine.getEnv().currentSpec.runs(func); +}; +if (isCommonJS) exports.runs = runs; + +/** + * Waits a fixed time period before moving to the next block. + * + * @deprecated Use waitsFor() instead + * @param {Number} timeout milliseconds to wait + */ +var waits = function(timeout) { + jasmine.getEnv().currentSpec.waits(timeout); +}; +if (isCommonJS) exports.waits = waits; + +/** + * Waits for the latchFunction to return true before proceeding to the next block. + * + * @param {Function} latchFunction + * @param {String} optional_timeoutMessage + * @param {Number} optional_timeout + */ +var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { + jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments); +}; +if (isCommonJS) exports.waitsFor = waitsFor; + +/** + * A function that is called before each spec in a suite. + * + * Used for spec setup, including validating assumptions. + * + * @param {Function} beforeEachFunction + */ +var beforeEach = function(beforeEachFunction) { + jasmine.getEnv().beforeEach(beforeEachFunction); +}; +if (isCommonJS) exports.beforeEach = beforeEach; + +/** + * A function that is called after each spec in a suite. + * + * Used for restoring any state that is hijacked during spec execution. + * + * @param {Function} afterEachFunction + */ +var afterEach = function(afterEachFunction) { + jasmine.getEnv().afterEach(afterEachFunction); +}; +if (isCommonJS) exports.afterEach = afterEach; + +/** + * Defines a suite of specifications. + * + * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared + * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization + * of setup in some tests. + * + * @example + * // TODO: a simple suite + * + * // TODO: a simple suite with a nested describe block + * + * @param {String} description A string, usually the class under test. + * @param {Function} specDefinitions function that defines several specs. + */ +var describe = function(description, specDefinitions) { + return jasmine.getEnv().describe(description, specDefinitions); +}; +if (isCommonJS) exports.describe = describe; + +/** + * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development. + * + * @param {String} description A string, usually the class under test. + * @param {Function} specDefinitions function that defines several specs. + */ +var xdescribe = function(description, specDefinitions) { + return jasmine.getEnv().xdescribe(description, specDefinitions); +}; +if (isCommonJS) exports.xdescribe = xdescribe; + + +// Provide the XMLHttpRequest class for IE 5.x-6.x: +jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() { + function tryIt(f) { + try { + return f(); + } catch(e) { + } + return null; + } + + var xhr = tryIt(function() { + return new ActiveXObject("Msxml2.XMLHTTP.6.0"); + }) || + tryIt(function() { + return new ActiveXObject("Msxml2.XMLHTTP.3.0"); + }) || + tryIt(function() { + return new ActiveXObject("Msxml2.XMLHTTP"); + }) || + tryIt(function() { + return new ActiveXObject("Microsoft.XMLHTTP"); + }); + + if (!xhr) throw new Error("This browser does not support XMLHttpRequest."); + + return xhr; +} : XMLHttpRequest; +/** + * @namespace + */ +jasmine.util = {}; + +/** + * Declare that a child class inherit it's prototype from the parent class. + * + * @private + * @param {Function} childClass + * @param {Function} parentClass + */ +jasmine.util.inherit = function(childClass, parentClass) { + /** + * @private + */ + var subclass = function() { + }; + subclass.prototype = parentClass.prototype; + childClass.prototype = new subclass(); +}; + +jasmine.util.formatException = function(e) { + var lineNumber; + if (e.line) { + lineNumber = e.line; + } + else if (e.lineNumber) { + lineNumber = e.lineNumber; + } + + var file; + + if (e.sourceURL) { + file = e.sourceURL; + } + else if (e.fileName) { + file = e.fileName; + } + + var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString(); + + if (file && lineNumber) { + message += ' in ' + file + ' (line ' + lineNumber + ')'; + } + + return message; +}; + +jasmine.util.htmlEscape = function(str) { + if (!str) return str; + return str.replace(/&/g, '&') + .replace(//g, '>'); +}; + +jasmine.util.argsToArray = function(args) { + var arrayOfArgs = []; + for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]); + return arrayOfArgs; +}; + +jasmine.util.extend = function(destination, source) { + for (var property in source) destination[property] = source[property]; + return destination; +}; + +/** + * Environment for Jasmine + * + * @constructor + */ +jasmine.Env = function() { + this.currentSpec = null; + this.currentSuite = null; + this.currentRunner_ = new jasmine.Runner(this); + + this.reporter = new jasmine.MultiReporter(); + + this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL; + this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL; + this.lastUpdate = 0; + this.specFilter = function() { + return true; + }; + + this.nextSpecId_ = 0; + this.nextSuiteId_ = 0; + this.equalityTesters_ = []; + + // wrap matchers + this.matchersClass = function() { + jasmine.Matchers.apply(this, arguments); + }; + jasmine.util.inherit(this.matchersClass, jasmine.Matchers); + + jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass); +}; + + +jasmine.Env.prototype.setTimeout = jasmine.setTimeout; +jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout; +jasmine.Env.prototype.setInterval = jasmine.setInterval; +jasmine.Env.prototype.clearInterval = jasmine.clearInterval; + +/** + * @returns an object containing jasmine version build info, if set. + */ +jasmine.Env.prototype.version = function () { + if (jasmine.version_) { + return jasmine.version_; + } else { + throw new Error('Version not set'); + } +}; + +/** + * @returns string containing jasmine version build info, if set. + */ +jasmine.Env.prototype.versionString = function() { + if (!jasmine.version_) { + return "version unknown"; + } + + var version = this.version(); + var versionString = version.major + "." + version.minor + "." + version.build; + if (version.release_candidate) { + versionString += ".rc" + version.release_candidate; + } + versionString += " revision " + version.revision; + return versionString; +}; + +/** + * @returns a sequential integer starting at 0 + */ +jasmine.Env.prototype.nextSpecId = function () { + return this.nextSpecId_++; +}; + +/** + * @returns a sequential integer starting at 0 + */ +jasmine.Env.prototype.nextSuiteId = function () { + return this.nextSuiteId_++; +}; + +/** + * Register a reporter to receive status updates from Jasmine. + * @param {jasmine.Reporter} reporter An object which will receive status updates. + */ +jasmine.Env.prototype.addReporter = function(reporter) { + this.reporter.addReporter(reporter); +}; + +jasmine.Env.prototype.execute = function() { + this.currentRunner_.execute(); +}; + +jasmine.Env.prototype.describe = function(description, specDefinitions) { + var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite); + + var parentSuite = this.currentSuite; + if (parentSuite) { + parentSuite.add(suite); + } else { + this.currentRunner_.add(suite); + } + + this.currentSuite = suite; + + var declarationError = null; + try { + specDefinitions.call(suite); + } catch(e) { + declarationError = e; + } + + if (declarationError) { + this.it("encountered a declaration exception", function() { + throw declarationError; + }); + } + + this.currentSuite = parentSuite; + + return suite; +}; + +jasmine.Env.prototype.beforeEach = function(beforeEachFunction) { + if (this.currentSuite) { + this.currentSuite.beforeEach(beforeEachFunction); + } else { + this.currentRunner_.beforeEach(beforeEachFunction); + } +}; + +jasmine.Env.prototype.currentRunner = function () { + return this.currentRunner_; +}; + +jasmine.Env.prototype.afterEach = function(afterEachFunction) { + if (this.currentSuite) { + this.currentSuite.afterEach(afterEachFunction); + } else { + this.currentRunner_.afterEach(afterEachFunction); + } + +}; + +jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) { + return { + execute: function() { + } + }; +}; + +jasmine.Env.prototype.it = function(description, func) { + var spec = new jasmine.Spec(this, this.currentSuite, description); + this.currentSuite.add(spec); + this.currentSpec = spec; + + if (func) { + spec.runs(func); + } + + return spec; +}; + +jasmine.Env.prototype.xit = function(desc, func) { + return { + id: this.nextSpecId(), + runs: function() { + } + }; +}; + +jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) { + if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) { + return true; + } + + a.__Jasmine_been_here_before__ = b; + b.__Jasmine_been_here_before__ = a; + + var hasKey = function(obj, keyName) { + return obj !== null && obj[keyName] !== jasmine.undefined; + }; + + for (var property in b) { + if (!hasKey(a, property) && hasKey(b, property)) { + mismatchKeys.push("expected has key '" + property + "', but missing from actual."); + } + } + for (property in a) { + if (!hasKey(b, property) && hasKey(a, property)) { + mismatchKeys.push("expected missing key '" + property + "', but present in actual."); + } + } + for (property in b) { + if (property == '__Jasmine_been_here_before__') continue; + if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) { + mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual."); + } + } + + if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) { + mismatchValues.push("arrays were not the same length"); + } + + delete a.__Jasmine_been_here_before__; + delete b.__Jasmine_been_here_before__; + return (mismatchKeys.length === 0 && mismatchValues.length === 0); +}; + +jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) { + mismatchKeys = mismatchKeys || []; + mismatchValues = mismatchValues || []; + + for (var i = 0; i < this.equalityTesters_.length; i++) { + var equalityTester = this.equalityTesters_[i]; + var result = equalityTester(a, b, this, mismatchKeys, mismatchValues); + if (result !== jasmine.undefined) return result; + } + + if (a === b) return true; + + if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) { + return (a == jasmine.undefined && b == jasmine.undefined); + } + + if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) { + return a === b; + } + + if (a instanceof Date && b instanceof Date) { + return a.getTime() == b.getTime(); + } + + if (a.jasmineMatches) { + return a.jasmineMatches(b); + } + + if (b.jasmineMatches) { + return b.jasmineMatches(a); + } + + if (a instanceof jasmine.Matchers.ObjectContaining) { + return a.matches(b); + } + + if (b instanceof jasmine.Matchers.ObjectContaining) { + return b.matches(a); + } + + if (jasmine.isString_(a) && jasmine.isString_(b)) { + return (a == b); + } + + if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) { + return (a == b); + } + + if (typeof a === "object" && typeof b === "object") { + return this.compareObjects_(a, b, mismatchKeys, mismatchValues); + } + + //Straight check + return (a === b); +}; + +jasmine.Env.prototype.contains_ = function(haystack, needle) { + if (jasmine.isArray_(haystack)) { + for (var i = 0; i < haystack.length; i++) { + if (this.equals_(haystack[i], needle)) return true; + } + return false; + } + return haystack.indexOf(needle) >= 0; +}; + +jasmine.Env.prototype.addEqualityTester = function(equalityTester) { + this.equalityTesters_.push(equalityTester); +}; +/** No-op base class for Jasmine reporters. + * + * @constructor + */ +jasmine.Reporter = function() { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportRunnerStarting = function(runner) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportRunnerResults = function(runner) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportSuiteResults = function(suite) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportSpecStarting = function(spec) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportSpecResults = function(spec) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.log = function(str) { +}; + +/** + * Blocks are functions with executable code that make up a spec. + * + * @constructor + * @param {jasmine.Env} env + * @param {Function} func + * @param {jasmine.Spec} spec + */ +jasmine.Block = function(env, func, spec) { + this.env = env; + this.func = func; + this.spec = spec; +}; + +jasmine.Block.prototype.execute = function(onComplete) { + try { + this.func.apply(this.spec); + } catch (e) { + this.spec.fail(e); + } + onComplete(); +}; +/** JavaScript API reporter. + * + * @constructor + */ +jasmine.JsApiReporter = function() { + this.started = false; + this.finished = false; + this.suites_ = []; + this.results_ = {}; +}; + +jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) { + this.started = true; + var suites = runner.topLevelSuites(); + for (var i = 0; i < suites.length; i++) { + var suite = suites[i]; + this.suites_.push(this.summarize_(suite)); + } +}; + +jasmine.JsApiReporter.prototype.suites = function() { + return this.suites_; +}; + +jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) { + var isSuite = suiteOrSpec instanceof jasmine.Suite; + var summary = { + id: suiteOrSpec.id, + name: suiteOrSpec.description, + type: isSuite ? 'suite' : 'spec', + children: [] + }; + + if (isSuite) { + var children = suiteOrSpec.children(); + for (var i = 0; i < children.length; i++) { + summary.children.push(this.summarize_(children[i])); + } + } + return summary; +}; + +jasmine.JsApiReporter.prototype.results = function() { + return this.results_; +}; + +jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) { + return this.results_[specId]; +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) { + this.finished = true; +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) { + this.results_[spec.id] = { + messages: spec.results().getItems(), + result: spec.results().failedCount > 0 ? "failed" : "passed" + }; +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.log = function(str) { +}; + +jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){ + var results = {}; + for (var i = 0; i < specIds.length; i++) { + var specId = specIds[i]; + results[specId] = this.summarizeResult_(this.results_[specId]); + } + return results; +}; + +jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){ + var summaryMessages = []; + var messagesLength = result.messages.length; + for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) { + var resultMessage = result.messages[messageIndex]; + summaryMessages.push({ + text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined, + passed: resultMessage.passed ? resultMessage.passed() : true, + type: resultMessage.type, + message: resultMessage.message, + trace: { + stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined + } + }); + } + + return { + result : result.result, + messages : summaryMessages + }; +}; + +/** + * @constructor + * @param {jasmine.Env} env + * @param actual + * @param {jasmine.Spec} spec + */ +jasmine.Matchers = function(env, actual, spec, opt_isNot) { + this.env = env; + this.actual = actual; + this.spec = spec; + this.isNot = opt_isNot || false; + this.reportWasCalled_ = false; +}; + +// todo: @deprecated as of Jasmine 0.11, remove soon [xw] +jasmine.Matchers.pp = function(str) { + throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!"); +}; + +// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw] +jasmine.Matchers.prototype.report = function(result, failing_message, details) { + throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs"); +}; + +jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) { + for (var methodName in prototype) { + if (methodName == 'report') continue; + var orig = prototype[methodName]; + matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig); + } +}; + +jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { + return function() { + var matcherArgs = jasmine.util.argsToArray(arguments); + var result = matcherFunction.apply(this, arguments); + + if (this.isNot) { + result = !result; + } + + if (this.reportWasCalled_) return result; + + var message; + if (!result) { + if (this.message) { + message = this.message.apply(this, arguments); + if (jasmine.isArray_(message)) { + message = message[this.isNot ? 1 : 0]; + } + } else { + var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); + message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate; + if (matcherArgs.length > 0) { + for (var i = 0; i < matcherArgs.length; i++) { + if (i > 0) message += ","; + message += " " + jasmine.pp(matcherArgs[i]); + } + } + message += "."; + } + } + var expectationResult = new jasmine.ExpectationResult({ + matcherName: matcherName, + passed: result, + expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0], + actual: this.actual, + message: message + }); + this.spec.addMatcherResult(expectationResult); + return jasmine.undefined; + }; +}; + + + + +/** + * toBe: compares the actual to the expected using === + * @param expected + */ +jasmine.Matchers.prototype.toBe = function(expected) { + return this.actual === expected; +}; + +/** + * toNotBe: compares the actual to the expected using !== + * @param expected + * @deprecated as of 1.0. Use not.toBe() instead. + */ +jasmine.Matchers.prototype.toNotBe = function(expected) { + return this.actual !== expected; +}; + +/** + * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc. + * + * @param expected + */ +jasmine.Matchers.prototype.toEqual = function(expected) { + return this.env.equals_(this.actual, expected); +}; + +/** + * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual + * @param expected + * @deprecated as of 1.0. Use not.toEqual() instead. + */ +jasmine.Matchers.prototype.toNotEqual = function(expected) { + return !this.env.equals_(this.actual, expected); +}; + +/** + * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes + * a pattern or a String. + * + * @param expected + */ +jasmine.Matchers.prototype.toMatch = function(expected) { + return new RegExp(expected).test(this.actual); +}; + +/** + * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch + * @param expected + * @deprecated as of 1.0. Use not.toMatch() instead. + */ +jasmine.Matchers.prototype.toNotMatch = function(expected) { + return !(new RegExp(expected).test(this.actual)); +}; + +/** + * Matcher that compares the actual to jasmine.undefined. + */ +jasmine.Matchers.prototype.toBeDefined = function() { + return (this.actual !== jasmine.undefined); +}; + +/** + * Matcher that compares the actual to jasmine.undefined. + */ +jasmine.Matchers.prototype.toBeUndefined = function() { + return (this.actual === jasmine.undefined); +}; + +/** + * Matcher that compares the actual to null. + */ +jasmine.Matchers.prototype.toBeNull = function() { + return (this.actual === null); +}; + +/** + * Matcher that boolean not-nots the actual. + */ +jasmine.Matchers.prototype.toBeTruthy = function() { + return !!this.actual; +}; + + +/** + * Matcher that boolean nots the actual. + */ +jasmine.Matchers.prototype.toBeFalsy = function() { + return !this.actual; +}; + + +/** + * Matcher that checks to see if the actual, a Jasmine spy, was called. + */ +jasmine.Matchers.prototype.toHaveBeenCalled = function() { + if (arguments.length > 0) { + throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); + } + + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + + this.message = function() { + return [ + "Expected spy " + this.actual.identity + " to have been called.", + "Expected spy " + this.actual.identity + " not to have been called." + ]; + }; + + return this.actual.wasCalled; +}; + +/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */ +jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled; + +/** + * Matcher that checks to see if the actual, a Jasmine spy, was not called. + * + * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead + */ +jasmine.Matchers.prototype.wasNotCalled = function() { + if (arguments.length > 0) { + throw new Error('wasNotCalled does not take arguments'); + } + + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + + this.message = function() { + return [ + "Expected spy " + this.actual.identity + " to not have been called.", + "Expected spy " + this.actual.identity + " to have been called." + ]; + }; + + return !this.actual.wasCalled; +}; + +/** + * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters. + * + * @example + * + */ +jasmine.Matchers.prototype.toHaveBeenCalledWith = function() { + var expectedArgs = jasmine.util.argsToArray(arguments); + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + this.message = function() { + if (this.actual.callCount === 0) { + // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw] + return [ + "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.", + "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was." + ]; + } else { + return [ + "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall), + "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall) + ]; + } + }; + + return this.env.contains_(this.actual.argsForCall, expectedArgs); +}; + +/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */ +jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith; + +/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */ +jasmine.Matchers.prototype.wasNotCalledWith = function() { + var expectedArgs = jasmine.util.argsToArray(arguments); + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + + this.message = function() { + return [ + "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was", + "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was" + ]; + }; + + return !this.env.contains_(this.actual.argsForCall, expectedArgs); +}; + +/** + * Matcher that checks that the expected item is an element in the actual Array. + * + * @param {Object} expected + */ +jasmine.Matchers.prototype.toContain = function(expected) { + return this.env.contains_(this.actual, expected); +}; + +/** + * Matcher that checks that the expected item is NOT an element in the actual Array. + * + * @param {Object} expected + * @deprecated as of 1.0. Use not.toContain() instead. + */ +jasmine.Matchers.prototype.toNotContain = function(expected) { + return !this.env.contains_(this.actual, expected); +}; + +jasmine.Matchers.prototype.toBeLessThan = function(expected) { + return this.actual < expected; +}; + +jasmine.Matchers.prototype.toBeGreaterThan = function(expected) { + return this.actual > expected; +}; + +/** + * Matcher that checks that the expected item is equal to the actual item + * up to a given level of decimal precision (default 2). + * + * @param {Number} expected + * @param {Number} precision + */ +jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) { + if (!(precision === 0)) { + precision = precision || 2; + } + var multiplier = Math.pow(10, precision); + var actual = Math.round(this.actual * multiplier); + expected = Math.round(expected * multiplier); + return expected == actual; +}; + +/** + * Matcher that checks that the expected exception was thrown by the actual. + * + * @param {String} expected + */ +jasmine.Matchers.prototype.toThrow = function(expected) { + var result = false; + var exception; + if (typeof this.actual != 'function') { + throw new Error('Actual is not a function'); + } + try { + this.actual(); + } catch (e) { + exception = e; + } + if (exception) { + result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected)); + } + + var not = this.isNot ? "not " : ""; + + this.message = function() { + if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) { + return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' '); + } else { + return "Expected function to throw an exception."; + } + }; + + return result; +}; + +jasmine.Matchers.Any = function(expectedClass) { + this.expectedClass = expectedClass; +}; + +jasmine.Matchers.Any.prototype.jasmineMatches = function(other) { + if (this.expectedClass == String) { + return typeof other == 'string' || other instanceof String; + } + + if (this.expectedClass == Number) { + return typeof other == 'number' || other instanceof Number; + } + + if (this.expectedClass == Function) { + return typeof other == 'function' || other instanceof Function; + } + + if (this.expectedClass == Object) { + return typeof other == 'object'; + } + + return other instanceof this.expectedClass; +}; + +jasmine.Matchers.Any.prototype.jasmineToString = function() { + return ''; +}; + +jasmine.Matchers.ObjectContaining = function (sample) { + this.sample = sample; +}; + +jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) { + mismatchKeys = mismatchKeys || []; + mismatchValues = mismatchValues || []; + + var env = jasmine.getEnv(); + + var hasKey = function(obj, keyName) { + return obj != null && obj[keyName] !== jasmine.undefined; + }; + + for (var property in this.sample) { + if (!hasKey(other, property) && hasKey(this.sample, property)) { + mismatchKeys.push("expected has key '" + property + "', but missing from actual."); + } + else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) { + mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual."); + } + } + + return (mismatchKeys.length === 0 && mismatchValues.length === 0); +}; + +jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () { + return ""; +}; +// Mock setTimeout, clearTimeout +// Contributed by Pivotal Computer Systems, www.pivotalsf.com + +jasmine.FakeTimer = function() { + this.reset(); + + var self = this; + self.setTimeout = function(funcToCall, millis) { + self.timeoutsMade++; + self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false); + return self.timeoutsMade; + }; + + self.setInterval = function(funcToCall, millis) { + self.timeoutsMade++; + self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true); + return self.timeoutsMade; + }; + + self.clearTimeout = function(timeoutKey) { + self.scheduledFunctions[timeoutKey] = jasmine.undefined; + }; + + self.clearInterval = function(timeoutKey) { + self.scheduledFunctions[timeoutKey] = jasmine.undefined; + }; + +}; + +jasmine.FakeTimer.prototype.reset = function() { + this.timeoutsMade = 0; + this.scheduledFunctions = {}; + this.nowMillis = 0; +}; + +jasmine.FakeTimer.prototype.tick = function(millis) { + var oldMillis = this.nowMillis; + var newMillis = oldMillis + millis; + this.runFunctionsWithinRange(oldMillis, newMillis); + this.nowMillis = newMillis; +}; + +jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) { + var scheduledFunc; + var funcsToRun = []; + for (var timeoutKey in this.scheduledFunctions) { + scheduledFunc = this.scheduledFunctions[timeoutKey]; + if (scheduledFunc != jasmine.undefined && + scheduledFunc.runAtMillis >= oldMillis && + scheduledFunc.runAtMillis <= nowMillis) { + funcsToRun.push(scheduledFunc); + this.scheduledFunctions[timeoutKey] = jasmine.undefined; + } + } + + if (funcsToRun.length > 0) { + funcsToRun.sort(function(a, b) { + return a.runAtMillis - b.runAtMillis; + }); + for (var i = 0; i < funcsToRun.length; ++i) { + try { + var funcToRun = funcsToRun[i]; + this.nowMillis = funcToRun.runAtMillis; + funcToRun.funcToCall(); + if (funcToRun.recurring) { + this.scheduleFunction(funcToRun.timeoutKey, + funcToRun.funcToCall, + funcToRun.millis, + true); + } + } catch(e) { + } + } + this.runFunctionsWithinRange(oldMillis, nowMillis); + } +}; + +jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) { + this.scheduledFunctions[timeoutKey] = { + runAtMillis: this.nowMillis + millis, + funcToCall: funcToCall, + recurring: recurring, + timeoutKey: timeoutKey, + millis: millis + }; +}; + +/** + * @namespace + */ +jasmine.Clock = { + defaultFakeTimer: new jasmine.FakeTimer(), + + reset: function() { + jasmine.Clock.assertInstalled(); + jasmine.Clock.defaultFakeTimer.reset(); + }, + + tick: function(millis) { + jasmine.Clock.assertInstalled(); + jasmine.Clock.defaultFakeTimer.tick(millis); + }, + + runFunctionsWithinRange: function(oldMillis, nowMillis) { + jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis); + }, + + scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) { + jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring); + }, + + useMock: function() { + if (!jasmine.Clock.isInstalled()) { + var spec = jasmine.getEnv().currentSpec; + spec.after(jasmine.Clock.uninstallMock); + + jasmine.Clock.installMock(); + } + }, + + installMock: function() { + jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer; + }, + + uninstallMock: function() { + jasmine.Clock.assertInstalled(); + jasmine.Clock.installed = jasmine.Clock.real; + }, + + real: { + setTimeout: jasmine.getGlobal().setTimeout, + clearTimeout: jasmine.getGlobal().clearTimeout, + setInterval: jasmine.getGlobal().setInterval, + clearInterval: jasmine.getGlobal().clearInterval + }, + + assertInstalled: function() { + if (!jasmine.Clock.isInstalled()) { + throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()"); + } + }, + + isInstalled: function() { + return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer; + }, + + installed: null +}; +jasmine.Clock.installed = jasmine.Clock.real; + +//else for IE support +jasmine.getGlobal().setTimeout = function(funcToCall, millis) { + if (jasmine.Clock.installed.setTimeout.apply) { + return jasmine.Clock.installed.setTimeout.apply(this, arguments); + } else { + return jasmine.Clock.installed.setTimeout(funcToCall, millis); + } +}; + +jasmine.getGlobal().setInterval = function(funcToCall, millis) { + if (jasmine.Clock.installed.setInterval.apply) { + return jasmine.Clock.installed.setInterval.apply(this, arguments); + } else { + return jasmine.Clock.installed.setInterval(funcToCall, millis); + } +}; + +jasmine.getGlobal().clearTimeout = function(timeoutKey) { + if (jasmine.Clock.installed.clearTimeout.apply) { + return jasmine.Clock.installed.clearTimeout.apply(this, arguments); + } else { + return jasmine.Clock.installed.clearTimeout(timeoutKey); + } +}; + +jasmine.getGlobal().clearInterval = function(timeoutKey) { + if (jasmine.Clock.installed.clearTimeout.apply) { + return jasmine.Clock.installed.clearInterval.apply(this, arguments); + } else { + return jasmine.Clock.installed.clearInterval(timeoutKey); + } +}; + +/** + * @constructor + */ +jasmine.MultiReporter = function() { + this.subReporters_ = []; +}; +jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter); + +jasmine.MultiReporter.prototype.addReporter = function(reporter) { + this.subReporters_.push(reporter); +}; + +(function() { + var functionNames = [ + "reportRunnerStarting", + "reportRunnerResults", + "reportSuiteResults", + "reportSpecStarting", + "reportSpecResults", + "log" + ]; + for (var i = 0; i < functionNames.length; i++) { + var functionName = functionNames[i]; + jasmine.MultiReporter.prototype[functionName] = (function(functionName) { + return function() { + for (var j = 0; j < this.subReporters_.length; j++) { + var subReporter = this.subReporters_[j]; + if (subReporter[functionName]) { + subReporter[functionName].apply(subReporter, arguments); + } + } + }; + })(functionName); + } +})(); +/** + * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults + * + * @constructor + */ +jasmine.NestedResults = function() { + /** + * The total count of results + */ + this.totalCount = 0; + /** + * Number of passed results + */ + this.passedCount = 0; + /** + * Number of failed results + */ + this.failedCount = 0; + /** + * Was this suite/spec skipped? + */ + this.skipped = false; + /** + * @ignore + */ + this.items_ = []; +}; + +/** + * Roll up the result counts. + * + * @param result + */ +jasmine.NestedResults.prototype.rollupCounts = function(result) { + this.totalCount += result.totalCount; + this.passedCount += result.passedCount; + this.failedCount += result.failedCount; +}; + +/** + * Adds a log message. + * @param values Array of message parts which will be concatenated later. + */ +jasmine.NestedResults.prototype.log = function(values) { + this.items_.push(new jasmine.MessageResult(values)); +}; + +/** + * Getter for the results: message & results. + */ +jasmine.NestedResults.prototype.getItems = function() { + return this.items_; +}; + +/** + * Adds a result, tracking counts (total, passed, & failed) + * @param {jasmine.ExpectationResult|jasmine.NestedResults} result + */ +jasmine.NestedResults.prototype.addResult = function(result) { + if (result.type != 'log') { + if (result.items_) { + this.rollupCounts(result); + } else { + this.totalCount++; + if (result.passed()) { + this.passedCount++; + } else { + this.failedCount++; + } + } + } + this.items_.push(result); +}; + +/** + * @returns {Boolean} True if everything below passed + */ +jasmine.NestedResults.prototype.passed = function() { + return this.passedCount === this.totalCount; +}; +/** + * Base class for pretty printing for expectation results. + */ +jasmine.PrettyPrinter = function() { + this.ppNestLevel_ = 0; +}; + +/** + * Formats a value in a nice, human-readable string. + * + * @param value + */ +jasmine.PrettyPrinter.prototype.format = function(value) { + if (this.ppNestLevel_ > 40) { + throw new Error('jasmine.PrettyPrinter: format() nested too deeply!'); + } + + this.ppNestLevel_++; + try { + if (value === jasmine.undefined) { + this.emitScalar('undefined'); + } else if (value === null) { + this.emitScalar('null'); + } else if (value === jasmine.getGlobal()) { + this.emitScalar(''); + } else if (value.jasmineToString) { + this.emitScalar(value.jasmineToString()); + } else if (typeof value === 'string') { + this.emitString(value); + } else if (jasmine.isSpy(value)) { + this.emitScalar("spy on " + value.identity); + } else if (value instanceof RegExp) { + this.emitScalar(value.toString()); + } else if (typeof value === 'function') { + this.emitScalar('Function'); + } else if (typeof value.nodeType === 'number') { + this.emitScalar('HTMLNode'); + } else if (value instanceof Date) { + this.emitScalar('Date(' + value + ')'); + } else if (value.__Jasmine_been_here_before__) { + this.emitScalar(''); + } else if (jasmine.isArray_(value) || typeof value == 'object') { + value.__Jasmine_been_here_before__ = true; + if (jasmine.isArray_(value)) { + this.emitArray(value); + } else { + this.emitObject(value); + } + delete value.__Jasmine_been_here_before__; + } else { + this.emitScalar(value.toString()); + } + } finally { + this.ppNestLevel_--; + } +}; + +jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) { + for (var property in obj) { + if (property == '__Jasmine_been_here_before__') continue; + fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && + obj.__lookupGetter__(property) !== null) : false); + } +}; + +jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_; +jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_; +jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_; +jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_; + +jasmine.StringPrettyPrinter = function() { + jasmine.PrettyPrinter.call(this); + + this.string = ''; +}; +jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter); + +jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) { + this.append(value); +}; + +jasmine.StringPrettyPrinter.prototype.emitString = function(value) { + this.append("'" + value + "'"); +}; + +jasmine.StringPrettyPrinter.prototype.emitArray = function(array) { + this.append('[ '); + for (var i = 0; i < array.length; i++) { + if (i > 0) { + this.append(', '); + } + this.format(array[i]); + } + this.append(' ]'); +}; + +jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) { + var self = this; + this.append('{ '); + var first = true; + + this.iterateObject(obj, function(property, isGetter) { + if (first) { + first = false; + } else { + self.append(', '); + } + + self.append(property); + self.append(' : '); + if (isGetter) { + self.append(''); + } else { + self.format(obj[property]); + } + }); + + this.append(' }'); +}; + +jasmine.StringPrettyPrinter.prototype.append = function(value) { + this.string += value; +}; +jasmine.Queue = function(env) { + this.env = env; + this.blocks = []; + this.running = false; + this.index = 0; + this.offset = 0; + this.abort = false; +}; + +jasmine.Queue.prototype.addBefore = function(block) { + this.blocks.unshift(block); +}; + +jasmine.Queue.prototype.add = function(block) { + this.blocks.push(block); +}; + +jasmine.Queue.prototype.insertNext = function(block) { + this.blocks.splice((this.index + this.offset + 1), 0, block); + this.offset++; +}; + +jasmine.Queue.prototype.start = function(onComplete) { + this.running = true; + this.onComplete = onComplete; + this.next_(); +}; + +jasmine.Queue.prototype.isRunning = function() { + return this.running; +}; + +jasmine.Queue.LOOP_DONT_RECURSE = true; + +jasmine.Queue.prototype.next_ = function() { + var self = this; + var goAgain = true; + + while (goAgain) { + goAgain = false; + + if (self.index < self.blocks.length && !this.abort) { + var calledSynchronously = true; + var completedSynchronously = false; + + var onComplete = function () { + if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) { + completedSynchronously = true; + return; + } + + if (self.blocks[self.index].abort) { + self.abort = true; + } + + self.offset = 0; + self.index++; + + var now = new Date().getTime(); + if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) { + self.env.lastUpdate = now; + self.env.setTimeout(function() { + self.next_(); + }, 0); + } else { + if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) { + goAgain = true; + } else { + self.next_(); + } + } + }; + self.blocks[self.index].execute(onComplete); + + calledSynchronously = false; + if (completedSynchronously) { + onComplete(); + } + + } else { + self.running = false; + if (self.onComplete) { + self.onComplete(); + } + } + } +}; + +jasmine.Queue.prototype.results = function() { + var results = new jasmine.NestedResults(); + for (var i = 0; i < this.blocks.length; i++) { + if (this.blocks[i].results) { + results.addResult(this.blocks[i].results()); + } + } + return results; +}; + + +/** + * Runner + * + * @constructor + * @param {jasmine.Env} env + */ +jasmine.Runner = function(env) { + var self = this; + self.env = env; + self.queue = new jasmine.Queue(env); + self.before_ = []; + self.after_ = []; + self.suites_ = []; +}; + +jasmine.Runner.prototype.execute = function() { + var self = this; + if (self.env.reporter.reportRunnerStarting) { + self.env.reporter.reportRunnerStarting(this); + } + self.queue.start(function () { + self.finishCallback(); + }); +}; + +jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) { + beforeEachFunction.typeName = 'beforeEach'; + this.before_.splice(0,0,beforeEachFunction); +}; + +jasmine.Runner.prototype.afterEach = function(afterEachFunction) { + afterEachFunction.typeName = 'afterEach'; + this.after_.splice(0,0,afterEachFunction); +}; + + +jasmine.Runner.prototype.finishCallback = function() { + this.env.reporter.reportRunnerResults(this); +}; + +jasmine.Runner.prototype.addSuite = function(suite) { + this.suites_.push(suite); +}; + +jasmine.Runner.prototype.add = function(block) { + if (block instanceof jasmine.Suite) { + this.addSuite(block); + } + this.queue.add(block); +}; + +jasmine.Runner.prototype.specs = function () { + var suites = this.suites(); + var specs = []; + for (var i = 0; i < suites.length; i++) { + specs = specs.concat(suites[i].specs()); + } + return specs; +}; + +jasmine.Runner.prototype.suites = function() { + return this.suites_; +}; + +jasmine.Runner.prototype.topLevelSuites = function() { + var topLevelSuites = []; + for (var i = 0; i < this.suites_.length; i++) { + if (!this.suites_[i].parentSuite) { + topLevelSuites.push(this.suites_[i]); + } + } + return topLevelSuites; +}; + +jasmine.Runner.prototype.results = function() { + return this.queue.results(); +}; +/** + * Internal representation of a Jasmine specification, or test. + * + * @constructor + * @param {jasmine.Env} env + * @param {jasmine.Suite} suite + * @param {String} description + */ +jasmine.Spec = function(env, suite, description) { + if (!env) { + throw new Error('jasmine.Env() required'); + } + if (!suite) { + throw new Error('jasmine.Suite() required'); + } + var spec = this; + spec.id = env.nextSpecId ? env.nextSpecId() : null; + spec.env = env; + spec.suite = suite; + spec.description = description; + spec.queue = new jasmine.Queue(env); + + spec.afterCallbacks = []; + spec.spies_ = []; + + spec.results_ = new jasmine.NestedResults(); + spec.results_.description = description; + spec.matchersClass = null; +}; + +jasmine.Spec.prototype.getFullName = function() { + return this.suite.getFullName() + ' ' + this.description + '.'; +}; + + +jasmine.Spec.prototype.results = function() { + return this.results_; +}; + +/** + * All parameters are pretty-printed and concatenated together, then written to the spec's output. + * + * Be careful not to leave calls to jasmine.log in production code. + */ +jasmine.Spec.prototype.log = function() { + return this.results_.log(arguments); +}; + +jasmine.Spec.prototype.runs = function (func) { + var block = new jasmine.Block(this.env, func, this); + this.addToQueue(block); + return this; +}; + +jasmine.Spec.prototype.addToQueue = function (block) { + if (this.queue.isRunning()) { + this.queue.insertNext(block); + } else { + this.queue.add(block); + } +}; + +/** + * @param {jasmine.ExpectationResult} result + */ +jasmine.Spec.prototype.addMatcherResult = function(result) { + this.results_.addResult(result); +}; + +jasmine.Spec.prototype.expect = function(actual) { + var positive = new (this.getMatchersClass_())(this.env, actual, this); + positive.not = new (this.getMatchersClass_())(this.env, actual, this, true); + return positive; +}; + +/** + * Waits a fixed time period before moving to the next block. + * + * @deprecated Use waitsFor() instead + * @param {Number} timeout milliseconds to wait + */ +jasmine.Spec.prototype.waits = function(timeout) { + var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this); + this.addToQueue(waitsFunc); + return this; +}; + +/** + * Waits for the latchFunction to return true before proceeding to the next block. + * + * @param {Function} latchFunction + * @param {String} optional_timeoutMessage + * @param {Number} optional_timeout + */ +jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { + var latchFunction_ = null; + var optional_timeoutMessage_ = null; + var optional_timeout_ = null; + + for (var i = 0; i < arguments.length; i++) { + var arg = arguments[i]; + switch (typeof arg) { + case 'function': + latchFunction_ = arg; + break; + case 'string': + optional_timeoutMessage_ = arg; + break; + case 'number': + optional_timeout_ = arg; + break; + } + } + + var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this); + this.addToQueue(waitsForFunc); + return this; +}; + +jasmine.Spec.prototype.fail = function (e) { + var expectationResult = new jasmine.ExpectationResult({ + passed: false, + message: e ? jasmine.util.formatException(e) : 'Exception', + trace: { stack: e.stack } + }); + this.results_.addResult(expectationResult); +}; + +jasmine.Spec.prototype.getMatchersClass_ = function() { + return this.matchersClass || this.env.matchersClass; +}; + +jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { + var parent = this.getMatchersClass_(); + var newMatchersClass = function() { + parent.apply(this, arguments); + }; + jasmine.util.inherit(newMatchersClass, parent); + jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass); + this.matchersClass = newMatchersClass; +}; + +jasmine.Spec.prototype.finishCallback = function() { + this.env.reporter.reportSpecResults(this); +}; + +jasmine.Spec.prototype.finish = function(onComplete) { + this.removeAllSpies(); + this.finishCallback(); + if (onComplete) { + onComplete(); + } +}; + +jasmine.Spec.prototype.after = function(doAfter) { + if (this.queue.isRunning()) { + this.queue.add(new jasmine.Block(this.env, doAfter, this)); + } else { + this.afterCallbacks.unshift(doAfter); + } +}; + +jasmine.Spec.prototype.execute = function(onComplete) { + var spec = this; + if (!spec.env.specFilter(spec)) { + spec.results_.skipped = true; + spec.finish(onComplete); + return; + } + + this.env.reporter.reportSpecStarting(this); + + spec.env.currentSpec = spec; + + spec.addBeforesAndAftersToQueue(); + + spec.queue.start(function () { + spec.finish(onComplete); + }); +}; + +jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() { + var runner = this.env.currentRunner(); + var i; + + for (var suite = this.suite; suite; suite = suite.parentSuite) { + for (i = 0; i < suite.before_.length; i++) { + this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this)); + } + } + for (i = 0; i < runner.before_.length; i++) { + this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this)); + } + for (i = 0; i < this.afterCallbacks.length; i++) { + this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this)); + } + for (suite = this.suite; suite; suite = suite.parentSuite) { + for (i = 0; i < suite.after_.length; i++) { + this.queue.add(new jasmine.Block(this.env, suite.after_[i], this)); + } + } + for (i = 0; i < runner.after_.length; i++) { + this.queue.add(new jasmine.Block(this.env, runner.after_[i], this)); + } +}; + +jasmine.Spec.prototype.explodes = function() { + throw 'explodes function should not have been called'; +}; + +jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) { + if (obj == jasmine.undefined) { + throw "spyOn could not find an object to spy upon for " + methodName + "()"; + } + + if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) { + throw methodName + '() method does not exist'; + } + + if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) { + throw new Error(methodName + ' has already been spied upon'); + } + + var spyObj = jasmine.createSpy(methodName); + + this.spies_.push(spyObj); + spyObj.baseObj = obj; + spyObj.methodName = methodName; + spyObj.originalValue = obj[methodName]; + + obj[methodName] = spyObj; + + return spyObj; +}; + +jasmine.Spec.prototype.removeAllSpies = function() { + for (var i = 0; i < this.spies_.length; i++) { + var spy = this.spies_[i]; + spy.baseObj[spy.methodName] = spy.originalValue; + } + this.spies_ = []; +}; + +/** + * Internal representation of a Jasmine suite. + * + * @constructor + * @param {jasmine.Env} env + * @param {String} description + * @param {Function} specDefinitions + * @param {jasmine.Suite} parentSuite + */ +jasmine.Suite = function(env, description, specDefinitions, parentSuite) { + var self = this; + self.id = env.nextSuiteId ? env.nextSuiteId() : null; + self.description = description; + self.queue = new jasmine.Queue(env); + self.parentSuite = parentSuite; + self.env = env; + self.before_ = []; + self.after_ = []; + self.children_ = []; + self.suites_ = []; + self.specs_ = []; +}; + +jasmine.Suite.prototype.getFullName = function() { + var fullName = this.description; + for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { + fullName = parentSuite.description + ' ' + fullName; + } + return fullName; +}; + +jasmine.Suite.prototype.finish = function(onComplete) { + this.env.reporter.reportSuiteResults(this); + this.finished = true; + if (typeof(onComplete) == 'function') { + onComplete(); + } +}; + +jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) { + beforeEachFunction.typeName = 'beforeEach'; + this.before_.unshift(beforeEachFunction); +}; + +jasmine.Suite.prototype.afterEach = function(afterEachFunction) { + afterEachFunction.typeName = 'afterEach'; + this.after_.unshift(afterEachFunction); +}; + +jasmine.Suite.prototype.results = function() { + return this.queue.results(); +}; + +jasmine.Suite.prototype.add = function(suiteOrSpec) { + this.children_.push(suiteOrSpec); + if (suiteOrSpec instanceof jasmine.Suite) { + this.suites_.push(suiteOrSpec); + this.env.currentRunner().addSuite(suiteOrSpec); + } else { + this.specs_.push(suiteOrSpec); + } + this.queue.add(suiteOrSpec); +}; + +jasmine.Suite.prototype.specs = function() { + return this.specs_; +}; + +jasmine.Suite.prototype.suites = function() { + return this.suites_; +}; + +jasmine.Suite.prototype.children = function() { + return this.children_; +}; + +jasmine.Suite.prototype.execute = function(onComplete) { + var self = this; + this.queue.start(function () { + self.finish(onComplete); + }); +}; +jasmine.WaitsBlock = function(env, timeout, spec) { + this.timeout = timeout; + jasmine.Block.call(this, env, null, spec); +}; + +jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block); + +jasmine.WaitsBlock.prototype.execute = function (onComplete) { + if (jasmine.VERBOSE) { + this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...'); + } + this.env.setTimeout(function () { + onComplete(); + }, this.timeout); +}; +/** + * A block which waits for some condition to become true, with timeout. + * + * @constructor + * @extends jasmine.Block + * @param {jasmine.Env} env The Jasmine environment. + * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true. + * @param {Function} latchFunction A function which returns true when the desired condition has been met. + * @param {String} message The message to display if the desired condition hasn't been met within the given time period. + * @param {jasmine.Spec} spec The Jasmine spec. + */ +jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) { + this.timeout = timeout || env.defaultTimeoutInterval; + this.latchFunction = latchFunction; + this.message = message; + this.totalTimeSpentWaitingForLatch = 0; + jasmine.Block.call(this, env, null, spec); +}; +jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block); + +jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10; + +jasmine.WaitsForBlock.prototype.execute = function(onComplete) { + if (jasmine.VERBOSE) { + this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen')); + } + var latchFunctionResult; + try { + latchFunctionResult = this.latchFunction.apply(this.spec); + } catch (e) { + this.spec.fail(e); + onComplete(); + return; + } + + if (latchFunctionResult) { + onComplete(); + } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) { + var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen'); + this.spec.fail({ + name: 'timeout', + message: message + }); + + this.abort = true; + onComplete(); + } else { + this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT; + var self = this; + this.env.setTimeout(function() { + self.execute(onComplete); + }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT); + } +}; + +jasmine.version_= { + "major": 1, + "minor": 2, + "build": 0, + "revision": 1333310630, + "release_candidate": 1 +}; diff --git a/plugins/org.apache.cordova.camera/test/autotest/pages/camera.html b/plugins/org.apache.cordova.camera/test/autotest/pages/camera.html new file mode 100644 index 0000000..c1dafee --- /dev/null +++ b/plugins/org.apache.cordova.camera/test/autotest/pages/camera.html @@ -0,0 +1,71 @@ + + + + + + + + Cordova: Camera API Specs + + + + + + + + + + + + + + + + + + + + + + + + + + + Back + + diff --git a/plugins/org.apache.cordova.camera/test/autotest/test-runner.js b/plugins/org.apache.cordova.camera/test/autotest/test-runner.js new file mode 100644 index 0000000..f72b3cc --- /dev/null +++ b/plugins/org.apache.cordova.camera/test/autotest/test-runner.js @@ -0,0 +1,62 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + +if (window.sessionStorage != null) { + window.sessionStorage.clear(); +} + +// Timeout is 2 seconds to allow physical devices enough +// time to query the response. This is important for some +// Android devices. +var Tests = function() {}; +Tests.TEST_TIMEOUT = 7500; + +// Creates a spy that will fail if called. +function createDoNotCallSpy(name, opt_extraMessage) { + return jasmine.createSpy().andCallFake(function() { + var errorMessage = name + ' should not have been called.'; + if (arguments.length) { + errorMessage += ' Got args: ' + JSON.stringify(arguments); + } + if (opt_extraMessage) { + errorMessage += '\n' + opt_extraMessage; + } + expect(false).toBe(true, errorMessage); + }); +} + +// Waits for any of the given spys to be called. +// Last param may be a custom timeout duration. +function waitsForAny() { + var spys = [].slice.call(arguments); + var timeout = Tests.TEST_TIMEOUT; + if (typeof spys[spys.length - 1] == 'number') { + timeout = spys.pop(); + } + waitsFor(function() { + for (var i = 0; i < spys.length; ++i) { + if (spys[i].wasCalled) { + return true; + } + } + return false; + }, "Expecting callbacks to be called.", timeout); +} diff --git a/plugins/org.apache.cordova.camera/test/autotest/tests/camera.tests.js b/plugins/org.apache.cordova.camera/test/autotest/tests/camera.tests.js new file mode 100644 index 0000000..ee5bacd --- /dev/null +++ b/plugins/org.apache.cordova.camera/test/autotest/tests/camera.tests.js @@ -0,0 +1,70 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + +describe('Camera (navigator.camera)', function () { + it("should exist", function() { + expect(navigator.camera).toBeDefined(); + }); + + it("should contain a getPicture function", function() { + expect(navigator.camera.getPicture).toBeDefined(); + expect(typeof navigator.camera.getPicture == 'function').toBe(true); + }); +}); + +describe('Camera Constants (window.Camera + navigator.camera)', function () { + it("camera.spec.1 window.Camera should exist", function() { + expect(window.Camera).toBeDefined(); + }); + + it("camera.spec.2 should contain three DestinationType constants", function() { + expect(Camera.DestinationType.DATA_URL).toBe(0); + expect(Camera.DestinationType.FILE_URI).toBe(1); + expect(Camera.DestinationType.NATIVE_URI).toBe(2); + expect(navigator.camera.DestinationType.DATA_URL).toBe(0); + expect(navigator.camera.DestinationType.FILE_URI).toBe(1); + expect(navigator.camera.DestinationType.NATIVE_URI).toBe(2); + }); + + it("camera.spec.3 should contain two EncodingType constants", function() { + expect(Camera.EncodingType.JPEG).toBe(0); + expect(Camera.EncodingType.PNG).toBe(1); + expect(navigator.camera.EncodingType.JPEG).toBe(0); + expect(navigator.camera.EncodingType.PNG).toBe(1); + }); + + it("camera.spec.4 should contain three MediaType constants", function() { + expect(Camera.MediaType.PICTURE).toBe(0); + expect(Camera.MediaType.VIDEO).toBe(1); + expect(Camera.MediaType.ALLMEDIA).toBe(2); + expect(navigator.camera.MediaType.PICTURE).toBe(0); + expect(navigator.camera.MediaType.VIDEO).toBe(1); + expect(navigator.camera.MediaType.ALLMEDIA).toBe(2); + }); + it("camera.spec.5 should contain three PictureSourceType constants", function() { + expect(Camera.PictureSourceType.PHOTOLIBRARY).toBe(0); + expect(Camera.PictureSourceType.CAMERA).toBe(1); + expect(Camera.PictureSourceType.SAVEDPHOTOALBUM).toBe(2); + expect(navigator.camera.PictureSourceType.PHOTOLIBRARY).toBe(0); + expect(navigator.camera.PictureSourceType.CAMERA).toBe(1); + expect(navigator.camera.PictureSourceType.SAVEDPHOTOALBUM).toBe(2); + }); +}); diff --git a/plugins/org.apache.cordova.camera/test/camera/index.html b/plugins/org.apache.cordova.camera/test/camera/index.html new file mode 100644 index 0000000..2495a16 --- /dev/null +++ b/plugins/org.apache.cordova.camera/test/camera/index.html @@ -0,0 +1,394 @@ + + + + + + + + + Cordova Mobile Spec + + + + + + + + + +

Camera

+
+ Status:
+ img: + canvas: +
+

Cordova Camera API

+
+
camera.getPicture()
+

Native File Inputs

+
input type=file
+
capture=camera
+
capture=camcorder
+
capture=microphone
+

Actions

+
Get File Metadata
+
Read with FileReader
+
Copy Image
+
Write Image
+
Upload Image
+
Draw Using Canvas
+
Remove Image
+

Back
+ + diff --git a/plugins/org.apache.cordova.camera/test/cordova-incl.js b/plugins/org.apache.cordova.camera/test/cordova-incl.js new file mode 100644 index 0000000..dbcd1a6 --- /dev/null +++ b/plugins/org.apache.cordova.camera/test/cordova-incl.js @@ -0,0 +1,70 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + +var PLAT; +if (/Android/.exec(navigator.userAgent)) { + PLAT = 'android'; +} else if (/(iPad)|(iPhone)|(iPod)/.exec(navigator.userAgent)) { + PLAT = 'ios'; +} else if (/(BB10)|(PlayBook)|(BlackBerry)/.exec(navigator.userAgent)) { + PLAT = 'blackberry'; +} + +var scripts = document.getElementsByTagName('script'); +var currentPath = scripts[scripts.length - 1].src; +var platformCordovaPath = currentPath.replace("cordova-incl.js", "cordova." + PLAT + ".js"); +var normalCordovaPath = currentPath.replace("cordova-incl.js", "cordova.js"); +var cordovaPath = normalCordovaPath; + +if (PLAT) { + // XHR to local file is an error on some platforms, windowsphone for one + try { + var xhr = new XMLHttpRequest(); + xhr.open("GET", platformCordovaPath, false); + xhr.onreadystatechange = function() { + + if (this.readyState == this.DONE && this.responseText.length > 0) { + if(parseInt(this.status) >= 400){ + cordovaPath = normalCordovaPath; + }else{ + cordovaPath = platformCordovaPath; + } + } + }; + xhr.send(null); + } + catch(e){ + cordovaPath = normalCordovaPath; + } // access denied! +} + +if (!window._doNotWriteCordovaScript) { + document.write(''); +} + +function backHome() { + if (window.device && device.platform && device.platform.toLowerCase() == 'android') { + navigator.app.backHistory(); + } + else { + window.history.go(-1); + } +} diff --git a/plugins/org.apache.cordova.camera/test/index.html b/plugins/org.apache.cordova.camera/test/index.html new file mode 100644 index 0000000..2c5447b --- /dev/null +++ b/plugins/org.apache.cordova.camera/test/index.html @@ -0,0 +1,65 @@ + + + + + + + + + Cordova Mobile Spec + + + + + + +

Apache Cordova Tests

+
+

Platform:

+

Version:

+

UUID:

+

Name:

+

Model:

+

Width: , Height: + , Color Depth:

+

User-Agent:

+
+ Automatic Test + Accelerometer + Audio Play/Record + Battery + Camera + Compass + Contacts + Events + Location + Lazy Loading of cordova-incl.js + Misc Content + Network + Notification + Splashscreen + Web SQL + Local Storage + Benchmarks + In App Browser + + diff --git a/plugins/org.apache.cordova.camera/test/main.js b/plugins/org.apache.cordova.camera/test/main.js new file mode 100644 index 0000000..66c1bd3 --- /dev/null +++ b/plugins/org.apache.cordova.camera/test/main.js @@ -0,0 +1,163 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + +var deviceInfo = function() { + document.getElementById("platform").innerHTML = device.platform; + document.getElementById("version").innerHTML = device.version; + document.getElementById("uuid").innerHTML = device.uuid; + document.getElementById("name").innerHTML = device.name; + document.getElementById("model").innerHTML = device.model; + document.getElementById("width").innerHTML = screen.width; + document.getElementById("height").innerHTML = screen.height; + document.getElementById("colorDepth").innerHTML = screen.colorDepth; +}; + +var getLocation = function() { + var suc = function(p) { + alert(p.coords.latitude + " " + p.coords.longitude); + }; + var locFail = function() { + }; + navigator.geolocation.getCurrentPosition(suc, locFail); +}; + +var beep = function() { + navigator.notification.beep(2); +}; + +var vibrate = function() { + navigator.notification.vibrate(0); +}; + +function roundNumber(num) { + var dec = 3; + var result = Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec); + return result; +} + +var accelerationWatch = null; + +function updateAcceleration(a) { + document.getElementById('x').innerHTML = roundNumber(a.x); + document.getElementById('y').innerHTML = roundNumber(a.y); + document.getElementById('z').innerHTML = roundNumber(a.z); +} + +var toggleAccel = function() { + if (accelerationWatch !== null) { + navigator.accelerometer.clearWatch(accelerationWatch); + updateAcceleration({ + x : "", + y : "", + z : "" + }); + accelerationWatch = null; + } else { + var options = {}; + options.frequency = 1000; + accelerationWatch = navigator.accelerometer.watchAcceleration( + updateAcceleration, function(ex) { + alert("accel fail (" + ex.name + ": " + ex.message + ")"); + }, options); + } +}; + +var preventBehavior = function(e) { + e.preventDefault(); +}; + +function dump_pic(data) { + var viewport = document.getElementById('viewport'); + console.log(data); + viewport.style.display = ""; + viewport.style.position = "absolute"; + viewport.style.top = "10px"; + viewport.style.left = "10px"; + document.getElementById("test_img").src = "data:image/jpeg;base64," + data; +} + +function fail(msg) { + alert(msg); +} + +function show_pic() { + navigator.camera.getPicture(dump_pic, fail, { + quality : 50 + }); +} + +function close() { + var viewport = document.getElementById('viewport'); + viewport.style.position = "relative"; + viewport.style.display = "none"; +} + +// This is just to do this. +function readFile() { + navigator.file.read('/sdcard/cordova.txt', fail, fail); +} + +function writeFile() { + navigator.file.write('foo.txt', "This is a test of writing to a file", + fail, fail); +} + +function contacts_success(contacts) { + alert(contacts.length + + ' contacts returned.' + + (contacts[2] && contacts[2].name ? (' Third contact is ' + contacts[2].name.formatted) + : '')); +} + +function get_contacts() { + var obj = new ContactFindOptions(); + obj.filter = ""; + obj.multiple = true; + obj.limit = 5; + navigator.service.contacts.find( + [ "displayName", "name" ], contacts_success, + fail, obj); +} + +var networkReachableCallback = function(reachability) { + // There is no consistency on the format of reachability + var networkState = reachability.code || reachability; + + var currentState = {}; + currentState[NetworkStatus.NOT_REACHABLE] = 'No network connection'; + currentState[NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK] = 'Carrier data connection'; + currentState[NetworkStatus.REACHABLE_VIA_WIFI_NETWORK] = 'WiFi connection'; + + confirm("Connection type:\n" + currentState[networkState]); +}; + +function check_network() { + navigator.network.isReachable("www.mobiledevelopersolutions.com", + networkReachableCallback, {}); +} + +function init() { + // the next line makes it impossible to see Contacts on the HTC Evo since it + // doesn't have a scroll button + // document.addEventListener("touchmove", preventBehavior, false); + document.addEventListener("deviceready", deviceInfo, true); + document.getElementById("user-agent").textContent = navigator.userAgent; +} diff --git a/plugins/org.apache.cordova.camera/test/master.css b/plugins/org.apache.cordova.camera/test/master.css new file mode 100644 index 0000000..e93c937 --- /dev/null +++ b/plugins/org.apache.cordova.camera/test/master.css @@ -0,0 +1,164 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + + body { + background:#222 none repeat scroll 0 0; + color:#666; + font-family:Helvetica; + font-size:72%; + line-height:1.5em; + margin:0; + border-top:1px solid #393939; + } + + #info{ + background:#ffa; + border: 1px solid #ffd324; + -webkit-border-radius: 5px; + border-radius: 5px; + clear:both; + margin:15px 6px 0; + min-width:295px; + max-width:97%; + padding:4px 0px 2px 10px; + word-wrap:break-word; + margin-bottom:10px; + display:inline-block; + min-height: 160px; + max-height: 300px; + overflow: auto; + -webkit-overflow-scrolling: touch; + } + + #info > h4{ + font-size:.95em; + margin:5px 0; + } + + #stage.theme{ + padding-top:3px; + } + + /* Definition List */ + #stage.theme > dl{ + padding-top:10px; + clear:both; + margin:0; + list-style-type:none; + padding-left:10px; + overflow:auto; + } + + #stage.theme > dl > dt{ + font-weight:bold; + float:left; + margin-left:5px; + } + + #stage.theme > dl > dd{ + width:45px; + float:left; + color:#a87; + font-weight:bold; + } + + /* Content Styling */ + #stage.theme > h1, #stage.theme > h2, #stage.theme > p{ + margin:1em 0 .5em 13px; + } + + #stage.theme > h1{ + color:#eee; + font-size:1.6em; + text-align:center; + margin:0; + margin-top:15px; + padding:0; + } + + #stage.theme > h2{ + clear:both; + margin:0; + padding:3px; + font-size:1em; + text-align:center; + } + + /* Stage Buttons */ + #stage.theme .btn{ + border: 1px solid #555; + -webkit-border-radius: 5px; + border-radius: 5px; + text-align:center; + display:inline-block; + background:#444; + width:150px; + color:#9ab; + font-size:1.1em; + text-decoration:none; + padding:1.2em 0; + margin:3px 0px 3px 5px; + } + + #stage.theme .large{ + width:308px; + padding:1.2em 0; + } + + #stage.theme .wide{ + width:100%; + padding:1.2em 0; + } + + #stage.theme .backBtn{ + border: 1px solid #555; + -webkit-border-radius: 5px; + border-radius: 5px; + text-align:center; + display:block; + float:right; + background:#666; + width:75px; + color:#9ab; + font-size:1.1em; + text-decoration:none; + padding:1.2em 0; + margin:3px 5px 3px 5px; + } + + #stage.theme .input{ + border: 1px solid #555; + -webkit-border-radius: 5px; + border-radius: 5px; + text-align:center; + display:block; + float:light; + background:#888; + color:#9cd; + font-size:1.1em; + text-decoration:none; + padding:1.2em 0; + margin:3px 0px 3px 5px; + } + + #stage.theme .numeric{ + width:100%; + } diff --git a/plugins/org.apache.cordova.camera/www/Camera.js b/plugins/org.apache.cordova.camera/www/Camera.js new file mode 100644 index 0000000..b2da5da --- /dev/null +++ b/plugins/org.apache.cordova.camera/www/Camera.js @@ -0,0 +1,73 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + +var argscheck = require('cordova/argscheck'), + exec = require('cordova/exec'), + Camera = require('./Camera'), + CameraPopoverHandle = require('./CameraPopoverHandle'); + +var cameraExport = {}; + +// Tack on the Camera Constants to the base camera plugin. +for (var key in Camera) { + cameraExport[key] = Camera[key]; +} + +/** + * Gets a picture from source defined by "options.sourceType", and returns the + * image as defined by the "options.destinationType" option. + + * The defaults are sourceType=CAMERA and destinationType=FILE_URI. + * + * @param {Function} successCallback + * @param {Function} errorCallback + * @param {Object} options + */ +cameraExport.getPicture = function(successCallback, errorCallback, options) { + argscheck.checkArgs('fFO', 'Camera.getPicture', arguments); + options = options || {}; + var getValue = argscheck.getValue; + + var quality = getValue(options.quality, 50); + var destinationType = getValue(options.destinationType, Camera.DestinationType.FILE_URI); + var sourceType = getValue(options.sourceType, Camera.PictureSourceType.CAMERA); + var targetWidth = getValue(options.targetWidth, -1); + var targetHeight = getValue(options.targetHeight, -1); + var encodingType = getValue(options.encodingType, Camera.EncodingType.JPEG); + var mediaType = getValue(options.mediaType, Camera.MediaType.PICTURE); + var allowEdit = !!options.allowEdit; + var correctOrientation = !!options.correctOrientation; + var saveToPhotoAlbum = !!options.saveToPhotoAlbum; + var popoverOptions = getValue(options.popoverOptions, null); + var cameraDirection = getValue(options.cameraDirection, Camera.Direction.BACK); + + var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType, + mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions, cameraDirection]; + + exec(successCallback, errorCallback, "Camera", "takePicture", args); + return new CameraPopoverHandle(); +}; + +cameraExport.cleanup = function(successCallback, errorCallback) { + exec(successCallback, errorCallback, "Camera", "cleanup", []); +}; + +module.exports = cameraExport; diff --git a/plugins/org.apache.cordova.camera/www/CameraConstants.js b/plugins/org.apache.cordova.camera/www/CameraConstants.js new file mode 100644 index 0000000..ae4e534 --- /dev/null +++ b/plugins/org.apache.cordova.camera/www/CameraConstants.js @@ -0,0 +1,53 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + +module.exports = { + DestinationType:{ + DATA_URL: 0, // Return base64 encoded string + FILE_URI: 1, // Return file uri (content://media/external/images/media/2 for Android) + NATIVE_URI: 2 // Return native uri (eg. asset-library://... for iOS) + }, + EncodingType:{ + JPEG: 0, // Return JPEG encoded image + PNG: 1 // Return PNG encoded image + }, + MediaType:{ + PICTURE: 0, // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType + VIDEO: 1, // allow selection of video only, ONLY RETURNS URL + ALLMEDIA : 2 // allow selection from all media types + }, + PictureSourceType:{ + PHOTOLIBRARY : 0, // Choose image from picture library (same as SAVEDPHOTOALBUM for Android) + CAMERA : 1, // Take picture from camera + SAVEDPHOTOALBUM : 2 // Choose image from picture library (same as PHOTOLIBRARY for Android) + }, + PopoverArrowDirection:{ + ARROW_UP : 1, // matches iOS UIPopoverArrowDirection constants to specify arrow location on popover + ARROW_DOWN : 2, + ARROW_LEFT : 4, + ARROW_RIGHT : 8, + ARROW_ANY : 15 + }, + Direction:{ + BACK: 0, + FRONT: 1 + } +}; diff --git a/plugins/org.apache.cordova.camera/www/CameraPopoverHandle.js b/plugins/org.apache.cordova.camera/www/CameraPopoverHandle.js new file mode 100644 index 0000000..7a4c32d --- /dev/null +++ b/plugins/org.apache.cordova.camera/www/CameraPopoverHandle.js @@ -0,0 +1,33 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + +var exec = require('cordova/exec'); + +/** + * A handle to an image picker popover. + */ +var CameraPopoverHandle = function() { + this.setPosition = function(popoverOptions) { + console.log('CameraPopoverHandle.setPosition is only supported on iOS.'); + }; +}; + +module.exports = CameraPopoverHandle; diff --git a/plugins/org.apache.cordova.camera/www/CameraPopoverOptions.js b/plugins/org.apache.cordova.camera/www/CameraPopoverOptions.js new file mode 100644 index 0000000..c024984 --- /dev/null +++ b/plugins/org.apache.cordova.camera/www/CameraPopoverOptions.js @@ -0,0 +1,37 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + +var Camera = require('./Camera'); + +/** + * Encapsulates options for iOS Popover image picker + */ +var CameraPopoverOptions = function(x,y,width,height,arrowDir){ + // information of rectangle that popover should be anchored to + this.x = x || 0; + this.y = y || 32; + this.width = width || 320; + this.height = height || 480; + // The direction of the popover arrow + this.arrowDir = arrowDir || Camera.PopoverArrowDirection.ARROW_ANY; +}; + +module.exports = CameraPopoverOptions; diff --git a/plugins/org.apache.cordova.camera/www/ios/CameraPopoverHandle.js b/plugins/org.apache.cordova.camera/www/ios/CameraPopoverHandle.js new file mode 100644 index 0000000..fc48c11 --- /dev/null +++ b/plugins/org.apache.cordova.camera/www/ios/CameraPopoverHandle.js @@ -0,0 +1,34 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + +var exec = require('cordova/exec'); + +/** + * A handle to an image picker popover. + */ +var CameraPopoverHandle = function() { + this.setPosition = function(popoverOptions) { + var args = [ popoverOptions ]; + exec(null, null, "Camera", "repositionPopover", args); + }; +}; + +module.exports = CameraPopoverHandle; diff --git a/plugins/org.apache.cordova.console/.fetch.json b/plugins/org.apache.cordova.console/.fetch.json new file mode 100644 index 0000000..a9cea3a --- /dev/null +++ b/plugins/org.apache.cordova.console/.fetch.json @@ -0,0 +1 @@ +{"source":{"type":"git","url":"https://git-wip-us.apache.org/repos/asf/cordova-plugin-console.git","subdir":"."}} \ No newline at end of file diff --git a/plugins/org.apache.cordova.console/LICENSE b/plugins/org.apache.cordova.console/LICENSE new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/plugins/org.apache.cordova.console/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/plugins/org.apache.cordova.console/README.md b/plugins/org.apache.cordova.console/README.md new file mode 100644 index 0000000..1363f3c --- /dev/null +++ b/plugins/org.apache.cordova.console/README.md @@ -0,0 +1,5 @@ +cordova-plugin-console +====================== +To install this plugin, follow the [Command-line Interface Guide](http://cordova.apache.org/docs/en/edge/guide_cli_index.md.html#The%20Command-line%20Interface). + +If you are not using the Cordova Command-line Interface, follow [Using Plugman to Manage Plugins](http://cordova.apache.org/docs/en/edge/plugin_ref_plugman.md.html). diff --git a/plugins/org.apache.cordova.console/RELEASENOTES.md b/plugins/org.apache.cordova.console/RELEASENOTES.md new file mode 100644 index 0000000..286b8f5 --- /dev/null +++ b/plugins/org.apache.cordova.console/RELEASENOTES.md @@ -0,0 +1,32 @@ + +# Release Notes + +### 0.2.3 (Sept 25, 2013) +* CB-4889 bumping&resetting version +* CB-4889 renaming org.apache.cordova.core.console to org.apache.cordova.console +* Rename CHANGELOG.md -> RELEASENOTES.md +* [CB-4752] Incremented plugin version on dev branch. + + ### 0.2.4 (Oct 28, 2013) +* CB-5154 log formatting incorrectly to native +* CB-5128: added repo + issue tag to plugin.xml for console plugin +* [CB-4915] Incremented plugin version on dev branch. \ No newline at end of file diff --git a/plugins/org.apache.cordova.console/plugin.xml b/plugins/org.apache.cordova.console/plugin.xml new file mode 100644 index 0000000..2bb3dd6 --- /dev/null +++ b/plugins/org.apache.cordova.console/plugin.xml @@ -0,0 +1,85 @@ + + + + + Console + Cordova Console Plugin + Apache 2.0 + cordova,console + https://git-wip-us.apache.org/repos/asf/cordova-plugin-console.git + https://issues.apache.org/jira/browse/CB/component/12320644 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/org.apache.cordova.console/src/ios/CDVLogger.h b/plugins/org.apache.cordova.console/src/ios/CDVLogger.h new file mode 100644 index 0000000..7cfb306 --- /dev/null +++ b/plugins/org.apache.cordova.console/src/ios/CDVLogger.h @@ -0,0 +1,26 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import + +@interface CDVLogger : CDVPlugin + +- (void)logLevel:(CDVInvokedUrlCommand*)command; + +@end diff --git a/plugins/org.apache.cordova.console/src/ios/CDVLogger.m b/plugins/org.apache.cordova.console/src/ios/CDVLogger.m new file mode 100644 index 0000000..fe99f6b --- /dev/null +++ b/plugins/org.apache.cordova.console/src/ios/CDVLogger.m @@ -0,0 +1,38 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVLogger.h" +#import + +@implementation CDVLogger + +/* log a message */ +- (void)logLevel:(CDVInvokedUrlCommand*)command +{ + id level = [command.arguments objectAtIndex:0]; + id message = [command.arguments objectAtIndex:1]; + + if ([level isEqualToString:@"LOG"]) { + NSLog(@"%@", message); + } else { + NSLog(@"%@: %@", level, message); + } +} + +@end diff --git a/plugins/org.apache.cordova.console/src/wp/DebugConsole.cs b/plugins/org.apache.cordova.console/src/wp/DebugConsole.cs new file mode 100644 index 0000000..fa9863a --- /dev/null +++ b/plugins/org.apache.cordova.console/src/wp/DebugConsole.cs @@ -0,0 +1,49 @@ +/* + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using System.Diagnostics; + +namespace WPCordovaClassLib.Cordova.Commands +{ + + public class DebugConsole : BaseCommand + { + // warn, error + public void log(string msg) + { + Debug.WriteLine("Log:" + msg); + } + + public void error(string msg) + { + Debug.WriteLine("Error:" + msg); + } + + public void warn(string msg) + { + Debug.WriteLine("Warn:" + msg); + } + + } +} diff --git a/plugins/org.apache.cordova.console/www/console-via-logger.js b/plugins/org.apache.cordova.console/www/console-via-logger.js new file mode 100644 index 0000000..4095eb3 --- /dev/null +++ b/plugins/org.apache.cordova.console/www/console-via-logger.js @@ -0,0 +1,187 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + +//------------------------------------------------------------------------------ + +var logger = require("./logger"); +var utils = require("cordova/utils"); + +//------------------------------------------------------------------------------ +// object that we're exporting +//------------------------------------------------------------------------------ +var console = module.exports; + +//------------------------------------------------------------------------------ +// copy of the original console object +//------------------------------------------------------------------------------ +var WinConsole = window.console; + +//------------------------------------------------------------------------------ +// whether to use the logger +//------------------------------------------------------------------------------ +var UseLogger = false; + +//------------------------------------------------------------------------------ +// Timers +//------------------------------------------------------------------------------ +var Timers = {}; + +//------------------------------------------------------------------------------ +// used for unimplemented methods +//------------------------------------------------------------------------------ +function noop() {} + +//------------------------------------------------------------------------------ +// used for unimplemented methods +//------------------------------------------------------------------------------ +console.useLogger = function (value) { + if (arguments.length) UseLogger = !!value; + + if (UseLogger) { + if (logger.useConsole()) { + throw new Error("console and logger are too intertwingly"); + } + } + + return UseLogger; +}; + +//------------------------------------------------------------------------------ +console.log = function() { + if (logger.useConsole()) return; + logger.log.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.error = function() { + if (logger.useConsole()) return; + logger.error.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.warn = function() { + if (logger.useConsole()) return; + logger.warn.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.info = function() { + if (logger.useConsole()) return; + logger.info.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.debug = function() { + if (logger.useConsole()) return; + logger.debug.apply(logger, [].slice.call(arguments)); +}; + +//------------------------------------------------------------------------------ +console.assert = function(expression) { + if (expression) return; + + var message = logger.format.apply(logger.format, [].slice.call(arguments, 1)); + console.log("ASSERT: " + message); +}; + +//------------------------------------------------------------------------------ +console.clear = function() {}; + +//------------------------------------------------------------------------------ +console.dir = function(object) { + console.log("%o", object); +}; + +//------------------------------------------------------------------------------ +console.dirxml = function(node) { + console.log(node.innerHTML); +}; + +//------------------------------------------------------------------------------ +console.trace = noop; + +//------------------------------------------------------------------------------ +console.group = console.log; + +//------------------------------------------------------------------------------ +console.groupCollapsed = console.log; + +//------------------------------------------------------------------------------ +console.groupEnd = noop; + +//------------------------------------------------------------------------------ +console.time = function(name) { + Timers[name] = new Date().valueOf(); +}; + +//------------------------------------------------------------------------------ +console.timeEnd = function(name) { + var timeStart = Timers[name]; + if (!timeStart) { + console.warn("unknown timer: " + name); + return; + } + + var timeElapsed = new Date().valueOf() - timeStart; + console.log(name + ": " + timeElapsed + "ms"); +}; + +//------------------------------------------------------------------------------ +console.timeStamp = noop; + +//------------------------------------------------------------------------------ +console.profile = noop; + +//------------------------------------------------------------------------------ +console.profileEnd = noop; + +//------------------------------------------------------------------------------ +console.count = noop; + +//------------------------------------------------------------------------------ +console.exception = console.log; + +//------------------------------------------------------------------------------ +console.table = function(data, columns) { + console.log("%o", data); +}; + +//------------------------------------------------------------------------------ +// return a new function that calls both functions passed as args +//------------------------------------------------------------------------------ +function wrappedOrigCall(orgFunc, newFunc) { + return function() { + var args = [].slice.call(arguments); + try { orgFunc.apply(WinConsole, args); } catch (e) {} + try { newFunc.apply(console, args); } catch (e) {} + }; +} + +//------------------------------------------------------------------------------ +// For every function that exists in the original console object, that +// also exists in the new console object, wrap the new console method +// with one that calls both +//------------------------------------------------------------------------------ +for (var key in console) { + if (typeof WinConsole[key] == "function") { + console[key] = wrappedOrigCall(WinConsole[key], console[key]); + } +} diff --git a/plugins/org.apache.cordova.console/www/logger.js b/plugins/org.apache.cordova.console/www/logger.js new file mode 100644 index 0000000..cbf81b9 --- /dev/null +++ b/plugins/org.apache.cordova.console/www/logger.js @@ -0,0 +1,355 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + +//------------------------------------------------------------------------------ +// The logger module exports the following properties/functions: +// +// LOG - constant for the level LOG +// ERROR - constant for the level ERROR +// WARN - constant for the level WARN +// INFO - constant for the level INFO +// DEBUG - constant for the level DEBUG +// logLevel() - returns current log level +// logLevel(value) - sets and returns a new log level +// useConsole() - returns whether logger is using console +// useConsole(value) - sets and returns whether logger is using console +// log(message,...) - logs a message at level LOG +// error(message,...) - logs a message at level ERROR +// warn(message,...) - logs a message at level WARN +// info(message,...) - logs a message at level INFO +// debug(message,...) - logs a message at level DEBUG +// logLevel(level,message,...) - logs a message specified level +// +//------------------------------------------------------------------------------ + +var logger = exports; + +var exec = require('cordova/exec'); +var utils = require('cordova/utils'); + +var UseConsole = false; +var UseLogger = true; +var Queued = []; +var DeviceReady = false; +var CurrentLevel; + +var originalConsole = console; + +/** + * Logging levels + */ + +var Levels = [ + "LOG", + "ERROR", + "WARN", + "INFO", + "DEBUG" +]; + +/* + * add the logging levels to the logger object and + * to a separate levelsMap object for testing + */ + +var LevelsMap = {}; +for (var i=0; i 0){ + formatArgs.unshift(fmtString); // add formatString + } + + var message = logger.format.apply(logger.format, formatArgs); + + if (LevelsMap[level] === null) { + throw new Error("invalid logging level: " + level); + } + + if (LevelsMap[level] > CurrentLevel) return; + + // queue the message if not yet at deviceready + if (!DeviceReady && !UseConsole) { + Queued.push([level, message]); + return; + } + + // Log using the native logger if that is enabled + if (UseLogger) { + exec(null, null, "Console", "logLevel", [level, message]); + } + + // Log using the console if that is enabled + if (UseConsole) { + // make sure console is not using logger + if (console.useLogger()) { + throw new Error("console and logger are too intertwingly"); + } + + // log to the console + switch (level) { + case logger.LOG: originalConsole.log(message); break; + case logger.ERROR: originalConsole.log("ERROR: " + message); break; + case logger.WARN: originalConsole.log("WARN: " + message); break; + case logger.INFO: originalConsole.log("INFO: " + message); break; + case logger.DEBUG: originalConsole.log("DEBUG: " + message); break; + } + } +}; + + +/** + * Formats a string and arguments following it ala console.log() + * + * Any remaining arguments will be appended to the formatted string. + * + * for rationale, see FireBug's Console API: + * http://getfirebug.com/wiki/index.php/Console_API + */ +logger.format = function(formatString, args) { + return __format(arguments[0], [].slice.call(arguments,1)).join(' '); +}; + + +//------------------------------------------------------------------------------ +/** + * Formats a string and arguments following it ala vsprintf() + * + * format chars: + * %j - format arg as JSON + * %o - format arg as JSON + * %c - format arg as '' + * %% - replace with '%' + * any other char following % will format it's + * arg via toString(). + * + * Returns an array containing the formatted string and any remaining + * arguments. + */ +function __format(formatString, args) { + if (formatString === null || formatString === undefined) return [""]; + if (arguments.length == 1) return [formatString.toString()]; + + if (typeof formatString != "string") + formatString = formatString.toString(); + + var pattern = /(.*?)%(.)(.*)/; + var rest = formatString; + var result = []; + + while (args.length) { + var match = pattern.exec(rest); + if (!match) break; + + var arg = args.shift(); + rest = match[3]; + result.push(match[1]); + + if (match[2] == '%') { + result.push('%'); + args.unshift(arg); + continue; + } + + result.push(__formatted(arg, match[2])); + } + + result.push(rest); + + var remainingArgs = [].slice.call(args); + remainingArgs.unshift(result.join('')); + return remainingArgs; +} + +function __formatted(object, formatChar) { + + try { + switch(formatChar) { + case 'j': + case 'o': return JSON.stringify(object); + case 'c': return ''; + } + } + catch (e) { + return "error JSON.stringify()ing argument: " + e; + } + + if ((object === null) || (object === undefined)) { + return Object.prototype.toString.call(object); + } + + return object.toString(); +} + + +//------------------------------------------------------------------------------ +// when deviceready fires, log queued messages +logger.__onDeviceReady = function() { + if (DeviceReady) return; + + DeviceReady = true; + + for (var i=0; i +# Release Notes + +### 0.2.1 (Sept 5, 2013) +* removed extraneous print statement +* [CB-4432] copyright notice change + +### 0.2.3 (Sept 25, 2013) +* CB-4889 bumping&resetting version +* [windows8] commandProxy has moved +* [BlackBerry10] removed uneeded permission tags in plugin.xml +* CB-4889 renaming org.apache.cordova.core.device to org.apache.cordova.device +* Rename CHANGELOG.md -> RELEASENOTES.md +* updated to use commandProxy for ffos +* add firefoxos support +* [CB-4752] Incremented plugin version on dev branch. + +### 0.2.4 (Oct 28, 2013) +* CB-5128: added repo + issue tag in plugin.xml for device plugin +* CB-5085 device.cordova returning wrong value +* [CB-4915] Incremented plugin version on dev branch. diff --git a/plugins/org.apache.cordova.device/docs/device.cordova.md b/plugins/org.apache.cordova.device/docs/device.cordova.md new file mode 100644 index 0000000..b29e24f --- /dev/null +++ b/plugins/org.apache.cordova.device/docs/device.cordova.md @@ -0,0 +1,80 @@ +--- + license: Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--- + +device.cordova +=============== + +Get the version of Cordova running on the device. + + var string = device.cordova; + +Description +----------- + +`device.cordova` returns the version of Cordova running on the device. + +Supported Platforms +------------------- + +- Android +- BlackBerry WebWorks (OS 5.0 and higher) +- iOS +- Windows Phone 7 and 8 +- Bada 1.2 & 2.x +- Tizen +- Windows 8 + +Quick Example +------------- + + var name = device.cordova; + +Full Example +------------ + + + + + Device Properties Example + + + + + +

Loading device properties...

+ + + diff --git a/plugins/org.apache.cordova.device/docs/device.md b/plugins/org.apache.cordova.device/docs/device.md new file mode 100644 index 0000000..3748d36 --- /dev/null +++ b/plugins/org.apache.cordova.device/docs/device.md @@ -0,0 +1,104 @@ +--- + license: Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--- + +Device +====== + +> The `device` object describes the device's hardware and software. + +Properties +---------- + +- device.name +- device.cordova +- device.platform +- device.uuid +- device.version +- device.model + +Variable Scope +-------------- + +Since `device` is assigned to the `window` object, it is implicitly in the global scope. + + // These reference the same `device` + var phoneName = window.device.name; + var phoneName = device.name; + +Permissions +----------- + +### Android + +#### app/res/xml/config.xml + + + +#### app/AndroidManifest.xml + + + +### Bada + +#### manifest.xml + + + SYSTEM_SERVICE + + +### BlackBerry WebWorks + +#### www/plugins.xml + + + +#### www/config.xml + + + + read_device_identifying_information + + +### iOS + + No permissions are required. + +### webOS + + No permissions are required. + +### Windows Phone + +#### Properties/WPAppManifest.xml + + + + + + + +Reference: [Application Manifest for Windows Phone](http://msdn.microsoft.com/en-us/library/ff769509%28v=vs.92%29.aspx) + +### Tizen + +#### config.xml + + + +Reference: [Application Manifest for Tizen Web Application](https://developer.tizen.org/help/topic/org.tizen.help.gs/Creating%20a%20Project.html?path=0_1_1_3#8814682_CreatingaProject-EditingconfigxmlFeatures) diff --git a/plugins/org.apache.cordova.device/docs/device.model.md b/plugins/org.apache.cordova.device/docs/device.model.md new file mode 100644 index 0000000..e5238e4 --- /dev/null +++ b/plugins/org.apache.cordova.device/docs/device.model.md @@ -0,0 +1,106 @@ +--- + license: Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--- + +device.model +=========== + +Get the device's model name. + + var string = device.model; + +Description +----------- + +The `device.model` returns the name of the device's model or +product. The value is set by the device manufacturer and may be +different across versions of the same product. + +Supported Platforms +------------------- + +- Android +- BlackBerry WebWorks (OS 5.0 and higher) +- iOS +- Windows Phone 7 and 8 +- Bada 1.2 & 2.x +- webOS +- Tizen +- Windows 8 + +Quick Example +------------- + + // Android: Nexus One returns "Passion" (Nexus One code name) + // Motorola Droid returns "voles" + // BlackBerry: Torch 9800 returns "9800" + // iOS: for the iPad Mini, returns iPad2,5; iPhone 5 is iPhone 5,1. See http://theiphonewiki.com/wiki/index.php?title=Models + // + var model = device.model; + +Full Example +------------ + + + + + Device Properties Example + + + + + +

Loading device properties...

+ + + +Android Quirks +-------------- + +- Gets the [product name](http://developer.android.com/reference/android/os/Build.html#PRODUCT) instead of the [model name](http://developer.android.com/reference/android/os/Build.html#MODEL), which is often the production code name. For example, the Nexus One returns `Passion`, and Motorola Droid returns `voles`. + +Windows Phone 7 and 8 Quirks +------------- + +- Returns the device model specified by the manufacturer. For example, the Samsung Focus returns `SGH-i917`. + +Bada Quirks +----------- +- Returns the manufacturer model name, for example, `Samsung Wave S8500` + +Tizen Quirks +----------- +- Returns the device model assigned by the vendor, for example, `TIZEN` diff --git a/plugins/org.apache.cordova.device/docs/device.name.md b/plugins/org.apache.cordova.device/docs/device.name.md new file mode 100644 index 0000000..a70648f --- /dev/null +++ b/plugins/org.apache.cordova.device/docs/device.name.md @@ -0,0 +1,108 @@ +--- + license: Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--- + +device.name +=========== + +__WARNING:__ `device.name` is deprecated as of version 2.3.0. Use `device.model` instead. + +Get the device's model name. + + var string = device.name; + +Description +----------- + +`device.name` returns the name of the device's model or product. This +value is set by the device manufacturer and may be different across +versions of the same product. + +Supported Platforms +------------------- + +- Android +- BlackBerry WebWorks (OS 5.0 and higher) +- iOS +- Windows Phone 7 and 8 +- Bada 1.2 & 2.x +- webOS +- Tizen +- Windows 8 + +Quick Example +------------- + + // Android: Nexus One returns "Passion" (Nexus One code name) + // Motorola Droid returns "voles" + // BlackBerry: Torch 9800 returns "9800" + // iOS: All devices returns either "iPhone", "iPod Touch", "iPhone Simulator", "iPad", "iPad Simulator" + // + var name = device.name; + +Full Example +------------ + + + + + Device Properties Example + + + + + +

Loading device properties...

+ + + +Android Quirks +-------------- + +- Gets the [product name](http://developer.android.com/reference/android/os/Build.html#PRODUCT) instead of the [model name](http://developer.android.com/reference/android/os/Build.html#MODEL), which is often the production code name. For example, the Nexus One returns `Passion`, and Motorola Droid returns `voles`. + +Windows Phone 7 and 8 Quirks +------------- + +- Returns the device model specified by the manufacturer. For example, the Samsung Focus returns `SGH-i917`. + +Bada Quirks +----------- +- Returns the manufacturer model name, for example, `Samsung Wave S8500` + +Tizen Quirks +----------- +- Returns the device model assigned by the vendor, for example, `TIZEN` diff --git a/plugins/org.apache.cordova.device/docs/device.platform.md b/plugins/org.apache.cordova.device/docs/device.platform.md new file mode 100644 index 0000000..0d2cacb --- /dev/null +++ b/plugins/org.apache.cordova.device/docs/device.platform.md @@ -0,0 +1,99 @@ +--- + license: Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--- + +device.platform +=============== + +Get the device's operating system name. + + var string = device.platform; + +Supported Platforms +------------------- + +- Android +- BlackBerry WebWorks (OS 5.0 and higher) +- iOS +- Windows Phone 7 and 8 +- Bada 1.2 & 2.x +- webOS +- Tizen +- Windows 8 + +Quick Example +------------- + + // Depending on the device, a few examples are: + // - "Android" + // - "BlackBerry" + // - "iOS" + // - "webOS" + // - "WinCE" + // - "Tizen" + var devicePlatform = device.platform; + +Full Example +------------ + + + + + Device Properties Example + + + + + +

Loading device properties...

+ + + +BlackBerry Quirks +----------------- + +Devices may return the device platform version number instead of the +platform name. For example, the Storm2 9550 returns a value such as +`2.13.0.95`. + +Windows Phone 7 Quirks +----------------- + +Windows Phone 7 devices report the platform as `WinCE`. + +Windows Phone 8 Quirks +----------------- + +Windows Phone 8 devices report the platform as `Win32NT`. diff --git a/plugins/org.apache.cordova.device/docs/device.uuid.md b/plugins/org.apache.cordova.device/docs/device.uuid.md new file mode 100644 index 0000000..76abf73 --- /dev/null +++ b/plugins/org.apache.cordova.device/docs/device.uuid.md @@ -0,0 +1,115 @@ +--- + license: Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--- + +device.uuid +=========== + +Get the device's Universally Unique Identifier ([UUID](http://en.wikipedia.org/wiki/Universally_Unique_Identifier)). + + var string = device.uuid; + +Description +----------- + +The details of how a UUID is generated are determined by the device manufacturer and are specific to the device's platform or model. + +Supported Platforms +------------------- + +- Android +- BlackBerry WebWorks (OS 5.0 and higher) +- iOS +- Windows Phone 7 and 8 +- Bada 1.2 & 2.x +- webOS +- Tizen +- Windows 8 + +Quick Example +------------- + + // Android: Returns a random 64-bit integer (as a string, again!) + // The integer is generated on the device's first boot + // + // BlackBerry: Returns the PIN number of the device + // This is a nine-digit unique integer (as a string, though!) + // + // iPhone: (Paraphrased from the UIDevice Class documentation) + // Returns a string of hash values created from multiple hardware identifies. + // It is guaranteed to be unique for every device and cannot be tied + // to the user account. + // Windows Phone 7 : Returns a hash of device+current user, + // if the user is not defined, a guid is generated and will persist until the app is uninstalled + // + // webOS: returns the device NDUID + // + // Tizen: returns the device IMEI (International Mobile Equipment Identity or IMEI is a number + // unique to every GSM and UMTS mobile phone. + var deviceID = device.uuid; + +Full Example +------------ + + + + + Device Properties Example + + + + + +

Loading device properties...

+ + + +iOS Quirk +------------- + +The `uuid` on iOS is not unique to a device, but varies for each +application, for each installation. It changes if you delete and +re-install the app, and possibly also when you upgrade iOS, or even +upgrade your app per version (apparent in iOS 5.1). The `uuid` is not +a reliable value. + +Windows Phone 7 and 8 Quirks +------------- + +The `uuid` for Windows Phone 7 requires the permission +`ID_CAP_IDENTITY_DEVICE`. Microsoft will likely deprecate this +property soon. If the capability is not available, the application +generates a persistent guid that is maintained for the duration of the +application's installation on the device. diff --git a/plugins/org.apache.cordova.device/docs/device.version.md b/plugins/org.apache.cordova.device/docs/device.version.md new file mode 100644 index 0000000..ecd4733 --- /dev/null +++ b/plugins/org.apache.cordova.device/docs/device.version.md @@ -0,0 +1,86 @@ +--- + license: Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--- + +device.version +============== + +Get the operating system version. + + var string = device.version; + +Supported Platforms +------------------- + +- Android 2.1+ +- BlackBerry WebWorks (OS 5.0 and higher) +- iOS +- Windows Phone 7 and 8 +- Bada 1.2 & 2.x +- webOS +- Tizen +- Windows 8 + +Quick Example +------------- + + // Android: Froyo OS would return "2.2" + // Eclair OS would return "2.1", "2.0.1", or "2.0" + // Version can also return update level "2.1-update1" + // + // BlackBerry: Torch 9800 using OS 6.0 would return "6.0.0.600" + // + // iPhone: iOS 3.2 returns "3.2" + // + // Windows Phone 7: returns current OS version number, ex. on Mango returns 7.10.7720 + // webOS: webOS 2.2.4 return 2.2.4 + // Tizen: returns "TIZEN_20120425_2" + var deviceVersion = device.version; + +Full Example +------------ + + + + + Device Properties Example + + + + + +

Loading device properties...

+ + diff --git a/plugins/org.apache.cordova.device/plugin.xml b/plugins/org.apache.cordova.device/plugin.xml new file mode 100644 index 0000000..7eb03c4 --- /dev/null +++ b/plugins/org.apache.cordova.device/plugin.xml @@ -0,0 +1,103 @@ + + + + Device + Cordova Device Plugin + Apache 2.0 + cordova,device + https://git-wip-us.apache.org/repos/asf/cordova-plugin-device.git + https://issues.apache.org/jira/browse/CB/component/12320648 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + read_device_identifying_information + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/org.apache.cordova.device/src/android/Device.java b/plugins/org.apache.cordova.device/src/android/Device.java new file mode 100644 index 0000000..2ce3cc7 --- /dev/null +++ b/plugins/org.apache.cordova.device/src/android/Device.java @@ -0,0 +1,200 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ +package org.apache.cordova.device; + +import java.util.TimeZone; + +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.LOG; +import org.apache.cordova.CordovaInterface; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.provider.Settings; +import android.telephony.TelephonyManager; + +public class Device extends CordovaPlugin { + public static final String TAG = "Device"; + + public static String cordovaVersion = "dev"; // Cordova version + public static String platform = "Android"; // Device OS + public static String uuid; // Device UUID + + BroadcastReceiver telephonyReceiver = null; + + /** + * Constructor. + */ + public Device() { + } + + /** + * Sets the context of the Command. This can then be used to do things like + * get file paths associated with the Activity. + * + * @param cordova The context of the main Activity. + * @param webView The CordovaWebView Cordova is running in. + */ + public void initialize(CordovaInterface cordova, CordovaWebView webView) { + super.initialize(cordova, webView); + Device.uuid = getUuid(); + this.initTelephonyReceiver(); + } + + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArry of arguments for the plugin. + * @param callbackContext The callback id used when calling back into JavaScript. + * @return True if the action was valid, false if not. + */ + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { + if (action.equals("getDeviceInfo")) { + JSONObject r = new JSONObject(); + r.put("uuid", Device.uuid); + r.put("version", this.getOSVersion()); + r.put("platform", Device.platform); + r.put("cordova", Device.cordovaVersion); + r.put("model", this.getModel()); + callbackContext.success(r); + } + else { + return false; + } + return true; + } + + /** + * Unregister receiver. + */ + public void onDestroy() { + this.cordova.getActivity().unregisterReceiver(this.telephonyReceiver); + } + + //-------------------------------------------------------------------------- + // LOCAL METHODS + //-------------------------------------------------------------------------- + + /** + * Listen for telephony events: RINGING, OFFHOOK and IDLE + * Send these events to all plugins using + * CordovaActivity.onMessage("telephone", "ringing" | "offhook" | "idle") + */ + private void initTelephonyReceiver() { + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); + //final CordovaInterface mycordova = this.cordova; + this.telephonyReceiver = new BroadcastReceiver() { + + @Override + public void onReceive(Context context, Intent intent) { + + // If state has changed + if ((intent != null) && intent.getAction().equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { + if (intent.hasExtra(TelephonyManager.EXTRA_STATE)) { + String extraData = intent.getStringExtra(TelephonyManager.EXTRA_STATE); + if (extraData.equals(TelephonyManager.EXTRA_STATE_RINGING)) { + LOG.i(TAG, "Telephone RINGING"); + webView.postMessage("telephone", "ringing"); + } + else if (extraData.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) { + LOG.i(TAG, "Telephone OFFHOOK"); + webView.postMessage("telephone", "offhook"); + } + else if (extraData.equals(TelephonyManager.EXTRA_STATE_IDLE)) { + LOG.i(TAG, "Telephone IDLE"); + webView.postMessage("telephone", "idle"); + } + } + } + } + }; + + // Register the receiver + this.cordova.getActivity().registerReceiver(this.telephonyReceiver, intentFilter); + } + + /** + * Get the OS name. + * + * @return + */ + public String getPlatform() { + return Device.platform; + } + + /** + * Get the device's Universally Unique Identifier (UUID). + * + * @return + */ + public String getUuid() { + String uuid = Settings.Secure.getString(this.cordova.getActivity().getContentResolver(), android.provider.Settings.Secure.ANDROID_ID); + return uuid; + } + + /** + * Get the Cordova version. + * + * @return + */ + public String getCordovaVersion() { + return Device.cordovaVersion; + } + + public String getModel() { + String model = android.os.Build.MODEL; + return model; + } + + public String getProductName() { + String productname = android.os.Build.PRODUCT; + return productname; + } + + /** + * Get the OS version. + * + * @return + */ + public String getOSVersion() { + String osversion = android.os.Build.VERSION.RELEASE; + return osversion; + } + + public String getSDKVersion() { + @SuppressWarnings("deprecation") + String sdkversion = android.os.Build.VERSION.SDK; + return sdkversion; + } + + public String getTimeZoneID() { + TimeZone tz = TimeZone.getDefault(); + return (tz.getID()); + } + +} diff --git a/plugins/org.apache.cordova.device/src/blackberry10/index.js b/plugins/org.apache.cordova.device/src/blackberry10/index.js new file mode 100644 index 0000000..639ac64 --- /dev/null +++ b/plugins/org.apache.cordova.device/src/blackberry10/index.js @@ -0,0 +1,69 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + +function getModelName () { + var modelName = window.qnx.webplatform.device.modelName; + //Pre 10.2 (meaning Z10 or Q10) + if (typeof modelName === "undefined") { + if (window.screen.height === 720 && window.screen.width === 720) { + if ( window.matchMedia("(-blackberry-display-technology: -blackberry-display-oled)").matches) { + modelName = "Q10"; + } else { + modelName = "Q5"; + } + } else if ((window.screen.height === 1280 && window.screen.width === 768) || + (window.screen.height === 768 && window.screen.width === 1280)) { + modelName = "Z10"; + } else { + modelName = window.qnx.webplatform.deviceName; + } + } + + return modelName; +} + +function getUUID () { + var uuid = ""; + try { + //Must surround by try catch because this will throw if the app is missing permissions + uuid = window.qnx.webplatform.device.devicePin; + } catch (e) { + //DO Nothing + } + return uuid; +} + +module.exports = { + getDeviceInfo: function (success, fail, args, env) { + var result = new PluginResult(args, env), + modelName = getModelName(), + uuid = getUUID(), + info = { + platform: "blackberry10", + version: window.qnx.webplatform.device.scmBundle, + model: modelName, + uuid: uuid, + cordova: "dev" + }; + + result.ok(info); + } +}; diff --git a/plugins/org.apache.cordova.device/src/firefoxos/DeviceProxy.js b/plugins/org.apache.cordova.device/src/firefoxos/DeviceProxy.js new file mode 100644 index 0000000..f37e761 --- /dev/null +++ b/plugins/org.apache.cordova.device/src/firefoxos/DeviceProxy.js @@ -0,0 +1,39 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + +var firefoxos = require('cordova/platform'); +var cordova = require('cordova'); + +module.exports = { + getDeviceInfo: function(success, error) { + setTimeout(function () { + success({ + cordova: firefoxos.cordovaVersion, + platform: 'firefoxos', + model: null, + version: null, + uuid: null + }); + }, 0); + } +}; + +require("cordova/firefoxos/commandProxy").add("Device", module.exports); diff --git a/plugins/org.apache.cordova.device/src/ios/CDVDevice.h b/plugins/org.apache.cordova.device/src/ios/CDVDevice.h new file mode 100644 index 0000000..a146d88 --- /dev/null +++ b/plugins/org.apache.cordova.device/src/ios/CDVDevice.h @@ -0,0 +1,30 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import +#import + +@interface CDVDevice : CDVPlugin +{} + ++ (NSString*)cordovaVersion; + +- (void)getDeviceInfo:(CDVInvokedUrlCommand*)command; + +@end diff --git a/plugins/org.apache.cordova.device/src/ios/CDVDevice.m b/plugins/org.apache.cordova.device/src/ios/CDVDevice.m new file mode 100644 index 0000000..7e18d92 --- /dev/null +++ b/plugins/org.apache.cordova.device/src/ios/CDVDevice.m @@ -0,0 +1,90 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#include +#include + +#import +#import "CDVDevice.h" + +@implementation UIDevice (ModelVersion) + +- (NSString*)modelVersion +{ + size_t size; + + sysctlbyname("hw.machine", NULL, &size, NULL, 0); + char* machine = malloc(size); + sysctlbyname("hw.machine", machine, &size, NULL, 0); + NSString* platform = [NSString stringWithUTF8String:machine]; + free(machine); + + return platform; +} + +@end + +@interface CDVDevice () {} +@end + +@implementation CDVDevice + +- (void)getDeviceInfo:(CDVInvokedUrlCommand*)command +{ + NSDictionary* deviceProperties = [self deviceProperties]; + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:deviceProperties]; + + /* Settings.plist + * Read the optional Settings.plist file and push these user-defined settings down into the web application. + * This can be useful for supplying build-time configuration variables down to the app to change its behavior, + * such as specifying Full / Lite version, or localization (English vs German, for instance). + */ + // TODO: turn this into an iOS only plugin + NSDictionary* temp = [CDVViewController getBundlePlist:@"Settings"]; + + if ([temp respondsToSelector:@selector(JSONString)]) { + NSLog(@"Deprecation warning: window.Setting will be removed Aug 2013. Refer to https://issues.apache.org/jira/browse/CB-2433"); + NSString* js = [NSString stringWithFormat:@"window.Settings = %@;", [temp JSONString]]; + [self.commandDelegate evalJs:js]; + } + + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + +- (NSDictionary*)deviceProperties +{ + UIDevice* device = [UIDevice currentDevice]; + NSMutableDictionary* devProps = [NSMutableDictionary dictionaryWithCapacity:4]; + + [devProps setObject:[device modelVersion] forKey:@"model"]; + [devProps setObject:@"iOS" forKey:@"platform"]; + [devProps setObject:[device systemVersion] forKey:@"version"]; + [devProps setObject:[device uniqueAppInstanceIdentifier] forKey:@"uuid"]; + [devProps setObject:[[self class] cordovaVersion] forKey:@"cordova"]; + + NSDictionary* devReturn = [NSDictionary dictionaryWithDictionary:devProps]; + return devReturn; +} + ++ (NSString*)cordovaVersion +{ + return CDV_VERSION; +} + +@end diff --git a/plugins/org.apache.cordova.device/src/windows8/DeviceProxy.js b/plugins/org.apache.cordova.device/src/windows8/DeviceProxy.js new file mode 100644 index 0000000..16145c0 --- /dev/null +++ b/plugins/org.apache.cordova.device/src/windows8/DeviceProxy.js @@ -0,0 +1,50 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + + +var cordova = require('cordova'); +var utils = require('cordova/utils'); + +module.exports = { + + getDeviceInfo:function(win,fail,args) { + + // deviceId aka uuid, stored in Windows.Storage.ApplicationData.current.localSettings.values.deviceId + var deviceId; + + var localSettings = Windows.Storage.ApplicationData.current.localSettings; + + if (localSettings.values.deviceId) { + deviceId = localSettings.values.deviceId; + } + else { + deviceId = localSettings.values.deviceId = utils.createUUID(); + } + + setTimeout(function () { + win({ platform: "windows8", version: "8", uuid: deviceId, cordova: '0.0.0', model: window.clientInformation.platform }); + }, 0); + } + +}; + +require("cordova/windows8/commandProxy").add("Device", module.exports); + diff --git a/plugins/org.apache.cordova.device/src/wp/Device.cs b/plugins/org.apache.cordova.device/src/wp/Device.cs new file mode 100644 index 0000000..0d2576d --- /dev/null +++ b/plugins/org.apache.cordova.device/src/wp/Device.cs @@ -0,0 +1,135 @@ +/* + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using Microsoft.Phone.Info; +using System.IO.IsolatedStorage; +using System.Windows.Resources; +using System.IO; +using System.Diagnostics; + +namespace WPCordovaClassLib.Cordova.Commands +{ + public class Device : BaseCommand + { + public void getDeviceInfo(string notused) + { + + string res = String.Format("\"name\":\"{0}\",\"cordova\":\"{1}\",\"platform\":\"{2}\",\"uuid\":\"{3}\",\"version\":\"{4}\",\"model\":\"{5}\"", + this.name, + this.cordova, + this.platform, + this.uuid, + this.version, + this.model); + + + + res = "{" + res + "}"; + //Debug.WriteLine("Result::" + res); + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, res)); + } + + public string model + { + get + { + return DeviceStatus.DeviceName; + //return String.Format("{0},{1},{2}", DeviceStatus.DeviceManufacturer, DeviceStatus.DeviceHardwareVersion, DeviceStatus.DeviceFirmwareVersion); + } + } + + public string name + { + get + { + return DeviceStatus.DeviceName; + + } + } + + public string cordova + { + get + { + // TODO: should be able to dynamically read the Cordova version from somewhere... + return "3.0.0"; + } + } + + public string platform + { + get + { + return Environment.OSVersion.Platform.ToString(); + } + } + + public string uuid + { + get + { + string returnVal = ""; + object id; + UserExtendedProperties.TryGetValue("ANID", out id); + + if (id != null) + { + returnVal = id.ToString().Substring(2, 32); + } + else + { + returnVal = "???unknown???"; + + using (IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication()) + { + try + { + IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream("DeviceID.txt", FileMode.Open, FileAccess.Read, appStorage); + + using (StreamReader reader = new StreamReader(fileStream)) + { + returnVal = reader.ReadLine(); + } + } + catch (Exception /*ex*/) + { + + } + } + } + + return returnVal; + } + } + + public string version + { + get + { + return Environment.OSVersion.Version.ToString(); + } + } + + } +} diff --git a/plugins/org.apache.cordova.device/test/autotest/html/HtmlReporter.js b/plugins/org.apache.cordova.device/test/autotest/html/HtmlReporter.js new file mode 100644 index 0000000..7d9d924 --- /dev/null +++ b/plugins/org.apache.cordova.device/test/autotest/html/HtmlReporter.js @@ -0,0 +1,101 @@ +jasmine.HtmlReporter = function(_doc) { + var self = this; + var doc = _doc || window.document; + + var reporterView; + + var dom = {}; + + // Jasmine Reporter Public Interface + self.logRunningSpecs = false; + + self.reportRunnerStarting = function(runner) { + var specs = runner.specs() || []; + + if (specs.length == 0) { + return; + } + + createReporterDom(runner.env.versionString()); + doc.body.appendChild(dom.reporter); + + reporterView = new jasmine.HtmlReporter.ReporterView(dom); + reporterView.addSpecs(specs, self.specFilter); + }; + + self.reportRunnerResults = function(runner) { + reporterView && reporterView.complete(); + }; + + self.reportSuiteResults = function(suite) { + reporterView.suiteComplete(suite); + }; + + self.reportSpecStarting = function(spec) { + if (self.logRunningSpecs) { + self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); + } + }; + + self.reportSpecResults = function(spec) { + reporterView.specComplete(spec); + }; + + self.log = function() { + var console = jasmine.getGlobal().console; + if (console && console.log) { + if (console.log.apply) { + console.log.apply(console, arguments); + } else { + console.log(arguments); // ie fix: console.log.apply doesn't exist on ie + } + } + }; + + self.specFilter = function(spec) { + if (!focusedSpecName()) { + return true; + } + + return spec.getFullName().indexOf(focusedSpecName()) === 0; + }; + + return self; + + function focusedSpecName() { + var specName; + + (function memoizeFocusedSpec() { + if (specName) { + return; + } + + var paramMap = []; + var params = doc.location.search.substring(1).split('&'); + + for (var i = 0; i < params.length; i++) { + var p = params[i].split('='); + paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); + } + + specName = paramMap.spec; + })(); + + return specName; + } + + function createReporterDom(version) { + dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' }, + dom.banner = self.createDom('div', { className: 'banner' }, + self.createDom('span', { className: 'title' }, "Jasmine "), + self.createDom('span', { className: 'version' }, version)), + + dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}), + dom.alert = self.createDom('div', {className: 'alert'}), + dom.results = self.createDom('div', {className: 'results'}, + dom.summary = self.createDom('div', { className: 'summary' }), + dom.details = self.createDom('div', { id: 'details' })) + ); + } +}; +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter); diff --git a/plugins/org.apache.cordova.device/test/autotest/html/HtmlReporterHelpers.js b/plugins/org.apache.cordova.device/test/autotest/html/HtmlReporterHelpers.js new file mode 100644 index 0000000..745e1e0 --- /dev/null +++ b/plugins/org.apache.cordova.device/test/autotest/html/HtmlReporterHelpers.js @@ -0,0 +1,60 @@ +jasmine.HtmlReporterHelpers = {}; + +jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) { + var el = document.createElement(type); + + for (var i = 2; i < arguments.length; i++) { + var child = arguments[i]; + + if (typeof child === 'string') { + el.appendChild(document.createTextNode(child)); + } else { + if (child) { + el.appendChild(child); + } + } + } + + for (var attr in attrs) { + if (attr == "className") { + el[attr] = attrs[attr]; + } else { + el.setAttribute(attr, attrs[attr]); + } + } + + return el; +}; + +jasmine.HtmlReporterHelpers.getSpecStatus = function(child) { + var results = child.results(); + var status = results.passed() ? 'passed' : 'failed'; + if (results.skipped) { + status = 'skipped'; + } + + return status; +}; + +jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) { + var parentDiv = this.dom.summary; + var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite'; + var parent = child[parentSuite]; + + if (parent) { + if (typeof this.views.suites[parent.id] == 'undefined') { + this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views); + } + parentDiv = this.views.suites[parent.id].element; + } + + parentDiv.appendChild(childElement); +}; + + +jasmine.HtmlReporterHelpers.addHelpers = function(ctor) { + for(var fn in jasmine.HtmlReporterHelpers) { + ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn]; + } +}; + diff --git a/plugins/org.apache.cordova.device/test/autotest/html/ReporterView.js b/plugins/org.apache.cordova.device/test/autotest/html/ReporterView.js new file mode 100644 index 0000000..6a6d005 --- /dev/null +++ b/plugins/org.apache.cordova.device/test/autotest/html/ReporterView.js @@ -0,0 +1,164 @@ +jasmine.HtmlReporter.ReporterView = function(dom) { + this.startedAt = new Date(); + this.runningSpecCount = 0; + this.completeSpecCount = 0; + this.passedCount = 0; + this.failedCount = 0; + this.skippedCount = 0; + + this.createResultsMenu = function() { + this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'}, + this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'), + ' | ', + this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing')); + + this.summaryMenuItem.onclick = function() { + dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, ''); + }; + + this.detailsMenuItem.onclick = function() { + showDetails(); + }; + }; + + this.addSpecs = function(specs, specFilter) { + this.totalSpecCount = specs.length; + + this.views = { + specs: {}, + suites: {} + }; + + for (var i = 0; i < specs.length; i++) { + var spec = specs[i]; + this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views); + if (specFilter(spec)) { + this.runningSpecCount++; + } + } + }; + + this.specComplete = function(spec) { + this.completeSpecCount++; + + if (isUndefined(this.views.specs[spec.id])) { + this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom); + } + + var specView = this.views.specs[spec.id]; + + switch (specView.status()) { + case 'passed': + this.passedCount++; + break; + + case 'failed': + this.failedCount++; + break; + + case 'skipped': + this.skippedCount++; + break; + } + + specView.refresh(); + this.refresh(); + }; + + this.suiteComplete = function(suite) { + var suiteView = this.views.suites[suite.id]; + if (isUndefined(suiteView)) { + return; + } + suiteView.refresh(); + }; + + this.refresh = function() { + + if (isUndefined(this.resultsMenu)) { + this.createResultsMenu(); + } + + // currently running UI + if (isUndefined(this.runningAlert)) { + this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"}); + dom.alert.appendChild(this.runningAlert); + } + this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount); + + // skipped specs UI + if (isUndefined(this.skippedAlert)) { + this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"}); + } + + this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; + + if (this.skippedCount === 1 && isDefined(dom.alert)) { + dom.alert.appendChild(this.skippedAlert); + } + + // passing specs UI + if (isUndefined(this.passedAlert)) { + this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"}); + } + this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount); + + // failing specs UI + if (isUndefined(this.failedAlert)) { + this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"}); + } + this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount); + + if (this.failedCount === 1 && isDefined(dom.alert)) { + dom.alert.appendChild(this.failedAlert); + dom.alert.appendChild(this.resultsMenu); + } + + // summary info + this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount); + this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing"; + }; + + this.complete = function() { + dom.alert.removeChild(this.runningAlert); + + this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; + + if (this.failedCount === 0) { + dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount))); + } else { + showDetails(); + } + + dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s")); + }; + + return this; + + function showDetails() { + if (dom.reporter.className.search(/showDetails/) === -1) { + dom.reporter.className += " showDetails"; + } + } + + function isUndefined(obj) { + return typeof obj === 'undefined'; + } + + function isDefined(obj) { + return !isUndefined(obj); + } + + function specPluralizedFor(count) { + var str = count + " spec"; + if (count > 1) { + str += "s" + } + return str; + } + +}; + +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView); + + diff --git a/plugins/org.apache.cordova.device/test/autotest/html/SpecView.js b/plugins/org.apache.cordova.device/test/autotest/html/SpecView.js new file mode 100644 index 0000000..e8a3c23 --- /dev/null +++ b/plugins/org.apache.cordova.device/test/autotest/html/SpecView.js @@ -0,0 +1,79 @@ +jasmine.HtmlReporter.SpecView = function(spec, dom, views) { + this.spec = spec; + this.dom = dom; + this.views = views; + + this.symbol = this.createDom('li', { className: 'pending' }); + this.dom.symbolSummary.appendChild(this.symbol); + + this.summary = this.createDom('div', { className: 'specSummary' }, + this.createDom('a', { + className: 'description', + href: '?spec=' + encodeURIComponent(this.spec.getFullName()), + title: this.spec.getFullName() + }, this.spec.description) + ); + + this.detail = this.createDom('div', { className: 'specDetail' }, + this.createDom('a', { + className: 'description', + href: '?spec=' + encodeURIComponent(this.spec.getFullName()), + title: this.spec.getFullName() + }, this.spec.getFullName()) + ); +}; + +jasmine.HtmlReporter.SpecView.prototype.status = function() { + return this.getSpecStatus(this.spec); +}; + +jasmine.HtmlReporter.SpecView.prototype.refresh = function() { + this.symbol.className = this.status(); + + switch (this.status()) { + case 'skipped': + break; + + case 'passed': + this.appendSummaryToSuiteDiv(); + break; + + case 'failed': + this.appendSummaryToSuiteDiv(); + this.appendFailureDetail(); + break; + } +}; + +jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() { + this.summary.className += ' ' + this.status(); + this.appendToSummary(this.spec, this.summary); +}; + +jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() { + this.detail.className += ' ' + this.status(); + + var resultItems = this.spec.results().getItems(); + var messagesDiv = this.createDom('div', { className: 'messages' }); + + for (var i = 0; i < resultItems.length; i++) { + var result = resultItems[i]; + + if (result.type == 'log') { + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); + } else if (result.type == 'expect' && result.passed && !result.passed()) { + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); + + if (result.trace.stack) { + messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); + } + } + } + + if (messagesDiv.childNodes.length > 0) { + this.detail.appendChild(messagesDiv); + this.dom.details.appendChild(this.detail); + } +}; + +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView); diff --git a/plugins/org.apache.cordova.device/test/autotest/html/SuiteView.js b/plugins/org.apache.cordova.device/test/autotest/html/SuiteView.js new file mode 100644 index 0000000..19a1efa --- /dev/null +++ b/plugins/org.apache.cordova.device/test/autotest/html/SuiteView.js @@ -0,0 +1,22 @@ +jasmine.HtmlReporter.SuiteView = function(suite, dom, views) { + this.suite = suite; + this.dom = dom; + this.views = views; + + this.element = this.createDom('div', { className: 'suite' }, + this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description) + ); + + this.appendToSummary(this.suite, this.element); +}; + +jasmine.HtmlReporter.SuiteView.prototype.status = function() { + return this.getSpecStatus(this.suite); +}; + +jasmine.HtmlReporter.SuiteView.prototype.refresh = function() { + this.element.className += " " + this.status(); +}; + +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView); + diff --git a/plugins/org.apache.cordova.device/test/autotest/html/TrivialReporter.js b/plugins/org.apache.cordova.device/test/autotest/html/TrivialReporter.js new file mode 100644 index 0000000..167ac50 --- /dev/null +++ b/plugins/org.apache.cordova.device/test/autotest/html/TrivialReporter.js @@ -0,0 +1,192 @@ +/* @deprecated Use jasmine.HtmlReporter instead + */ +jasmine.TrivialReporter = function(doc) { + this.document = doc || document; + this.suiteDivs = {}; + this.logRunningSpecs = false; +}; + +jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) { + var el = document.createElement(type); + + for (var i = 2; i < arguments.length; i++) { + var child = arguments[i]; + + if (typeof child === 'string') { + el.appendChild(document.createTextNode(child)); + } else { + if (child) { el.appendChild(child); } + } + } + + for (var attr in attrs) { + if (attr == "className") { + el[attr] = attrs[attr]; + } else { + el.setAttribute(attr, attrs[attr]); + } + } + + return el; +}; + +jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) { + var showPassed, showSkipped; + + this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' }, + this.createDom('div', { className: 'banner' }, + this.createDom('div', { className: 'logo' }, + this.createDom('span', { className: 'title' }, "Jasmine"), + this.createDom('span', { className: 'version' }, runner.env.versionString())), + this.createDom('div', { className: 'options' }, + "Show ", + showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }), + this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "), + showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }), + this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped") + ) + ), + + this.runnerDiv = this.createDom('div', { className: 'runner running' }, + this.createDom('a', { className: 'run_spec', href: '?' }, "run all"), + this.runnerMessageSpan = this.createDom('span', {}, "Running..."), + this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, "")) + ); + + this.document.body.appendChild(this.outerDiv); + + var suites = runner.suites(); + for (var i = 0; i < suites.length; i++) { + var suite = suites[i]; + var suiteDiv = this.createDom('div', { className: 'suite' }, + this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"), + this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description)); + this.suiteDivs[suite.id] = suiteDiv; + var parentDiv = this.outerDiv; + if (suite.parentSuite) { + parentDiv = this.suiteDivs[suite.parentSuite.id]; + } + parentDiv.appendChild(suiteDiv); + } + + this.startedAt = new Date(); + + var self = this; + showPassed.onclick = function(evt) { + if (showPassed.checked) { + self.outerDiv.className += ' show-passed'; + } else { + self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, ''); + } + }; + + showSkipped.onclick = function(evt) { + if (showSkipped.checked) { + self.outerDiv.className += ' show-skipped'; + } else { + self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, ''); + } + }; +}; + +jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) { + var results = runner.results(); + var className = (results.failedCount > 0) ? "runner failed" : "runner passed"; + this.runnerDiv.setAttribute("class", className); + //do it twice for IE + this.runnerDiv.setAttribute("className", className); + var specs = runner.specs(); + var specCount = 0; + for (var i = 0; i < specs.length; i++) { + if (this.specFilter(specs[i])) { + specCount++; + } + } + var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s"); + message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"; + this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild); + + this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString())); +}; + +jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) { + var results = suite.results(); + var status = results.passed() ? 'passed' : 'failed'; + if (results.totalCount === 0) { // todo: change this to check results.skipped + status = 'skipped'; + } + this.suiteDivs[suite.id].className += " " + status; +}; + +jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) { + if (this.logRunningSpecs) { + this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); + } +}; + +jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) { + var results = spec.results(); + var status = results.passed() ? 'passed' : 'failed'; + if (results.skipped) { + status = 'skipped'; + } + var specDiv = this.createDom('div', { className: 'spec ' + status }, + this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"), + this.createDom('a', { + className: 'description', + href: '?spec=' + encodeURIComponent(spec.getFullName()), + title: spec.getFullName() + }, spec.description)); + + + var resultItems = results.getItems(); + var messagesDiv = this.createDom('div', { className: 'messages' }); + for (var i = 0; i < resultItems.length; i++) { + var result = resultItems[i]; + + if (result.type == 'log') { + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); + } else if (result.type == 'expect' && result.passed && !result.passed()) { + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); + + if (result.trace.stack) { + messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); + } + } + } + + if (messagesDiv.childNodes.length > 0) { + specDiv.appendChild(messagesDiv); + } + + this.suiteDivs[spec.suite.id].appendChild(specDiv); +}; + +jasmine.TrivialReporter.prototype.log = function() { + var console = jasmine.getGlobal().console; + if (console && console.log) { + if (console.log.apply) { + console.log.apply(console, arguments); + } else { + console.log(arguments); // ie fix: console.log.apply doesn't exist on ie + } + } +}; + +jasmine.TrivialReporter.prototype.getLocation = function() { + return this.document.location; +}; + +jasmine.TrivialReporter.prototype.specFilter = function(spec) { + var paramMap = {}; + var params = this.getLocation().search.substring(1).split('&'); + for (var i = 0; i < params.length; i++) { + var p = params[i].split('='); + paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); + } + + if (!paramMap.spec) { + return true; + } + return spec.getFullName().indexOf(paramMap.spec) === 0; +}; diff --git a/plugins/org.apache.cordova.device/test/autotest/index.html b/plugins/org.apache.cordova.device/test/autotest/index.html new file mode 100644 index 0000000..6ebccbd --- /dev/null +++ b/plugins/org.apache.cordova.device/test/autotest/index.html @@ -0,0 +1,59 @@ + + + + + + + + + + Cordova API Specs + + + + + +

Cordova API Specs

+ + Run All Tests + Run Accelerometer Tests + Run Battery Tests + Run Camera Tests + Run Capture Tests + Run Compass Tests + Run Contacts Tests + Run Data URI Tests + Run Device Tests + Run File Tests + Run FileTransfer Tests + Run Geolocation Tests + Run Globalization Tests + Run Media Tests + Run Network Tests + Run Notification Tests + Run Platform Tests + Run Storage Tests + Run Bridge Tests + +

Back
+ + diff --git a/plugins/org.apache.cordova.device/test/autotest/jasmine.css b/plugins/org.apache.cordova.device/test/autotest/jasmine.css new file mode 100644 index 0000000..826e575 --- /dev/null +++ b/plugins/org.apache.cordova.device/test/autotest/jasmine.css @@ -0,0 +1,81 @@ +body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } + +#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } +#HTMLReporter a { text-decoration: none; } +#HTMLReporter a:hover { text-decoration: underline; } +#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; } +#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; } +#HTMLReporter #jasmine_content { position: fixed; right: 100%; } +#HTMLReporter .version { color: #aaaaaa; } +#HTMLReporter .banner { margin-top: 14px; } +#HTMLReporter .duration { color: #aaaaaa; float: right; } +#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; } +#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; } +#HTMLReporter .symbolSummary li.passed { font-size: 14px; } +#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; } +#HTMLReporter .symbolSummary li.failed { line-height: 9px; } +#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } +#HTMLReporter .symbolSummary li.skipped { font-size: 14px; } +#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; } +#HTMLReporter .symbolSummary li.pending { line-height: 11px; } +#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; } +#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } +#HTMLReporter .runningAlert { background-color: #666666; } +#HTMLReporter .skippedAlert { background-color: #aaaaaa; } +#HTMLReporter .skippedAlert:first-child { background-color: #333333; } +#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; } +#HTMLReporter .passingAlert { background-color: #a6b779; } +#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; } +#HTMLReporter .failingAlert { background-color: #cf867e; } +#HTMLReporter .failingAlert:first-child { background-color: #b03911; } +#HTMLReporter .results { margin-top: 14px; } +#HTMLReporter #details { display: none; } +#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; } +#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } +#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } +#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } +#HTMLReporter.showDetails .summary { display: none; } +#HTMLReporter.showDetails #details { display: block; } +#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } +#HTMLReporter .summary { margin-top: 14px; } +#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; } +#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; } +#HTMLReporter .summary .specSummary.failed a { color: #b03911; } +#HTMLReporter .description + .suite { margin-top: 0; } +#HTMLReporter .suite { margin-top: 14px; } +#HTMLReporter .suite a { color: #333333; } +#HTMLReporter #details .specDetail { margin-bottom: 28px; } +#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; } +#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; } +#HTMLReporter .resultMessage span.result { display: block; } +#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } + +#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ } +#TrivialReporter a:visited, #TrivialReporter a { color: #303; } +#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; } +#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; } +#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; } +#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; } +#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; } +#TrivialReporter .runner.running { background-color: yellow; } +#TrivialReporter .options { text-align: right; font-size: .8em; } +#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; } +#TrivialReporter .suite .suite { margin: 5px; } +#TrivialReporter .suite.passed { background-color: #dfd; } +#TrivialReporter .suite.failed { background-color: #fdd; } +#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; } +#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; } +#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; } +#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; } +#TrivialReporter .spec.skipped { background-color: #bbb; } +#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; } +#TrivialReporter .passed { background-color: #cfc; display: none; } +#TrivialReporter .failed { background-color: #fbb; } +#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; } +#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; } +#TrivialReporter .resultMessage .mismatch { color: black; } +#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; } +#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; } +#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; } +#TrivialReporter #jasmine_content { position: fixed; right: 100%; } +#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; } diff --git a/plugins/org.apache.cordova.device/test/autotest/jasmine.js b/plugins/org.apache.cordova.device/test/autotest/jasmine.js new file mode 100644 index 0000000..bccb66c --- /dev/null +++ b/plugins/org.apache.cordova.device/test/autotest/jasmine.js @@ -0,0 +1,2530 @@ +var isCommonJS = typeof window == "undefined"; + +/** + * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework. + * + * @namespace + */ +var jasmine = {}; +if (isCommonJS) exports.jasmine = jasmine; +/** + * @private + */ +jasmine.unimplementedMethod_ = function() { + throw new Error("unimplemented method"); +}; + +/** + * Use jasmine.undefined instead of undefined, since undefined is just + * a plain old variable and may be redefined by somebody else. + * + * @private + */ +jasmine.undefined = jasmine.___undefined___; + +/** + * Show diagnostic messages in the console if set to true + * + */ +jasmine.VERBOSE = false; + +/** + * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed. + * + */ +jasmine.DEFAULT_UPDATE_INTERVAL = 250; + +/** + * Default timeout interval in milliseconds for waitsFor() blocks. + */ +jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; + +jasmine.getGlobal = function() { + function getGlobal() { + return this; + } + + return getGlobal(); +}; + +/** + * Allows for bound functions to be compared. Internal use only. + * + * @ignore + * @private + * @param base {Object} bound 'this' for the function + * @param name {Function} function to find + */ +jasmine.bindOriginal_ = function(base, name) { + var original = base[name]; + if (original.apply) { + return function() { + return original.apply(base, arguments); + }; + } else { + // IE support + return jasmine.getGlobal()[name]; + } +}; + +jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout'); +jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout'); +jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval'); +jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval'); + +jasmine.MessageResult = function(values) { + this.type = 'log'; + this.values = values; + this.trace = new Error(); // todo: test better +}; + +jasmine.MessageResult.prototype.toString = function() { + var text = ""; + for (var i = 0; i < this.values.length; i++) { + if (i > 0) text += " "; + if (jasmine.isString_(this.values[i])) { + text += this.values[i]; + } else { + text += jasmine.pp(this.values[i]); + } + } + return text; +}; + +jasmine.ExpectationResult = function(params) { + this.type = 'expect'; + this.matcherName = params.matcherName; + this.passed_ = params.passed; + this.expected = params.expected; + this.actual = params.actual; + this.message = this.passed_ ? 'Passed.' : params.message; + + var trace = (params.trace || new Error(this.message)); + this.trace = this.passed_ ? '' : trace; +}; + +jasmine.ExpectationResult.prototype.toString = function () { + return this.message; +}; + +jasmine.ExpectationResult.prototype.passed = function () { + return this.passed_; +}; + +/** + * Getter for the Jasmine environment. Ensures one gets created + */ +jasmine.getEnv = function() { + var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env(); + return env; +}; + +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ +jasmine.isArray_ = function(value) { + return jasmine.isA_("Array", value); +}; + +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ +jasmine.isString_ = function(value) { + return jasmine.isA_("String", value); +}; + +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ +jasmine.isNumber_ = function(value) { + return jasmine.isA_("Number", value); +}; + +/** + * @ignore + * @private + * @param {String} typeName + * @param value + * @returns {Boolean} + */ +jasmine.isA_ = function(typeName, value) { + return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; +}; + +/** + * Pretty printer for expecations. Takes any object and turns it into a human-readable string. + * + * @param value {Object} an object to be outputted + * @returns {String} + */ +jasmine.pp = function(value) { + var stringPrettyPrinter = new jasmine.StringPrettyPrinter(); + stringPrettyPrinter.format(value); + return stringPrettyPrinter.string; +}; + +/** + * Returns true if the object is a DOM Node. + * + * @param {Object} obj object to check + * @returns {Boolean} + */ +jasmine.isDomNode = function(obj) { + return obj.nodeType > 0; +}; + +/** + * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter. + * + * @example + * // don't care about which function is passed in, as long as it's a function + * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function)); + * + * @param {Class} clazz + * @returns matchable object of the type clazz + */ +jasmine.any = function(clazz) { + return new jasmine.Matchers.Any(clazz); +}; + +/** + * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the + * attributes on the object. + * + * @example + * // don't care about any other attributes than foo. + * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"}); + * + * @param sample {Object} sample + * @returns matchable object for the sample + */ +jasmine.objectContaining = function (sample) { + return new jasmine.Matchers.ObjectContaining(sample); +}; + +/** + * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks. + * + * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine + * expectation syntax. Spies can be checked if they were called or not and what the calling params were. + * + * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs). + * + * Spies are torn down at the end of every spec. + * + * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj. + * + * @example + * // a stub + * var myStub = jasmine.createSpy('myStub'); // can be used anywhere + * + * // spy example + * var foo = { + * not: function(bool) { return !bool; } + * } + * + * // actual foo.not will not be called, execution stops + * spyOn(foo, 'not'); + + // foo.not spied upon, execution will continue to implementation + * spyOn(foo, 'not').andCallThrough(); + * + * // fake example + * var foo = { + * not: function(bool) { return !bool; } + * } + * + * // foo.not(val) will return val + * spyOn(foo, 'not').andCallFake(function(value) {return value;}); + * + * // mock example + * foo.not(7 == 7); + * expect(foo.not).toHaveBeenCalled(); + * expect(foo.not).toHaveBeenCalledWith(true); + * + * @constructor + * @see spyOn, jasmine.createSpy, jasmine.createSpyObj + * @param {String} name + */ +jasmine.Spy = function(name) { + /** + * The name of the spy, if provided. + */ + this.identity = name || 'unknown'; + /** + * Is this Object a spy? + */ + this.isSpy = true; + /** + * The actual function this spy stubs. + */ + this.plan = function() { + }; + /** + * Tracking of the most recent call to the spy. + * @example + * var mySpy = jasmine.createSpy('foo'); + * mySpy(1, 2); + * mySpy.mostRecentCall.args = [1, 2]; + */ + this.mostRecentCall = {}; + + /** + * Holds arguments for each call to the spy, indexed by call count + * @example + * var mySpy = jasmine.createSpy('foo'); + * mySpy(1, 2); + * mySpy(7, 8); + * mySpy.mostRecentCall.args = [7, 8]; + * mySpy.argsForCall[0] = [1, 2]; + * mySpy.argsForCall[1] = [7, 8]; + */ + this.argsForCall = []; + this.calls = []; +}; + +/** + * Tells a spy to call through to the actual implemenatation. + * + * @example + * var foo = { + * bar: function() { // do some stuff } + * } + * + * // defining a spy on an existing property: foo.bar + * spyOn(foo, 'bar').andCallThrough(); + */ +jasmine.Spy.prototype.andCallThrough = function() { + this.plan = this.originalValue; + return this; +}; + +/** + * For setting the return value of a spy. + * + * @example + * // defining a spy from scratch: foo() returns 'baz' + * var foo = jasmine.createSpy('spy on foo').andReturn('baz'); + * + * // defining a spy on an existing property: foo.bar() returns 'baz' + * spyOn(foo, 'bar').andReturn('baz'); + * + * @param {Object} value + */ +jasmine.Spy.prototype.andReturn = function(value) { + this.plan = function() { + return value; + }; + return this; +}; + +/** + * For throwing an exception when a spy is called. + * + * @example + * // defining a spy from scratch: foo() throws an exception w/ message 'ouch' + * var foo = jasmine.createSpy('spy on foo').andThrow('baz'); + * + * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch' + * spyOn(foo, 'bar').andThrow('baz'); + * + * @param {String} exceptionMsg + */ +jasmine.Spy.prototype.andThrow = function(exceptionMsg) { + this.plan = function() { + throw exceptionMsg; + }; + return this; +}; + +/** + * Calls an alternate implementation when a spy is called. + * + * @example + * var baz = function() { + * // do some stuff, return something + * } + * // defining a spy from scratch: foo() calls the function baz + * var foo = jasmine.createSpy('spy on foo').andCall(baz); + * + * // defining a spy on an existing property: foo.bar() calls an anonymnous function + * spyOn(foo, 'bar').andCall(function() { return 'baz';} ); + * + * @param {Function} fakeFunc + */ +jasmine.Spy.prototype.andCallFake = function(fakeFunc) { + this.plan = fakeFunc; + return this; +}; + +/** + * Resets all of a spy's the tracking variables so that it can be used again. + * + * @example + * spyOn(foo, 'bar'); + * + * foo.bar(); + * + * expect(foo.bar.callCount).toEqual(1); + * + * foo.bar.reset(); + * + * expect(foo.bar.callCount).toEqual(0); + */ +jasmine.Spy.prototype.reset = function() { + this.wasCalled = false; + this.callCount = 0; + this.argsForCall = []; + this.calls = []; + this.mostRecentCall = {}; +}; + +jasmine.createSpy = function(name) { + + var spyObj = function() { + spyObj.wasCalled = true; + spyObj.callCount++; + var args = jasmine.util.argsToArray(arguments); + spyObj.mostRecentCall.object = this; + spyObj.mostRecentCall.args = args; + spyObj.argsForCall.push(args); + spyObj.calls.push({object: this, args: args}); + return spyObj.plan.apply(this, arguments); + }; + + var spy = new jasmine.Spy(name); + + for (var prop in spy) { + spyObj[prop] = spy[prop]; + } + + spyObj.reset(); + + return spyObj; +}; + +/** + * Determines whether an object is a spy. + * + * @param {jasmine.Spy|Object} putativeSpy + * @returns {Boolean} + */ +jasmine.isSpy = function(putativeSpy) { + return putativeSpy && putativeSpy.isSpy; +}; + +/** + * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something + * large in one call. + * + * @param {String} baseName name of spy class + * @param {Array} methodNames array of names of methods to make spies + */ +jasmine.createSpyObj = function(baseName, methodNames) { + if (!jasmine.isArray_(methodNames) || methodNames.length === 0) { + throw new Error('createSpyObj requires a non-empty array of method names to create spies for'); + } + var obj = {}; + for (var i = 0; i < methodNames.length; i++) { + obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]); + } + return obj; +}; + +/** + * All parameters are pretty-printed and concatenated together, then written to the current spec's output. + * + * Be careful not to leave calls to jasmine.log in production code. + */ +jasmine.log = function() { + var spec = jasmine.getEnv().currentSpec; + spec.log.apply(spec, arguments); +}; + +/** + * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy. + * + * @example + * // spy example + * var foo = { + * not: function(bool) { return !bool; } + * } + * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops + * + * @see jasmine.createSpy + * @param obj + * @param methodName + * @returns a Jasmine spy that can be chained with all spy methods + */ +var spyOn = function(obj, methodName) { + return jasmine.getEnv().currentSpec.spyOn(obj, methodName); +}; +if (isCommonJS) exports.spyOn = spyOn; + +/** + * Creates a Jasmine spec that will be added to the current suite. + * + * // TODO: pending tests + * + * @example + * it('should be true', function() { + * expect(true).toEqual(true); + * }); + * + * @param {String} desc description of this specification + * @param {Function} func defines the preconditions and expectations of the spec + */ +var it = function(desc, func) { + return jasmine.getEnv().it(desc, func); +}; +if (isCommonJS) exports.it = it; + +/** + * Creates a disabled Jasmine spec. + * + * A convenience method that allows existing specs to be disabled temporarily during development. + * + * @param {String} desc description of this specification + * @param {Function} func defines the preconditions and expectations of the spec + */ +var xit = function(desc, func) { + return jasmine.getEnv().xit(desc, func); +}; +if (isCommonJS) exports.xit = xit; + +/** + * Starts a chain for a Jasmine expectation. + * + * It is passed an Object that is the actual value and should chain to one of the many + * jasmine.Matchers functions. + * + * @param {Object} actual Actual value to test against and expected value + */ +var expect = function(actual) { + return jasmine.getEnv().currentSpec.expect(actual); +}; +if (isCommonJS) exports.expect = expect; + +/** + * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs. + * + * @param {Function} func Function that defines part of a jasmine spec. + */ +var runs = function(func) { + jasmine.getEnv().currentSpec.runs(func); +}; +if (isCommonJS) exports.runs = runs; + +/** + * Waits a fixed time period before moving to the next block. + * + * @deprecated Use waitsFor() instead + * @param {Number} timeout milliseconds to wait + */ +var waits = function(timeout) { + jasmine.getEnv().currentSpec.waits(timeout); +}; +if (isCommonJS) exports.waits = waits; + +/** + * Waits for the latchFunction to return true before proceeding to the next block. + * + * @param {Function} latchFunction + * @param {String} optional_timeoutMessage + * @param {Number} optional_timeout + */ +var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { + jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments); +}; +if (isCommonJS) exports.waitsFor = waitsFor; + +/** + * A function that is called before each spec in a suite. + * + * Used for spec setup, including validating assumptions. + * + * @param {Function} beforeEachFunction + */ +var beforeEach = function(beforeEachFunction) { + jasmine.getEnv().beforeEach(beforeEachFunction); +}; +if (isCommonJS) exports.beforeEach = beforeEach; + +/** + * A function that is called after each spec in a suite. + * + * Used for restoring any state that is hijacked during spec execution. + * + * @param {Function} afterEachFunction + */ +var afterEach = function(afterEachFunction) { + jasmine.getEnv().afterEach(afterEachFunction); +}; +if (isCommonJS) exports.afterEach = afterEach; + +/** + * Defines a suite of specifications. + * + * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared + * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization + * of setup in some tests. + * + * @example + * // TODO: a simple suite + * + * // TODO: a simple suite with a nested describe block + * + * @param {String} description A string, usually the class under test. + * @param {Function} specDefinitions function that defines several specs. + */ +var describe = function(description, specDefinitions) { + return jasmine.getEnv().describe(description, specDefinitions); +}; +if (isCommonJS) exports.describe = describe; + +/** + * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development. + * + * @param {String} description A string, usually the class under test. + * @param {Function} specDefinitions function that defines several specs. + */ +var xdescribe = function(description, specDefinitions) { + return jasmine.getEnv().xdescribe(description, specDefinitions); +}; +if (isCommonJS) exports.xdescribe = xdescribe; + + +// Provide the XMLHttpRequest class for IE 5.x-6.x: +jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() { + function tryIt(f) { + try { + return f(); + } catch(e) { + } + return null; + } + + var xhr = tryIt(function() { + return new ActiveXObject("Msxml2.XMLHTTP.6.0"); + }) || + tryIt(function() { + return new ActiveXObject("Msxml2.XMLHTTP.3.0"); + }) || + tryIt(function() { + return new ActiveXObject("Msxml2.XMLHTTP"); + }) || + tryIt(function() { + return new ActiveXObject("Microsoft.XMLHTTP"); + }); + + if (!xhr) throw new Error("This browser does not support XMLHttpRequest."); + + return xhr; +} : XMLHttpRequest; +/** + * @namespace + */ +jasmine.util = {}; + +/** + * Declare that a child class inherit it's prototype from the parent class. + * + * @private + * @param {Function} childClass + * @param {Function} parentClass + */ +jasmine.util.inherit = function(childClass, parentClass) { + /** + * @private + */ + var subclass = function() { + }; + subclass.prototype = parentClass.prototype; + childClass.prototype = new subclass(); +}; + +jasmine.util.formatException = function(e) { + var lineNumber; + if (e.line) { + lineNumber = e.line; + } + else if (e.lineNumber) { + lineNumber = e.lineNumber; + } + + var file; + + if (e.sourceURL) { + file = e.sourceURL; + } + else if (e.fileName) { + file = e.fileName; + } + + var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString(); + + if (file && lineNumber) { + message += ' in ' + file + ' (line ' + lineNumber + ')'; + } + + return message; +}; + +jasmine.util.htmlEscape = function(str) { + if (!str) return str; + return str.replace(/&/g, '&') + .replace(//g, '>'); +}; + +jasmine.util.argsToArray = function(args) { + var arrayOfArgs = []; + for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]); + return arrayOfArgs; +}; + +jasmine.util.extend = function(destination, source) { + for (var property in source) destination[property] = source[property]; + return destination; +}; + +/** + * Environment for Jasmine + * + * @constructor + */ +jasmine.Env = function() { + this.currentSpec = null; + this.currentSuite = null; + this.currentRunner_ = new jasmine.Runner(this); + + this.reporter = new jasmine.MultiReporter(); + + this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL; + this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL; + this.lastUpdate = 0; + this.specFilter = function() { + return true; + }; + + this.nextSpecId_ = 0; + this.nextSuiteId_ = 0; + this.equalityTesters_ = []; + + // wrap matchers + this.matchersClass = function() { + jasmine.Matchers.apply(this, arguments); + }; + jasmine.util.inherit(this.matchersClass, jasmine.Matchers); + + jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass); +}; + + +jasmine.Env.prototype.setTimeout = jasmine.setTimeout; +jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout; +jasmine.Env.prototype.setInterval = jasmine.setInterval; +jasmine.Env.prototype.clearInterval = jasmine.clearInterval; + +/** + * @returns an object containing jasmine version build info, if set. + */ +jasmine.Env.prototype.version = function () { + if (jasmine.version_) { + return jasmine.version_; + } else { + throw new Error('Version not set'); + } +}; + +/** + * @returns string containing jasmine version build info, if set. + */ +jasmine.Env.prototype.versionString = function() { + if (!jasmine.version_) { + return "version unknown"; + } + + var version = this.version(); + var versionString = version.major + "." + version.minor + "." + version.build; + if (version.release_candidate) { + versionString += ".rc" + version.release_candidate; + } + versionString += " revision " + version.revision; + return versionString; +}; + +/** + * @returns a sequential integer starting at 0 + */ +jasmine.Env.prototype.nextSpecId = function () { + return this.nextSpecId_++; +}; + +/** + * @returns a sequential integer starting at 0 + */ +jasmine.Env.prototype.nextSuiteId = function () { + return this.nextSuiteId_++; +}; + +/** + * Register a reporter to receive status updates from Jasmine. + * @param {jasmine.Reporter} reporter An object which will receive status updates. + */ +jasmine.Env.prototype.addReporter = function(reporter) { + this.reporter.addReporter(reporter); +}; + +jasmine.Env.prototype.execute = function() { + this.currentRunner_.execute(); +}; + +jasmine.Env.prototype.describe = function(description, specDefinitions) { + var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite); + + var parentSuite = this.currentSuite; + if (parentSuite) { + parentSuite.add(suite); + } else { + this.currentRunner_.add(suite); + } + + this.currentSuite = suite; + + var declarationError = null; + try { + specDefinitions.call(suite); + } catch(e) { + declarationError = e; + } + + if (declarationError) { + this.it("encountered a declaration exception", function() { + throw declarationError; + }); + } + + this.currentSuite = parentSuite; + + return suite; +}; + +jasmine.Env.prototype.beforeEach = function(beforeEachFunction) { + if (this.currentSuite) { + this.currentSuite.beforeEach(beforeEachFunction); + } else { + this.currentRunner_.beforeEach(beforeEachFunction); + } +}; + +jasmine.Env.prototype.currentRunner = function () { + return this.currentRunner_; +}; + +jasmine.Env.prototype.afterEach = function(afterEachFunction) { + if (this.currentSuite) { + this.currentSuite.afterEach(afterEachFunction); + } else { + this.currentRunner_.afterEach(afterEachFunction); + } + +}; + +jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) { + return { + execute: function() { + } + }; +}; + +jasmine.Env.prototype.it = function(description, func) { + var spec = new jasmine.Spec(this, this.currentSuite, description); + this.currentSuite.add(spec); + this.currentSpec = spec; + + if (func) { + spec.runs(func); + } + + return spec; +}; + +jasmine.Env.prototype.xit = function(desc, func) { + return { + id: this.nextSpecId(), + runs: function() { + } + }; +}; + +jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) { + if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) { + return true; + } + + a.__Jasmine_been_here_before__ = b; + b.__Jasmine_been_here_before__ = a; + + var hasKey = function(obj, keyName) { + return obj !== null && obj[keyName] !== jasmine.undefined; + }; + + for (var property in b) { + if (!hasKey(a, property) && hasKey(b, property)) { + mismatchKeys.push("expected has key '" + property + "', but missing from actual."); + } + } + for (property in a) { + if (!hasKey(b, property) && hasKey(a, property)) { + mismatchKeys.push("expected missing key '" + property + "', but present in actual."); + } + } + for (property in b) { + if (property == '__Jasmine_been_here_before__') continue; + if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) { + mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual."); + } + } + + if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) { + mismatchValues.push("arrays were not the same length"); + } + + delete a.__Jasmine_been_here_before__; + delete b.__Jasmine_been_here_before__; + return (mismatchKeys.length === 0 && mismatchValues.length === 0); +}; + +jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) { + mismatchKeys = mismatchKeys || []; + mismatchValues = mismatchValues || []; + + for (var i = 0; i < this.equalityTesters_.length; i++) { + var equalityTester = this.equalityTesters_[i]; + var result = equalityTester(a, b, this, mismatchKeys, mismatchValues); + if (result !== jasmine.undefined) return result; + } + + if (a === b) return true; + + if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) { + return (a == jasmine.undefined && b == jasmine.undefined); + } + + if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) { + return a === b; + } + + if (a instanceof Date && b instanceof Date) { + return a.getTime() == b.getTime(); + } + + if (a.jasmineMatches) { + return a.jasmineMatches(b); + } + + if (b.jasmineMatches) { + return b.jasmineMatches(a); + } + + if (a instanceof jasmine.Matchers.ObjectContaining) { + return a.matches(b); + } + + if (b instanceof jasmine.Matchers.ObjectContaining) { + return b.matches(a); + } + + if (jasmine.isString_(a) && jasmine.isString_(b)) { + return (a == b); + } + + if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) { + return (a == b); + } + + if (typeof a === "object" && typeof b === "object") { + return this.compareObjects_(a, b, mismatchKeys, mismatchValues); + } + + //Straight check + return (a === b); +}; + +jasmine.Env.prototype.contains_ = function(haystack, needle) { + if (jasmine.isArray_(haystack)) { + for (var i = 0; i < haystack.length; i++) { + if (this.equals_(haystack[i], needle)) return true; + } + return false; + } + return haystack.indexOf(needle) >= 0; +}; + +jasmine.Env.prototype.addEqualityTester = function(equalityTester) { + this.equalityTesters_.push(equalityTester); +}; +/** No-op base class for Jasmine reporters. + * + * @constructor + */ +jasmine.Reporter = function() { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportRunnerStarting = function(runner) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportRunnerResults = function(runner) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportSuiteResults = function(suite) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportSpecStarting = function(spec) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.reportSpecResults = function(spec) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.Reporter.prototype.log = function(str) { +}; + +/** + * Blocks are functions with executable code that make up a spec. + * + * @constructor + * @param {jasmine.Env} env + * @param {Function} func + * @param {jasmine.Spec} spec + */ +jasmine.Block = function(env, func, spec) { + this.env = env; + this.func = func; + this.spec = spec; +}; + +jasmine.Block.prototype.execute = function(onComplete) { + try { + this.func.apply(this.spec); + } catch (e) { + this.spec.fail(e); + } + onComplete(); +}; +/** JavaScript API reporter. + * + * @constructor + */ +jasmine.JsApiReporter = function() { + this.started = false; + this.finished = false; + this.suites_ = []; + this.results_ = {}; +}; + +jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) { + this.started = true; + var suites = runner.topLevelSuites(); + for (var i = 0; i < suites.length; i++) { + var suite = suites[i]; + this.suites_.push(this.summarize_(suite)); + } +}; + +jasmine.JsApiReporter.prototype.suites = function() { + return this.suites_; +}; + +jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) { + var isSuite = suiteOrSpec instanceof jasmine.Suite; + var summary = { + id: suiteOrSpec.id, + name: suiteOrSpec.description, + type: isSuite ? 'suite' : 'spec', + children: [] + }; + + if (isSuite) { + var children = suiteOrSpec.children(); + for (var i = 0; i < children.length; i++) { + summary.children.push(this.summarize_(children[i])); + } + } + return summary; +}; + +jasmine.JsApiReporter.prototype.results = function() { + return this.results_; +}; + +jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) { + return this.results_[specId]; +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) { + this.finished = true; +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) { +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) { + this.results_[spec.id] = { + messages: spec.results().getItems(), + result: spec.results().failedCount > 0 ? "failed" : "passed" + }; +}; + +//noinspection JSUnusedLocalSymbols +jasmine.JsApiReporter.prototype.log = function(str) { +}; + +jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){ + var results = {}; + for (var i = 0; i < specIds.length; i++) { + var specId = specIds[i]; + results[specId] = this.summarizeResult_(this.results_[specId]); + } + return results; +}; + +jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){ + var summaryMessages = []; + var messagesLength = result.messages.length; + for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) { + var resultMessage = result.messages[messageIndex]; + summaryMessages.push({ + text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined, + passed: resultMessage.passed ? resultMessage.passed() : true, + type: resultMessage.type, + message: resultMessage.message, + trace: { + stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined + } + }); + } + + return { + result : result.result, + messages : summaryMessages + }; +}; + +/** + * @constructor + * @param {jasmine.Env} env + * @param actual + * @param {jasmine.Spec} spec + */ +jasmine.Matchers = function(env, actual, spec, opt_isNot) { + this.env = env; + this.actual = actual; + this.spec = spec; + this.isNot = opt_isNot || false; + this.reportWasCalled_ = false; +}; + +// todo: @deprecated as of Jasmine 0.11, remove soon [xw] +jasmine.Matchers.pp = function(str) { + throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!"); +}; + +// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw] +jasmine.Matchers.prototype.report = function(result, failing_message, details) { + throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs"); +}; + +jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) { + for (var methodName in prototype) { + if (methodName == 'report') continue; + var orig = prototype[methodName]; + matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig); + } +}; + +jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { + return function() { + var matcherArgs = jasmine.util.argsToArray(arguments); + var result = matcherFunction.apply(this, arguments); + + if (this.isNot) { + result = !result; + } + + if (this.reportWasCalled_) return result; + + var message; + if (!result) { + if (this.message) { + message = this.message.apply(this, arguments); + if (jasmine.isArray_(message)) { + message = message[this.isNot ? 1 : 0]; + } + } else { + var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); + message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate; + if (matcherArgs.length > 0) { + for (var i = 0; i < matcherArgs.length; i++) { + if (i > 0) message += ","; + message += " " + jasmine.pp(matcherArgs[i]); + } + } + message += "."; + } + } + var expectationResult = new jasmine.ExpectationResult({ + matcherName: matcherName, + passed: result, + expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0], + actual: this.actual, + message: message + }); + this.spec.addMatcherResult(expectationResult); + return jasmine.undefined; + }; +}; + + + + +/** + * toBe: compares the actual to the expected using === + * @param expected + */ +jasmine.Matchers.prototype.toBe = function(expected) { + return this.actual === expected; +}; + +/** + * toNotBe: compares the actual to the expected using !== + * @param expected + * @deprecated as of 1.0. Use not.toBe() instead. + */ +jasmine.Matchers.prototype.toNotBe = function(expected) { + return this.actual !== expected; +}; + +/** + * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc. + * + * @param expected + */ +jasmine.Matchers.prototype.toEqual = function(expected) { + return this.env.equals_(this.actual, expected); +}; + +/** + * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual + * @param expected + * @deprecated as of 1.0. Use not.toEqual() instead. + */ +jasmine.Matchers.prototype.toNotEqual = function(expected) { + return !this.env.equals_(this.actual, expected); +}; + +/** + * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes + * a pattern or a String. + * + * @param expected + */ +jasmine.Matchers.prototype.toMatch = function(expected) { + return new RegExp(expected).test(this.actual); +}; + +/** + * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch + * @param expected + * @deprecated as of 1.0. Use not.toMatch() instead. + */ +jasmine.Matchers.prototype.toNotMatch = function(expected) { + return !(new RegExp(expected).test(this.actual)); +}; + +/** + * Matcher that compares the actual to jasmine.undefined. + */ +jasmine.Matchers.prototype.toBeDefined = function() { + return (this.actual !== jasmine.undefined); +}; + +/** + * Matcher that compares the actual to jasmine.undefined. + */ +jasmine.Matchers.prototype.toBeUndefined = function() { + return (this.actual === jasmine.undefined); +}; + +/** + * Matcher that compares the actual to null. + */ +jasmine.Matchers.prototype.toBeNull = function() { + return (this.actual === null); +}; + +/** + * Matcher that boolean not-nots the actual. + */ +jasmine.Matchers.prototype.toBeTruthy = function() { + return !!this.actual; +}; + + +/** + * Matcher that boolean nots the actual. + */ +jasmine.Matchers.prototype.toBeFalsy = function() { + return !this.actual; +}; + + +/** + * Matcher that checks to see if the actual, a Jasmine spy, was called. + */ +jasmine.Matchers.prototype.toHaveBeenCalled = function() { + if (arguments.length > 0) { + throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); + } + + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + + this.message = function() { + return [ + "Expected spy " + this.actual.identity + " to have been called.", + "Expected spy " + this.actual.identity + " not to have been called." + ]; + }; + + return this.actual.wasCalled; +}; + +/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */ +jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled; + +/** + * Matcher that checks to see if the actual, a Jasmine spy, was not called. + * + * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead + */ +jasmine.Matchers.prototype.wasNotCalled = function() { + if (arguments.length > 0) { + throw new Error('wasNotCalled does not take arguments'); + } + + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + + this.message = function() { + return [ + "Expected spy " + this.actual.identity + " to not have been called.", + "Expected spy " + this.actual.identity + " to have been called." + ]; + }; + + return !this.actual.wasCalled; +}; + +/** + * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters. + * + * @example + * + */ +jasmine.Matchers.prototype.toHaveBeenCalledWith = function() { + var expectedArgs = jasmine.util.argsToArray(arguments); + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + this.message = function() { + if (this.actual.callCount === 0) { + // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw] + return [ + "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.", + "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was." + ]; + } else { + return [ + "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall), + "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall) + ]; + } + }; + + return this.env.contains_(this.actual.argsForCall, expectedArgs); +}; + +/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */ +jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith; + +/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */ +jasmine.Matchers.prototype.wasNotCalledWith = function() { + var expectedArgs = jasmine.util.argsToArray(arguments); + if (!jasmine.isSpy(this.actual)) { + throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); + } + + this.message = function() { + return [ + "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was", + "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was" + ]; + }; + + return !this.env.contains_(this.actual.argsForCall, expectedArgs); +}; + +/** + * Matcher that checks that the expected item is an element in the actual Array. + * + * @param {Object} expected + */ +jasmine.Matchers.prototype.toContain = function(expected) { + return this.env.contains_(this.actual, expected); +}; + +/** + * Matcher that checks that the expected item is NOT an element in the actual Array. + * + * @param {Object} expected + * @deprecated as of 1.0. Use not.toContain() instead. + */ +jasmine.Matchers.prototype.toNotContain = function(expected) { + return !this.env.contains_(this.actual, expected); +}; + +jasmine.Matchers.prototype.toBeLessThan = function(expected) { + return this.actual < expected; +}; + +jasmine.Matchers.prototype.toBeGreaterThan = function(expected) { + return this.actual > expected; +}; + +/** + * Matcher that checks that the expected item is equal to the actual item + * up to a given level of decimal precision (default 2). + * + * @param {Number} expected + * @param {Number} precision + */ +jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) { + if (!(precision === 0)) { + precision = precision || 2; + } + var multiplier = Math.pow(10, precision); + var actual = Math.round(this.actual * multiplier); + expected = Math.round(expected * multiplier); + return expected == actual; +}; + +/** + * Matcher that checks that the expected exception was thrown by the actual. + * + * @param {String} expected + */ +jasmine.Matchers.prototype.toThrow = function(expected) { + var result = false; + var exception; + if (typeof this.actual != 'function') { + throw new Error('Actual is not a function'); + } + try { + this.actual(); + } catch (e) { + exception = e; + } + if (exception) { + result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected)); + } + + var not = this.isNot ? "not " : ""; + + this.message = function() { + if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) { + return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' '); + } else { + return "Expected function to throw an exception."; + } + }; + + return result; +}; + +jasmine.Matchers.Any = function(expectedClass) { + this.expectedClass = expectedClass; +}; + +jasmine.Matchers.Any.prototype.jasmineMatches = function(other) { + if (this.expectedClass == String) { + return typeof other == 'string' || other instanceof String; + } + + if (this.expectedClass == Number) { + return typeof other == 'number' || other instanceof Number; + } + + if (this.expectedClass == Function) { + return typeof other == 'function' || other instanceof Function; + } + + if (this.expectedClass == Object) { + return typeof other == 'object'; + } + + return other instanceof this.expectedClass; +}; + +jasmine.Matchers.Any.prototype.jasmineToString = function() { + return ''; +}; + +jasmine.Matchers.ObjectContaining = function (sample) { + this.sample = sample; +}; + +jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) { + mismatchKeys = mismatchKeys || []; + mismatchValues = mismatchValues || []; + + var env = jasmine.getEnv(); + + var hasKey = function(obj, keyName) { + return obj != null && obj[keyName] !== jasmine.undefined; + }; + + for (var property in this.sample) { + if (!hasKey(other, property) && hasKey(this.sample, property)) { + mismatchKeys.push("expected has key '" + property + "', but missing from actual."); + } + else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) { + mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual."); + } + } + + return (mismatchKeys.length === 0 && mismatchValues.length === 0); +}; + +jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () { + return ""; +}; +// Mock setTimeout, clearTimeout +// Contributed by Pivotal Computer Systems, www.pivotalsf.com + +jasmine.FakeTimer = function() { + this.reset(); + + var self = this; + self.setTimeout = function(funcToCall, millis) { + self.timeoutsMade++; + self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false); + return self.timeoutsMade; + }; + + self.setInterval = function(funcToCall, millis) { + self.timeoutsMade++; + self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true); + return self.timeoutsMade; + }; + + self.clearTimeout = function(timeoutKey) { + self.scheduledFunctions[timeoutKey] = jasmine.undefined; + }; + + self.clearInterval = function(timeoutKey) { + self.scheduledFunctions[timeoutKey] = jasmine.undefined; + }; + +}; + +jasmine.FakeTimer.prototype.reset = function() { + this.timeoutsMade = 0; + this.scheduledFunctions = {}; + this.nowMillis = 0; +}; + +jasmine.FakeTimer.prototype.tick = function(millis) { + var oldMillis = this.nowMillis; + var newMillis = oldMillis + millis; + this.runFunctionsWithinRange(oldMillis, newMillis); + this.nowMillis = newMillis; +}; + +jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) { + var scheduledFunc; + var funcsToRun = []; + for (var timeoutKey in this.scheduledFunctions) { + scheduledFunc = this.scheduledFunctions[timeoutKey]; + if (scheduledFunc != jasmine.undefined && + scheduledFunc.runAtMillis >= oldMillis && + scheduledFunc.runAtMillis <= nowMillis) { + funcsToRun.push(scheduledFunc); + this.scheduledFunctions[timeoutKey] = jasmine.undefined; + } + } + + if (funcsToRun.length > 0) { + funcsToRun.sort(function(a, b) { + return a.runAtMillis - b.runAtMillis; + }); + for (var i = 0; i < funcsToRun.length; ++i) { + try { + var funcToRun = funcsToRun[i]; + this.nowMillis = funcToRun.runAtMillis; + funcToRun.funcToCall(); + if (funcToRun.recurring) { + this.scheduleFunction(funcToRun.timeoutKey, + funcToRun.funcToCall, + funcToRun.millis, + true); + } + } catch(e) { + } + } + this.runFunctionsWithinRange(oldMillis, nowMillis); + } +}; + +jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) { + this.scheduledFunctions[timeoutKey] = { + runAtMillis: this.nowMillis + millis, + funcToCall: funcToCall, + recurring: recurring, + timeoutKey: timeoutKey, + millis: millis + }; +}; + +/** + * @namespace + */ +jasmine.Clock = { + defaultFakeTimer: new jasmine.FakeTimer(), + + reset: function() { + jasmine.Clock.assertInstalled(); + jasmine.Clock.defaultFakeTimer.reset(); + }, + + tick: function(millis) { + jasmine.Clock.assertInstalled(); + jasmine.Clock.defaultFakeTimer.tick(millis); + }, + + runFunctionsWithinRange: function(oldMillis, nowMillis) { + jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis); + }, + + scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) { + jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring); + }, + + useMock: function() { + if (!jasmine.Clock.isInstalled()) { + var spec = jasmine.getEnv().currentSpec; + spec.after(jasmine.Clock.uninstallMock); + + jasmine.Clock.installMock(); + } + }, + + installMock: function() { + jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer; + }, + + uninstallMock: function() { + jasmine.Clock.assertInstalled(); + jasmine.Clock.installed = jasmine.Clock.real; + }, + + real: { + setTimeout: jasmine.getGlobal().setTimeout, + clearTimeout: jasmine.getGlobal().clearTimeout, + setInterval: jasmine.getGlobal().setInterval, + clearInterval: jasmine.getGlobal().clearInterval + }, + + assertInstalled: function() { + if (!jasmine.Clock.isInstalled()) { + throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()"); + } + }, + + isInstalled: function() { + return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer; + }, + + installed: null +}; +jasmine.Clock.installed = jasmine.Clock.real; + +//else for IE support +jasmine.getGlobal().setTimeout = function(funcToCall, millis) { + if (jasmine.Clock.installed.setTimeout.apply) { + return jasmine.Clock.installed.setTimeout.apply(this, arguments); + } else { + return jasmine.Clock.installed.setTimeout(funcToCall, millis); + } +}; + +jasmine.getGlobal().setInterval = function(funcToCall, millis) { + if (jasmine.Clock.installed.setInterval.apply) { + return jasmine.Clock.installed.setInterval.apply(this, arguments); + } else { + return jasmine.Clock.installed.setInterval(funcToCall, millis); + } +}; + +jasmine.getGlobal().clearTimeout = function(timeoutKey) { + if (jasmine.Clock.installed.clearTimeout.apply) { + return jasmine.Clock.installed.clearTimeout.apply(this, arguments); + } else { + return jasmine.Clock.installed.clearTimeout(timeoutKey); + } +}; + +jasmine.getGlobal().clearInterval = function(timeoutKey) { + if (jasmine.Clock.installed.clearTimeout.apply) { + return jasmine.Clock.installed.clearInterval.apply(this, arguments); + } else { + return jasmine.Clock.installed.clearInterval(timeoutKey); + } +}; + +/** + * @constructor + */ +jasmine.MultiReporter = function() { + this.subReporters_ = []; +}; +jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter); + +jasmine.MultiReporter.prototype.addReporter = function(reporter) { + this.subReporters_.push(reporter); +}; + +(function() { + var functionNames = [ + "reportRunnerStarting", + "reportRunnerResults", + "reportSuiteResults", + "reportSpecStarting", + "reportSpecResults", + "log" + ]; + for (var i = 0; i < functionNames.length; i++) { + var functionName = functionNames[i]; + jasmine.MultiReporter.prototype[functionName] = (function(functionName) { + return function() { + for (var j = 0; j < this.subReporters_.length; j++) { + var subReporter = this.subReporters_[j]; + if (subReporter[functionName]) { + subReporter[functionName].apply(subReporter, arguments); + } + } + }; + })(functionName); + } +})(); +/** + * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults + * + * @constructor + */ +jasmine.NestedResults = function() { + /** + * The total count of results + */ + this.totalCount = 0; + /** + * Number of passed results + */ + this.passedCount = 0; + /** + * Number of failed results + */ + this.failedCount = 0; + /** + * Was this suite/spec skipped? + */ + this.skipped = false; + /** + * @ignore + */ + this.items_ = []; +}; + +/** + * Roll up the result counts. + * + * @param result + */ +jasmine.NestedResults.prototype.rollupCounts = function(result) { + this.totalCount += result.totalCount; + this.passedCount += result.passedCount; + this.failedCount += result.failedCount; +}; + +/** + * Adds a log message. + * @param values Array of message parts which will be concatenated later. + */ +jasmine.NestedResults.prototype.log = function(values) { + this.items_.push(new jasmine.MessageResult(values)); +}; + +/** + * Getter for the results: message & results. + */ +jasmine.NestedResults.prototype.getItems = function() { + return this.items_; +}; + +/** + * Adds a result, tracking counts (total, passed, & failed) + * @param {jasmine.ExpectationResult|jasmine.NestedResults} result + */ +jasmine.NestedResults.prototype.addResult = function(result) { + if (result.type != 'log') { + if (result.items_) { + this.rollupCounts(result); + } else { + this.totalCount++; + if (result.passed()) { + this.passedCount++; + } else { + this.failedCount++; + } + } + } + this.items_.push(result); +}; + +/** + * @returns {Boolean} True if everything below passed + */ +jasmine.NestedResults.prototype.passed = function() { + return this.passedCount === this.totalCount; +}; +/** + * Base class for pretty printing for expectation results. + */ +jasmine.PrettyPrinter = function() { + this.ppNestLevel_ = 0; +}; + +/** + * Formats a value in a nice, human-readable string. + * + * @param value + */ +jasmine.PrettyPrinter.prototype.format = function(value) { + if (this.ppNestLevel_ > 40) { + throw new Error('jasmine.PrettyPrinter: format() nested too deeply!'); + } + + this.ppNestLevel_++; + try { + if (value === jasmine.undefined) { + this.emitScalar('undefined'); + } else if (value === null) { + this.emitScalar('null'); + } else if (value === jasmine.getGlobal()) { + this.emitScalar(''); + } else if (value.jasmineToString) { + this.emitScalar(value.jasmineToString()); + } else if (typeof value === 'string') { + this.emitString(value); + } else if (jasmine.isSpy(value)) { + this.emitScalar("spy on " + value.identity); + } else if (value instanceof RegExp) { + this.emitScalar(value.toString()); + } else if (typeof value === 'function') { + this.emitScalar('Function'); + } else if (typeof value.nodeType === 'number') { + this.emitScalar('HTMLNode'); + } else if (value instanceof Date) { + this.emitScalar('Date(' + value + ')'); + } else if (value.__Jasmine_been_here_before__) { + this.emitScalar(''); + } else if (jasmine.isArray_(value) || typeof value == 'object') { + value.__Jasmine_been_here_before__ = true; + if (jasmine.isArray_(value)) { + this.emitArray(value); + } else { + this.emitObject(value); + } + delete value.__Jasmine_been_here_before__; + } else { + this.emitScalar(value.toString()); + } + } finally { + this.ppNestLevel_--; + } +}; + +jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) { + for (var property in obj) { + if (property == '__Jasmine_been_here_before__') continue; + fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && + obj.__lookupGetter__(property) !== null) : false); + } +}; + +jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_; +jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_; +jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_; +jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_; + +jasmine.StringPrettyPrinter = function() { + jasmine.PrettyPrinter.call(this); + + this.string = ''; +}; +jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter); + +jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) { + this.append(value); +}; + +jasmine.StringPrettyPrinter.prototype.emitString = function(value) { + this.append("'" + value + "'"); +}; + +jasmine.StringPrettyPrinter.prototype.emitArray = function(array) { + this.append('[ '); + for (var i = 0; i < array.length; i++) { + if (i > 0) { + this.append(', '); + } + this.format(array[i]); + } + this.append(' ]'); +}; + +jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) { + var self = this; + this.append('{ '); + var first = true; + + this.iterateObject(obj, function(property, isGetter) { + if (first) { + first = false; + } else { + self.append(', '); + } + + self.append(property); + self.append(' : '); + if (isGetter) { + self.append(''); + } else { + self.format(obj[property]); + } + }); + + this.append(' }'); +}; + +jasmine.StringPrettyPrinter.prototype.append = function(value) { + this.string += value; +}; +jasmine.Queue = function(env) { + this.env = env; + this.blocks = []; + this.running = false; + this.index = 0; + this.offset = 0; + this.abort = false; +}; + +jasmine.Queue.prototype.addBefore = function(block) { + this.blocks.unshift(block); +}; + +jasmine.Queue.prototype.add = function(block) { + this.blocks.push(block); +}; + +jasmine.Queue.prototype.insertNext = function(block) { + this.blocks.splice((this.index + this.offset + 1), 0, block); + this.offset++; +}; + +jasmine.Queue.prototype.start = function(onComplete) { + this.running = true; + this.onComplete = onComplete; + this.next_(); +}; + +jasmine.Queue.prototype.isRunning = function() { + return this.running; +}; + +jasmine.Queue.LOOP_DONT_RECURSE = true; + +jasmine.Queue.prototype.next_ = function() { + var self = this; + var goAgain = true; + + while (goAgain) { + goAgain = false; + + if (self.index < self.blocks.length && !this.abort) { + var calledSynchronously = true; + var completedSynchronously = false; + + var onComplete = function () { + if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) { + completedSynchronously = true; + return; + } + + if (self.blocks[self.index].abort) { + self.abort = true; + } + + self.offset = 0; + self.index++; + + var now = new Date().getTime(); + if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) { + self.env.lastUpdate = now; + self.env.setTimeout(function() { + self.next_(); + }, 0); + } else { + if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) { + goAgain = true; + } else { + self.next_(); + } + } + }; + self.blocks[self.index].execute(onComplete); + + calledSynchronously = false; + if (completedSynchronously) { + onComplete(); + } + + } else { + self.running = false; + if (self.onComplete) { + self.onComplete(); + } + } + } +}; + +jasmine.Queue.prototype.results = function() { + var results = new jasmine.NestedResults(); + for (var i = 0; i < this.blocks.length; i++) { + if (this.blocks[i].results) { + results.addResult(this.blocks[i].results()); + } + } + return results; +}; + + +/** + * Runner + * + * @constructor + * @param {jasmine.Env} env + */ +jasmine.Runner = function(env) { + var self = this; + self.env = env; + self.queue = new jasmine.Queue(env); + self.before_ = []; + self.after_ = []; + self.suites_ = []; +}; + +jasmine.Runner.prototype.execute = function() { + var self = this; + if (self.env.reporter.reportRunnerStarting) { + self.env.reporter.reportRunnerStarting(this); + } + self.queue.start(function () { + self.finishCallback(); + }); +}; + +jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) { + beforeEachFunction.typeName = 'beforeEach'; + this.before_.splice(0,0,beforeEachFunction); +}; + +jasmine.Runner.prototype.afterEach = function(afterEachFunction) { + afterEachFunction.typeName = 'afterEach'; + this.after_.splice(0,0,afterEachFunction); +}; + + +jasmine.Runner.prototype.finishCallback = function() { + this.env.reporter.reportRunnerResults(this); +}; + +jasmine.Runner.prototype.addSuite = function(suite) { + this.suites_.push(suite); +}; + +jasmine.Runner.prototype.add = function(block) { + if (block instanceof jasmine.Suite) { + this.addSuite(block); + } + this.queue.add(block); +}; + +jasmine.Runner.prototype.specs = function () { + var suites = this.suites(); + var specs = []; + for (var i = 0; i < suites.length; i++) { + specs = specs.concat(suites[i].specs()); + } + return specs; +}; + +jasmine.Runner.prototype.suites = function() { + return this.suites_; +}; + +jasmine.Runner.prototype.topLevelSuites = function() { + var topLevelSuites = []; + for (var i = 0; i < this.suites_.length; i++) { + if (!this.suites_[i].parentSuite) { + topLevelSuites.push(this.suites_[i]); + } + } + return topLevelSuites; +}; + +jasmine.Runner.prototype.results = function() { + return this.queue.results(); +}; +/** + * Internal representation of a Jasmine specification, or test. + * + * @constructor + * @param {jasmine.Env} env + * @param {jasmine.Suite} suite + * @param {String} description + */ +jasmine.Spec = function(env, suite, description) { + if (!env) { + throw new Error('jasmine.Env() required'); + } + if (!suite) { + throw new Error('jasmine.Suite() required'); + } + var spec = this; + spec.id = env.nextSpecId ? env.nextSpecId() : null; + spec.env = env; + spec.suite = suite; + spec.description = description; + spec.queue = new jasmine.Queue(env); + + spec.afterCallbacks = []; + spec.spies_ = []; + + spec.results_ = new jasmine.NestedResults(); + spec.results_.description = description; + spec.matchersClass = null; +}; + +jasmine.Spec.prototype.getFullName = function() { + return this.suite.getFullName() + ' ' + this.description + '.'; +}; + + +jasmine.Spec.prototype.results = function() { + return this.results_; +}; + +/** + * All parameters are pretty-printed and concatenated together, then written to the spec's output. + * + * Be careful not to leave calls to jasmine.log in production code. + */ +jasmine.Spec.prototype.log = function() { + return this.results_.log(arguments); +}; + +jasmine.Spec.prototype.runs = function (func) { + var block = new jasmine.Block(this.env, func, this); + this.addToQueue(block); + return this; +}; + +jasmine.Spec.prototype.addToQueue = function (block) { + if (this.queue.isRunning()) { + this.queue.insertNext(block); + } else { + this.queue.add(block); + } +}; + +/** + * @param {jasmine.ExpectationResult} result + */ +jasmine.Spec.prototype.addMatcherResult = function(result) { + this.results_.addResult(result); +}; + +jasmine.Spec.prototype.expect = function(actual) { + var positive = new (this.getMatchersClass_())(this.env, actual, this); + positive.not = new (this.getMatchersClass_())(this.env, actual, this, true); + return positive; +}; + +/** + * Waits a fixed time period before moving to the next block. + * + * @deprecated Use waitsFor() instead + * @param {Number} timeout milliseconds to wait + */ +jasmine.Spec.prototype.waits = function(timeout) { + var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this); + this.addToQueue(waitsFunc); + return this; +}; + +/** + * Waits for the latchFunction to return true before proceeding to the next block. + * + * @param {Function} latchFunction + * @param {String} optional_timeoutMessage + * @param {Number} optional_timeout + */ +jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { + var latchFunction_ = null; + var optional_timeoutMessage_ = null; + var optional_timeout_ = null; + + for (var i = 0; i < arguments.length; i++) { + var arg = arguments[i]; + switch (typeof arg) { + case 'function': + latchFunction_ = arg; + break; + case 'string': + optional_timeoutMessage_ = arg; + break; + case 'number': + optional_timeout_ = arg; + break; + } + } + + var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this); + this.addToQueue(waitsForFunc); + return this; +}; + +jasmine.Spec.prototype.fail = function (e) { + var expectationResult = new jasmine.ExpectationResult({ + passed: false, + message: e ? jasmine.util.formatException(e) : 'Exception', + trace: { stack: e.stack } + }); + this.results_.addResult(expectationResult); +}; + +jasmine.Spec.prototype.getMatchersClass_ = function() { + return this.matchersClass || this.env.matchersClass; +}; + +jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { + var parent = this.getMatchersClass_(); + var newMatchersClass = function() { + parent.apply(this, arguments); + }; + jasmine.util.inherit(newMatchersClass, parent); + jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass); + this.matchersClass = newMatchersClass; +}; + +jasmine.Spec.prototype.finishCallback = function() { + this.env.reporter.reportSpecResults(this); +}; + +jasmine.Spec.prototype.finish = function(onComplete) { + this.removeAllSpies(); + this.finishCallback(); + if (onComplete) { + onComplete(); + } +}; + +jasmine.Spec.prototype.after = function(doAfter) { + if (this.queue.isRunning()) { + this.queue.add(new jasmine.Block(this.env, doAfter, this)); + } else { + this.afterCallbacks.unshift(doAfter); + } +}; + +jasmine.Spec.prototype.execute = function(onComplete) { + var spec = this; + if (!spec.env.specFilter(spec)) { + spec.results_.skipped = true; + spec.finish(onComplete); + return; + } + + this.env.reporter.reportSpecStarting(this); + + spec.env.currentSpec = spec; + + spec.addBeforesAndAftersToQueue(); + + spec.queue.start(function () { + spec.finish(onComplete); + }); +}; + +jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() { + var runner = this.env.currentRunner(); + var i; + + for (var suite = this.suite; suite; suite = suite.parentSuite) { + for (i = 0; i < suite.before_.length; i++) { + this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this)); + } + } + for (i = 0; i < runner.before_.length; i++) { + this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this)); + } + for (i = 0; i < this.afterCallbacks.length; i++) { + this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this)); + } + for (suite = this.suite; suite; suite = suite.parentSuite) { + for (i = 0; i < suite.after_.length; i++) { + this.queue.add(new jasmine.Block(this.env, suite.after_[i], this)); + } + } + for (i = 0; i < runner.after_.length; i++) { + this.queue.add(new jasmine.Block(this.env, runner.after_[i], this)); + } +}; + +jasmine.Spec.prototype.explodes = function() { + throw 'explodes function should not have been called'; +}; + +jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) { + if (obj == jasmine.undefined) { + throw "spyOn could not find an object to spy upon for " + methodName + "()"; + } + + if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) { + throw methodName + '() method does not exist'; + } + + if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) { + throw new Error(methodName + ' has already been spied upon'); + } + + var spyObj = jasmine.createSpy(methodName); + + this.spies_.push(spyObj); + spyObj.baseObj = obj; + spyObj.methodName = methodName; + spyObj.originalValue = obj[methodName]; + + obj[methodName] = spyObj; + + return spyObj; +}; + +jasmine.Spec.prototype.removeAllSpies = function() { + for (var i = 0; i < this.spies_.length; i++) { + var spy = this.spies_[i]; + spy.baseObj[spy.methodName] = spy.originalValue; + } + this.spies_ = []; +}; + +/** + * Internal representation of a Jasmine suite. + * + * @constructor + * @param {jasmine.Env} env + * @param {String} description + * @param {Function} specDefinitions + * @param {jasmine.Suite} parentSuite + */ +jasmine.Suite = function(env, description, specDefinitions, parentSuite) { + var self = this; + self.id = env.nextSuiteId ? env.nextSuiteId() : null; + self.description = description; + self.queue = new jasmine.Queue(env); + self.parentSuite = parentSuite; + self.env = env; + self.before_ = []; + self.after_ = []; + self.children_ = []; + self.suites_ = []; + self.specs_ = []; +}; + +jasmine.Suite.prototype.getFullName = function() { + var fullName = this.description; + for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { + fullName = parentSuite.description + ' ' + fullName; + } + return fullName; +}; + +jasmine.Suite.prototype.finish = function(onComplete) { + this.env.reporter.reportSuiteResults(this); + this.finished = true; + if (typeof(onComplete) == 'function') { + onComplete(); + } +}; + +jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) { + beforeEachFunction.typeName = 'beforeEach'; + this.before_.unshift(beforeEachFunction); +}; + +jasmine.Suite.prototype.afterEach = function(afterEachFunction) { + afterEachFunction.typeName = 'afterEach'; + this.after_.unshift(afterEachFunction); +}; + +jasmine.Suite.prototype.results = function() { + return this.queue.results(); +}; + +jasmine.Suite.prototype.add = function(suiteOrSpec) { + this.children_.push(suiteOrSpec); + if (suiteOrSpec instanceof jasmine.Suite) { + this.suites_.push(suiteOrSpec); + this.env.currentRunner().addSuite(suiteOrSpec); + } else { + this.specs_.push(suiteOrSpec); + } + this.queue.add(suiteOrSpec); +}; + +jasmine.Suite.prototype.specs = function() { + return this.specs_; +}; + +jasmine.Suite.prototype.suites = function() { + return this.suites_; +}; + +jasmine.Suite.prototype.children = function() { + return this.children_; +}; + +jasmine.Suite.prototype.execute = function(onComplete) { + var self = this; + this.queue.start(function () { + self.finish(onComplete); + }); +}; +jasmine.WaitsBlock = function(env, timeout, spec) { + this.timeout = timeout; + jasmine.Block.call(this, env, null, spec); +}; + +jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block); + +jasmine.WaitsBlock.prototype.execute = function (onComplete) { + if (jasmine.VERBOSE) { + this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...'); + } + this.env.setTimeout(function () { + onComplete(); + }, this.timeout); +}; +/** + * A block which waits for some condition to become true, with timeout. + * + * @constructor + * @extends jasmine.Block + * @param {jasmine.Env} env The Jasmine environment. + * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true. + * @param {Function} latchFunction A function which returns true when the desired condition has been met. + * @param {String} message The message to display if the desired condition hasn't been met within the given time period. + * @param {jasmine.Spec} spec The Jasmine spec. + */ +jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) { + this.timeout = timeout || env.defaultTimeoutInterval; + this.latchFunction = latchFunction; + this.message = message; + this.totalTimeSpentWaitingForLatch = 0; + jasmine.Block.call(this, env, null, spec); +}; +jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block); + +jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10; + +jasmine.WaitsForBlock.prototype.execute = function(onComplete) { + if (jasmine.VERBOSE) { + this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen')); + } + var latchFunctionResult; + try { + latchFunctionResult = this.latchFunction.apply(this.spec); + } catch (e) { + this.spec.fail(e); + onComplete(); + return; + } + + if (latchFunctionResult) { + onComplete(); + } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) { + var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen'); + this.spec.fail({ + name: 'timeout', + message: message + }); + + this.abort = true; + onComplete(); + } else { + this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT; + var self = this; + this.env.setTimeout(function() { + self.execute(onComplete); + }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT); + } +}; + +jasmine.version_= { + "major": 1, + "minor": 2, + "build": 0, + "revision": 1333310630, + "release_candidate": 1 +}; diff --git a/plugins/org.apache.cordova.device/test/autotest/pages/device.html b/plugins/org.apache.cordova.device/test/autotest/pages/device.html new file mode 100644 index 0000000..7252e67 --- /dev/null +++ b/plugins/org.apache.cordova.device/test/autotest/pages/device.html @@ -0,0 +1,71 @@ + + + + + + + + Cordova: Device API Specs + + + + + + + + + + + + + + + + + + + + + + + + + + Back + + + diff --git a/plugins/org.apache.cordova.device/test/autotest/test-runner.js b/plugins/org.apache.cordova.device/test/autotest/test-runner.js new file mode 100644 index 0000000..f72b3cc --- /dev/null +++ b/plugins/org.apache.cordova.device/test/autotest/test-runner.js @@ -0,0 +1,62 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + +if (window.sessionStorage != null) { + window.sessionStorage.clear(); +} + +// Timeout is 2 seconds to allow physical devices enough +// time to query the response. This is important for some +// Android devices. +var Tests = function() {}; +Tests.TEST_TIMEOUT = 7500; + +// Creates a spy that will fail if called. +function createDoNotCallSpy(name, opt_extraMessage) { + return jasmine.createSpy().andCallFake(function() { + var errorMessage = name + ' should not have been called.'; + if (arguments.length) { + errorMessage += ' Got args: ' + JSON.stringify(arguments); + } + if (opt_extraMessage) { + errorMessage += '\n' + opt_extraMessage; + } + expect(false).toBe(true, errorMessage); + }); +} + +// Waits for any of the given spys to be called. +// Last param may be a custom timeout duration. +function waitsForAny() { + var spys = [].slice.call(arguments); + var timeout = Tests.TEST_TIMEOUT; + if (typeof spys[spys.length - 1] == 'number') { + timeout = spys.pop(); + } + waitsFor(function() { + for (var i = 0; i < spys.length; ++i) { + if (spys[i].wasCalled) { + return true; + } + } + return false; + }, "Expecting callbacks to be called.", timeout); +} diff --git a/plugins/org.apache.cordova.device/test/autotest/tests/device.tests.js b/plugins/org.apache.cordova.device/test/autotest/tests/device.tests.js new file mode 100644 index 0000000..0bcd0d9 --- /dev/null +++ b/plugins/org.apache.cordova.device/test/autotest/tests/device.tests.js @@ -0,0 +1,64 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + +describe('Device Information (window.device)', function () { + it("should exist", function() { + expect(window.device).toBeDefined(); + }); + + it("should contain a platform specification that is a string", function() { + expect(window.device.platform).toBeDefined(); + expect((new String(window.device.platform)).length > 0).toBe(true); + }); + + it("should contain a version specification that is a string", function() { + expect(window.device.version).toBeDefined(); + expect((new String(window.device.version)).length > 0).toBe(true); + }); + + it("should contain a UUID specification that is a string or a number", function() { + expect(window.device.uuid).toBeDefined(); + if (typeof window.device.uuid == 'string' || typeof window.device.uuid == 'object') { + expect((new String(window.device.uuid)).length > 0).toBe(true); + } else { + expect(window.device.uuid > 0).toBe(true); + } + }); + + it("should contain a cordova specification that is a string", function() { + expect(window.device.cordova).toBeDefined(); + expect((new String(window.device.cordova)).length > 0).toBe(true); + }); + + it("should depend on the precense of cordova.version string", function() { + expect(window.cordova.version).toBeDefined(); + expect((new String(window.cordova.version)).length > 0).toBe(true); + }); + + it("should contain device.cordova equal to cordova.version", function() { + expect(window.device.cordova).toBe(window.cordova.version); + }); + + it("should contain a model specification that is a string", function() { + expect(window.device.model).toBeDefined(); + expect((new String(window.device.model)).length > 0).toBe(true); + }); +}); diff --git a/plugins/org.apache.cordova.device/test/cordova-incl.js b/plugins/org.apache.cordova.device/test/cordova-incl.js new file mode 100644 index 0000000..a82c590 --- /dev/null +++ b/plugins/org.apache.cordova.device/test/cordova-incl.js @@ -0,0 +1,85 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + +var PLAT; +(function getPlatform() { + var platforms = { + android: /Android/, + ios: /(iPad)|(iPhone)|(iPod)/, + blackberry10: /(BB10)/, + blackberry: /(PlayBook)|(BlackBerry)/, + windows8: /MSAppHost/, + windowsphone: /Windows Phone/ + }; + for (var key in platforms) { + if (platforms[key].exec(navigator.userAgent)) { + PLAT = key; + break; + } + } +})(); + +var scripts = document.getElementsByTagName('script'); +var currentPath = scripts[scripts.length - 1].src; +var platformCordovaPath = currentPath.replace("cordova-incl.js", "cordova." + PLAT + ".js"); +var normalCordovaPath = currentPath.replace("cordova-incl.js", "cordova.js"); +var cordovaPath = normalCordovaPath; + +if (PLAT) { + // XHR to local file is an error on some platforms, windowsphone for one + try { + var xhr = new XMLHttpRequest(); + xhr.open("GET", platformCordovaPath, false); + xhr.onreadystatechange = function() { + + if (this.readyState == this.DONE && this.responseText.length > 0) { + if(parseInt(this.status) >= 400){ + cordovaPath = normalCordovaPath; + }else{ + cordovaPath = platformCordovaPath; + } + } + }; + xhr.send(null); + } + catch(e){ + cordovaPath = normalCordovaPath; + } // access denied! +} + +if (!window._doNotWriteCordovaScript) { + if (PLAT != "windows8") { + document.write(''); + } else { + var s = document.createElement('script'); + s.src = cordovaPath; + document.head.appendChild(s); + } +} + +function backHome() { + if (window.device && device.platform && device.platform.toLowerCase() == 'android') { + navigator.app.backHistory(); + } + else { + window.history.go(-1); + } +} diff --git a/plugins/org.apache.cordova.device/test/index.html b/plugins/org.apache.cordova.device/test/index.html new file mode 100644 index 0000000..82f6d3b --- /dev/null +++ b/plugins/org.apache.cordova.device/test/index.html @@ -0,0 +1,66 @@ + + + + + + + + + Cordova Mobile Spec + + + + + + +

Apache Cordova Tests

+
+

cordova.version:

+

Platform:

+

Version:

+

UUID:

+

Model:

+

Width: , Height: + , Color Depth:

+

User-Agent:

+
+ Automatic Test + Accelerometer + Audio Play/Record + Battery + Camera + Capture + Compass + Contacts + Events + Location + Lazy Loading of cordova-incl.js + Misc Content + Network + Notification + Splashscreen + Web SQL + Local Storage + Benchmarks + In App Browser + + diff --git a/plugins/org.apache.cordova.device/test/main.js b/plugins/org.apache.cordova.device/test/main.js new file mode 100644 index 0000000..9508770 --- /dev/null +++ b/plugins/org.apache.cordova.device/test/main.js @@ -0,0 +1,163 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + +var deviceInfo = function() { + document.getElementById("cordova").innerHTML = cordova.version; + document.getElementById("platform").innerHTML = device.platform; + document.getElementById("version").innerHTML = device.version; + document.getElementById("uuid").innerHTML = device.uuid; + document.getElementById("model").innerHTML = device.model; + document.getElementById("width").innerHTML = screen.width; + document.getElementById("height").innerHTML = screen.height; + document.getElementById("colorDepth").innerHTML = screen.colorDepth; +}; + +var getLocation = function() { + var suc = function(p) { + alert(p.coords.latitude + " " + p.coords.longitude); + }; + var locFail = function() { + }; + navigator.geolocation.getCurrentPosition(suc, locFail); +}; + +var beep = function() { + navigator.notification.beep(2); +}; + +var vibrate = function() { + navigator.notification.vibrate(0); +}; + +function roundNumber(num) { + var dec = 3; + var result = Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec); + return result; +} + +var accelerationWatch = null; + +function updateAcceleration(a) { + document.getElementById('x').innerHTML = roundNumber(a.x); + document.getElementById('y').innerHTML = roundNumber(a.y); + document.getElementById('z').innerHTML = roundNumber(a.z); +} + +var toggleAccel = function() { + if (accelerationWatch !== null) { + navigator.accelerometer.clearWatch(accelerationWatch); + updateAcceleration({ + x : "", + y : "", + z : "" + }); + accelerationWatch = null; + } else { + var options = {}; + options.frequency = 1000; + accelerationWatch = navigator.accelerometer.watchAcceleration( + updateAcceleration, function(ex) { + alert("accel fail (" + ex.name + ": " + ex.message + ")"); + }, options); + } +}; + +var preventBehavior = function(e) { + e.preventDefault(); +}; + +function dump_pic(data) { + var viewport = document.getElementById('viewport'); + console.log(data); + viewport.style.display = ""; + viewport.style.position = "absolute"; + viewport.style.top = "10px"; + viewport.style.left = "10px"; + document.getElementById("test_img").src = "data:image/jpeg;base64," + data; +} + +function fail(msg) { + alert(msg); +} + +function show_pic() { + navigator.camera.getPicture(dump_pic, fail, { + quality : 50 + }); +} + +function close() { + var viewport = document.getElementById('viewport'); + viewport.style.position = "relative"; + viewport.style.display = "none"; +} + +// This is just to do this. +function readFile() { + navigator.file.read('/sdcard/cordova.txt', fail, fail); +} + +function writeFile() { + navigator.file.write('foo.txt', "This is a test of writing to a file", + fail, fail); +} + +function contacts_success(contacts) { + alert(contacts.length + + ' contacts returned.' + + (contacts[2] && contacts[2].name ? (' Third contact is ' + contacts[2].name.formatted) + : '')); +} + +function get_contacts() { + var obj = new ContactFindOptions(); + obj.filter = ""; + obj.multiple = true; + obj.limit = 5; + navigator.service.contacts.find( + [ "displayName", "name" ], contacts_success, + fail, obj); +} + +var networkReachableCallback = function(reachability) { + // There is no consistency on the format of reachability + var networkState = reachability.code || reachability; + + var currentState = {}; + currentState[NetworkStatus.NOT_REACHABLE] = 'No network connection'; + currentState[NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK] = 'Carrier data connection'; + currentState[NetworkStatus.REACHABLE_VIA_WIFI_NETWORK] = 'WiFi connection'; + + confirm("Connection type:\n" + currentState[networkState]); +}; + +function check_network() { + navigator.network.isReachable("www.mobiledevelopersolutions.com", + networkReachableCallback, {}); +} + +function init() { + // the next line makes it impossible to see Contacts on the HTC Evo since it + // doesn't have a scroll button + // document.addEventListener("touchmove", preventBehavior, false); + document.addEventListener("deviceready", deviceInfo, true); + document.getElementById("user-agent").textContent = navigator.userAgent; +} diff --git a/plugins/org.apache.cordova.device/test/master.css b/plugins/org.apache.cordova.device/test/master.css new file mode 100644 index 0000000..e93c937 --- /dev/null +++ b/plugins/org.apache.cordova.device/test/master.css @@ -0,0 +1,164 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + + body { + background:#222 none repeat scroll 0 0; + color:#666; + font-family:Helvetica; + font-size:72%; + line-height:1.5em; + margin:0; + border-top:1px solid #393939; + } + + #info{ + background:#ffa; + border: 1px solid #ffd324; + -webkit-border-radius: 5px; + border-radius: 5px; + clear:both; + margin:15px 6px 0; + min-width:295px; + max-width:97%; + padding:4px 0px 2px 10px; + word-wrap:break-word; + margin-bottom:10px; + display:inline-block; + min-height: 160px; + max-height: 300px; + overflow: auto; + -webkit-overflow-scrolling: touch; + } + + #info > h4{ + font-size:.95em; + margin:5px 0; + } + + #stage.theme{ + padding-top:3px; + } + + /* Definition List */ + #stage.theme > dl{ + padding-top:10px; + clear:both; + margin:0; + list-style-type:none; + padding-left:10px; + overflow:auto; + } + + #stage.theme > dl > dt{ + font-weight:bold; + float:left; + margin-left:5px; + } + + #stage.theme > dl > dd{ + width:45px; + float:left; + color:#a87; + font-weight:bold; + } + + /* Content Styling */ + #stage.theme > h1, #stage.theme > h2, #stage.theme > p{ + margin:1em 0 .5em 13px; + } + + #stage.theme > h1{ + color:#eee; + font-size:1.6em; + text-align:center; + margin:0; + margin-top:15px; + padding:0; + } + + #stage.theme > h2{ + clear:both; + margin:0; + padding:3px; + font-size:1em; + text-align:center; + } + + /* Stage Buttons */ + #stage.theme .btn{ + border: 1px solid #555; + -webkit-border-radius: 5px; + border-radius: 5px; + text-align:center; + display:inline-block; + background:#444; + width:150px; + color:#9ab; + font-size:1.1em; + text-decoration:none; + padding:1.2em 0; + margin:3px 0px 3px 5px; + } + + #stage.theme .large{ + width:308px; + padding:1.2em 0; + } + + #stage.theme .wide{ + width:100%; + padding:1.2em 0; + } + + #stage.theme .backBtn{ + border: 1px solid #555; + -webkit-border-radius: 5px; + border-radius: 5px; + text-align:center; + display:block; + float:right; + background:#666; + width:75px; + color:#9ab; + font-size:1.1em; + text-decoration:none; + padding:1.2em 0; + margin:3px 5px 3px 5px; + } + + #stage.theme .input{ + border: 1px solid #555; + -webkit-border-radius: 5px; + border-radius: 5px; + text-align:center; + display:block; + float:light; + background:#888; + color:#9cd; + font-size:1.1em; + text-decoration:none; + padding:1.2em 0; + margin:3px 0px 3px 5px; + } + + #stage.theme .numeric{ + width:100%; + } diff --git a/plugins/org.apache.cordova.device/www/device.js b/plugins/org.apache.cordova.device/www/device.js new file mode 100644 index 0000000..05f4c47 --- /dev/null +++ b/plugins/org.apache.cordova.device/www/device.js @@ -0,0 +1,77 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + +var argscheck = require('cordova/argscheck'), + channel = require('cordova/channel'), + utils = require('cordova/utils'), + exec = require('cordova/exec'), + cordova = require('cordova'); + +channel.createSticky('onCordovaInfoReady'); +// Tell cordova channel to wait on the CordovaInfoReady event +channel.waitForInitialization('onCordovaInfoReady'); + +/** + * This represents the mobile device, and provides properties for inspecting the model, version, UUID of the + * phone, etc. + * @constructor + */ +function Device() { + this.available = false; + this.platform = null; + this.version = null; + this.uuid = null; + this.cordova = null; + this.model = null; + + var me = this; + + channel.onCordovaReady.subscribe(function() { + me.getInfo(function(info) { + //ignoring info.cordova returning from native, we should use value from cordova.version defined in cordova.js + //TODO: CB-5105 native implementations should not return info.cordova + var buildLabel = cordova.version; + me.available = true; + me.platform = info.platform; + me.version = info.version; + me.uuid = info.uuid; + me.cordova = buildLabel; + me.model = info.model; + channel.onCordovaInfoReady.fire(); + },function(e) { + me.available = false; + utils.alert("[ERROR] Error initializing Cordova: " + e); + }); + }); +} + +/** + * Get device info + * + * @param {Function} successCallback The function to call when the heading data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL) + */ +Device.prototype.getInfo = function(successCallback, errorCallback) { + argscheck.checkArgs('fF', 'Device.getInfo', arguments); + exec(successCallback, errorCallback, "Device", "getDeviceInfo", []); +}; + +module.exports = new Device(); diff --git a/server/createDataCsv.sh b/server/createDataCsv.sh new file mode 100644 index 0000000..ea299e6 --- /dev/null +++ b/server/createDataCsv.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# Assumes all JSON files have the same structure. +# TODO: Current akw splitting doesn'properly handle double quotes in data resulting in data truncation. + +OutFile="output.csv" + +rm "$OutFile" + +filename=$(ls *.JSON | head -n1) + +grep ":" "$filename" | awk -F "\"" '{ print $2; }' | while read field +do + echo -e -n "\"${field/\\\"/\"\"}\""";" >> "$OutFile" +done +echo >> "$OutFile" + + +ls *.JSON | while read filename +do echo processing $filename + + grep ":" "$filename" | awk -F "\"" '{ print $2; }' | while read field + do + fieldData=$(grep "\"$field\":" "$filename" | awk -F '"' '{ print $4; }') + + echo -e -n "\"${fieldData/\\\"/\"\"}\""";" >> "$OutFile" + done + echo >> "$OutFile" +done + diff --git a/server/extractPic.sh b/server/extractPic.sh new file mode 100644 index 0000000..983c657 --- /dev/null +++ b/server/extractPic.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Param1 -> name of the JSON-tag +# Param2 -> name of the input file +# Param3 -> name of the output file + +grep "\"$1\":" "$2" | awk -F '"' '{ print $4; }' | base64 -d > $3 diff --git a/server/node_modules/.bin/express b/server/node_modules/.bin/express new file mode 100644 index 0000000..833f053 --- /dev/null +++ b/server/node_modules/.bin/express @@ -0,0 +1 @@ +link ../express/bin/express \ No newline at end of file diff --git a/server/node_modules/express/.npmignore b/server/node_modules/express/.npmignore new file mode 100644 index 0000000..caf574d --- /dev/null +++ b/server/node_modules/express/.npmignore @@ -0,0 +1,9 @@ +.git* +docs/ +examples/ +support/ +test/ +testing.js +.DS_Store +coverage.html +lib-cov diff --git a/server/node_modules/express/.travis.yml b/server/node_modules/express/.travis.yml new file mode 100644 index 0000000..cc4dba2 --- /dev/null +++ b/server/node_modules/express/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - "0.8" + - "0.10" diff --git a/server/node_modules/express/History.md b/server/node_modules/express/History.md new file mode 100644 index 0000000..d784c26 --- /dev/null +++ b/server/node_modules/express/History.md @@ -0,0 +1,1233 @@ +3.4.4 / 2013-10-29 +================== + + * update connect + * update supertest + * update methods + * express(1): replace bodyParser() with urlencoded() and json() #1795 @chirag04 + +3.4.3 / 2013-10-23 +================== + + * update connect + +3.4.2 / 2013-10-18 +================== + + * update connect + * downgrade commander + +3.4.1 / 2013-10-15 +================== + + * update connect + * update commander + * jsonp: check if callback is a function + * router: wrap encodeURIComponent in a try/catch #1735 (@lxe) + * res.format: now includes chraset @1747 (@sorribas) + * res.links: allow multiple calls @1746 (@sorribas) + +3.4.0 / 2013-09-07 +================== + + * add res.vary(). Closes #1682 + * update connect + +3.3.8 / 2013-09-02 +================== + + * update connect + +3.3.7 / 2013-08-28 +================== + + * update connect + +3.3.6 / 2013-08-27 +================== + + * Revert "remove charset from json responses. Closes #1631" (causes issues in some clients) + * add: req.accepts take an argument list + +3.3.4 / 2013-07-08 +================== + + * update send and connect + +3.3.3 / 2013-07-04 +================== + + * update connect + +3.3.2 / 2013-07-03 +================== + + * update connect + * update send + * remove .version export + +3.3.1 / 2013-06-27 +================== + + * update connect + +3.3.0 / 2013-06-26 +================== + + * update connect + * add support for multiple X-Forwarded-Proto values. Closes #1646 + * change: remove charset from json responses. Closes #1631 + * change: return actual booleans from req.accept* functions + * fix jsonp callback array throw + +3.2.6 / 2013-06-02 +================== + + * update connect + +3.2.5 / 2013-05-21 +================== + + * update connect + * update node-cookie + * add: throw a meaningful error when there is no default engine + * change generation of ETags with res.send() to GET requests only. Closes #1619 + +3.2.4 / 2013-05-09 +================== + + * fix `req.subdomains` when no Host is present + * fix `req.host` when no Host is present, return undefined + +3.2.3 / 2013-05-07 +================== + + * update connect / qs + +3.2.2 / 2013-05-03 +================== + + * update qs + +3.2.1 / 2013-04-29 +================== + + * add app.VERB() paths array deprecation warning + * update connect + * update qs and remove all ~ semver crap + * fix: accept number as value of Signed Cookie + +3.2.0 / 2013-04-15 +================== + + * add "view" constructor setting to override view behaviour + * add req.acceptsEncoding(name) + * add req.acceptedEncodings + * revert cookie signature change causing session race conditions + * fix sorting of Accept values of the same quality + +3.1.2 / 2013-04-12 +================== + + * add support for custom Accept parameters + * update cookie-signature + +3.1.1 / 2013-04-01 +================== + + * add X-Forwarded-Host support to `req.host` + * fix relative redirects + * update mkdirp + * update buffer-crc32 + * remove legacy app.configure() method from app template. + +3.1.0 / 2013-01-25 +================== + + * add support for leading "." in "view engine" setting + * add array support to `res.set()` + * add node 0.8.x to travis.yml + * add "subdomain offset" setting for tweaking `req.subdomains` + * add `res.location(url)` implementing `res.redirect()`-like setting of Location + * use app.get() for x-powered-by setting for inheritance + * fix colons in passwords for `req.auth` + +3.0.6 / 2013-01-04 +================== + + * add http verb methods to Router + * update connect + * fix mangling of the `res.cookie()` options object + * fix jsonp whitespace escape. Closes #1132 + +3.0.5 / 2012-12-19 +================== + + * add throwing when a non-function is passed to a route + * fix: explicitly remove Transfer-Encoding header from 204 and 304 responses + * revert "add 'etag' option" + +3.0.4 / 2012-12-05 +================== + + * add 'etag' option to disable `res.send()` Etags + * add escaping of urls in text/plain in `res.redirect()` + for old browsers interpreting as html + * change crc32 module for a more liberal license + * update connect + +3.0.3 / 2012-11-13 +================== + + * update connect + * update cookie module + * fix cookie max-age + +3.0.2 / 2012-11-08 +================== + + * add OPTIONS to cors example. Closes #1398 + * fix route chaining regression. Closes #1397 + +3.0.1 / 2012-11-01 +================== + + * update connect + +3.0.0 / 2012-10-23 +================== + + * add `make clean` + * add "Basic" check to req.auth + * add `req.auth` test coverage + * add cb && cb(payload) to `res.jsonp()`. Closes #1374 + * add backwards compat for `res.redirect()` status. Closes #1336 + * add support for `res.json()` to retain previously defined Content-Types. Closes #1349 + * update connect + * change `res.redirect()` to utilize a pathname-relative Location again. Closes #1382 + * remove non-primitive string support for `res.send()` + * fix view-locals example. Closes #1370 + * fix route-separation example + +3.0.0rc5 / 2012-09-18 +================== + + * update connect + * add redis search example + * add static-files example + * add "x-powered-by" setting (`app.disable('x-powered-by')`) + * add "application/octet-stream" redirect Accept test case. Closes #1317 + +3.0.0rc4 / 2012-08-30 +================== + + * add `res.jsonp()`. Closes #1307 + * add "verbose errors" option to error-pages example + * add another route example to express(1) so people are not so confused + * add redis online user activity tracking example + * update connect dep + * fix etag quoting. Closes #1310 + * fix error-pages 404 status + * fix jsonp callback char restrictions + * remove old OPTIONS default response + +3.0.0rc3 / 2012-08-13 +================== + + * update connect dep + * fix signed cookies to work with `connect.cookieParser()` ("s:" prefix was missing) [tnydwrds] + * fix `res.render()` clobbering of "locals" + +3.0.0rc2 / 2012-08-03 +================== + + * add CORS example + * update connect dep + * deprecate `.createServer()` & remove old stale examples + * fix: escape `res.redirect()` link + * fix vhost example + +3.0.0rc1 / 2012-07-24 +================== + + * add more examples to view-locals + * add scheme-relative redirects (`res.redirect("//foo.com")`) support + * update cookie dep + * update connect dep + * update send dep + * fix `express(1)` -h flag, use -H for hogan. Closes #1245 + * fix `res.sendfile()` socket error handling regression + +3.0.0beta7 / 2012-07-16 +================== + + * update connect dep for `send()` root normalization regression + +3.0.0beta6 / 2012-07-13 +================== + + * add `err.view` property for view errors. Closes #1226 + * add "jsonp callback name" setting + * add support for "/foo/:bar*" non-greedy matches + * change `res.sendfile()` to use `send()` module + * change `res.send` to use "response-send" module + * remove `app.locals.use` and `res.locals.use`, use regular middleware + +3.0.0beta5 / 2012-07-03 +================== + + * add "make check" support + * add route-map example + * add `res.json(obj, status)` support back for BC + * add "methods" dep, remove internal methods module + * update connect dep + * update auth example to utilize cores pbkdf2 + * updated tests to use "supertest" + +3.0.0beta4 / 2012-06-25 +================== + + * Added `req.auth` + * Added `req.range(size)` + * Added `res.links(obj)` + * Added `res.send(body, status)` support back for backwards compat + * Added `.default()` support to `res.format()` + * Added 2xx / 304 check to `req.fresh` + * Revert "Added + support to the router" + * Fixed `res.send()` freshness check, respect res.statusCode + +3.0.0beta3 / 2012-06-15 +================== + + * Added hogan `--hjs` to express(1) [nullfirm] + * Added another example to content-negotiation + * Added `fresh` dep + * Changed: `res.send()` always checks freshness + * Fixed: expose connects mime module. Cloases #1165 + +3.0.0beta2 / 2012-06-06 +================== + + * Added `+` support to the router + * Added `req.host` + * Changed `req.param()` to check route first + * Update connect dep + +3.0.0beta1 / 2012-06-01 +================== + + * Added `res.format()` callback to override default 406 behaviour + * Fixed `res.redirect()` 406. Closes #1154 + +3.0.0alpha5 / 2012-05-30 +================== + + * Added `req.ip` + * Added `{ signed: true }` option to `res.cookie()` + * Removed `res.signedCookie()` + * Changed: dont reverse `req.ips` + * Fixed "trust proxy" setting check for `req.ips` + +3.0.0alpha4 / 2012-05-09 +================== + + * Added: allow `[]` in jsonp callback. Closes #1128 + * Added `PORT` env var support in generated template. Closes #1118 [benatkin] + * Updated: connect 2.2.2 + +3.0.0alpha3 / 2012-05-04 +================== + + * Added public `app.routes`. Closes #887 + * Added _view-locals_ example + * Added _mvc_ example + * Added `res.locals.use()`. Closes #1120 + * Added conditional-GET support to `res.send()` + * Added: coerce `res.set()` values to strings + * Changed: moved `static()` in generated apps below router + * Changed: `res.send()` only set ETag when not previously set + * Changed connect 2.2.1 dep + * Changed: `make test` now runs unit / acceptance tests + * Fixed req/res proto inheritance + +3.0.0alpha2 / 2012-04-26 +================== + + * Added `make benchmark` back + * Added `res.send()` support for `String` objects + * Added client-side data exposing example + * Added `res.header()` and `req.header()` aliases for BC + * Added `express.createServer()` for BC + * Perf: memoize parsed urls + * Perf: connect 2.2.0 dep + * Changed: make `expressInit()` middleware self-aware + * Fixed: use app.get() for all core settings + * Fixed redis session example + * Fixed session example. Closes #1105 + * Fixed generated express dep. Closes #1078 + +3.0.0alpha1 / 2012-04-15 +================== + + * Added `app.locals.use(callback)` + * Added `app.locals` object + * Added `app.locals(obj)` + * Added `res.locals` object + * Added `res.locals(obj)` + * Added `res.format()` for content-negotiation + * Added `app.engine()` + * Added `res.cookie()` JSON cookie support + * Added "trust proxy" setting + * Added `req.subdomains` + * Added `req.protocol` + * Added `req.secure` + * Added `req.path` + * Added `req.ips` + * Added `req.fresh` + * Added `req.stale` + * Added comma-delmited / array support for `req.accepts()` + * Added debug instrumentation + * Added `res.set(obj)` + * Added `res.set(field, value)` + * Added `res.get(field)` + * Added `app.get(setting)`. Closes #842 + * Added `req.acceptsLanguage()` + * Added `req.acceptsCharset()` + * Added `req.accepted` + * Added `req.acceptedLanguages` + * Added `req.acceptedCharsets` + * Added "json replacer" setting + * Added "json spaces" setting + * Added X-Forwarded-Proto support to `res.redirect()`. Closes #92 + * Added `--less` support to express(1) + * Added `express.response` prototype + * Added `express.request` prototype + * Added `express.application` prototype + * Added `app.path()` + * Added `app.render()` + * Added `res.type()` to replace `res.contentType()` + * Changed: `res.redirect()` to add relative support + * Changed: enable "jsonp callback" by default + * Changed: renamed "case sensitive routes" to "case sensitive routing" + * Rewrite of all tests with mocha + * Removed "root" setting + * Removed `res.redirect('home')` support + * Removed `req.notify()` + * Removed `app.register()` + * Removed `app.redirect()` + * Removed `app.is()` + * Removed `app.helpers()` + * Removed `app.dynamicHelpers()` + * Fixed `res.sendfile()` with non-GET. Closes #723 + * Fixed express(1) public dir for windows. Closes #866 + +2.5.9/ 2012-04-02 +================== + + * Added support for PURGE request method [pbuyle] + * Fixed `express(1)` generated app `app.address()` before `listening` [mmalecki] + +2.5.8 / 2012-02-08 +================== + + * Update mkdirp dep. Closes #991 + +2.5.7 / 2012-02-06 +================== + + * Fixed `app.all` duplicate DELETE requests [mscdex] + +2.5.6 / 2012-01-13 +================== + + * Updated hamljs dev dep. Closes #953 + +2.5.5 / 2012-01-08 +================== + + * Fixed: set `filename` on cached templates [matthewleon] + +2.5.4 / 2012-01-02 +================== + + * Fixed `express(1)` eol on 0.4.x. Closes #947 + +2.5.3 / 2011-12-30 +================== + + * Fixed `req.is()` when a charset is present + +2.5.2 / 2011-12-10 +================== + + * Fixed: express(1) LF -> CRLF for windows + +2.5.1 / 2011-11-17 +================== + + * Changed: updated connect to 1.8.x + * Removed sass.js support from express(1) + +2.5.0 / 2011-10-24 +================== + + * Added ./routes dir for generated app by default + * Added npm install reminder to express(1) app gen + * Added 0.5.x support + * Removed `make test-cov` since it wont work with node 0.5.x + * Fixed express(1) public dir for windows. Closes #866 + +2.4.7 / 2011-10-05 +================== + + * Added mkdirp to express(1). Closes #795 + * Added simple _json-config_ example + * Added shorthand for the parsed request's pathname via `req.path` + * Changed connect dep to 1.7.x to fix npm issue... + * Fixed `res.redirect()` __HEAD__ support. [reported by xerox] + * Fixed `req.flash()`, only escape args + * Fixed absolute path checking on windows. Closes #829 [reported by andrewpmckenzie] + +2.4.6 / 2011-08-22 +================== + + * Fixed multiple param callback regression. Closes #824 [reported by TroyGoode] + +2.4.5 / 2011-08-19 +================== + + * Added support for routes to handle errors. Closes #809 + * Added `app.routes.all()`. Closes #803 + * Added "basepath" setting to work in conjunction with reverse proxies etc. + * Refactored `Route` to use a single array of callbacks + * Added support for multiple callbacks for `app.param()`. Closes #801 +Closes #805 + * Changed: removed .call(self) for route callbacks + * Dependency: `qs >= 0.3.1` + * Fixed `res.redirect()` on windows due to `join()` usage. Closes #808 + +2.4.4 / 2011-08-05 +================== + + * Fixed `res.header()` intention of a set, even when `undefined` + * Fixed `*`, value no longer required + * Fixed `res.send(204)` support. Closes #771 + +2.4.3 / 2011-07-14 +================== + + * Added docs for `status` option special-case. Closes #739 + * Fixed `options.filename`, exposing the view path to template engines + +2.4.2. / 2011-07-06 +================== + + * Revert "removed jsonp stripping" for XSS + +2.4.1 / 2011-07-06 +================== + + * Added `res.json()` JSONP support. Closes #737 + * Added _extending-templates_ example. Closes #730 + * Added "strict routing" setting for trailing slashes + * Added support for multiple envs in `app.configure()` calls. Closes #735 + * Changed: `res.send()` using `res.json()` + * Changed: when cookie `path === null` don't default it + * Changed; default cookie path to "home" setting. Closes #731 + * Removed _pids/logs_ creation from express(1) + +2.4.0 / 2011-06-28 +================== + + * Added chainable `res.status(code)` + * Added `res.json()`, an explicit version of `res.send(obj)` + * Added simple web-service example + +2.3.12 / 2011-06-22 +================== + + * \#express is now on freenode! come join! + * Added `req.get(field, param)` + * Added links to Japanese documentation, thanks @hideyukisaito! + * Added; the `express(1)` generated app outputs the env + * Added `content-negotiation` example + * Dependency: connect >= 1.5.1 < 2.0.0 + * Fixed view layout bug. Closes #720 + * Fixed; ignore body on 304. Closes #701 + +2.3.11 / 2011-06-04 +================== + + * Added `npm test` + * Removed generation of dummy test file from `express(1)` + * Fixed; `express(1)` adds express as a dep + * Fixed; prune on `prepublish` + +2.3.10 / 2011-05-27 +================== + + * Added `req.route`, exposing the current route + * Added _package.json_ generation support to `express(1)` + * Fixed call to `app.param()` function for optional params. Closes #682 + +2.3.9 / 2011-05-25 +================== + + * Fixed bug-ish with `../' in `res.partial()` calls + +2.3.8 / 2011-05-24 +================== + + * Fixed `app.options()` + +2.3.7 / 2011-05-23 +================== + + * Added route `Collection`, ex: `app.get('/user/:id').remove();` + * Added support for `app.param(fn)` to define param logic + * Removed `app.param()` support for callback with return value + * Removed module.parent check from express(1) generated app. Closes #670 + * Refactored router. Closes #639 + +2.3.6 / 2011-05-20 +================== + + * Changed; using devDependencies instead of git submodules + * Fixed redis session example + * Fixed markdown example + * Fixed view caching, should not be enabled in development + +2.3.5 / 2011-05-20 +================== + + * Added export `.view` as alias for `.View` + +2.3.4 / 2011-05-08 +================== + + * Added `./examples/say` + * Fixed `res.sendfile()` bug preventing the transfer of files with spaces + +2.3.3 / 2011-05-03 +================== + + * Added "case sensitive routes" option. + * Changed; split methods supported per rfc [slaskis] + * Fixed route-specific middleware when using the same callback function several times + +2.3.2 / 2011-04-27 +================== + + * Fixed view hints + +2.3.1 / 2011-04-26 +================== + + * Added `app.match()` as `app.match.all()` + * Added `app.lookup()` as `app.lookup.all()` + * Added `app.remove()` for `app.remove.all()` + * Added `app.remove.VERB()` + * Fixed template caching collision issue. Closes #644 + * Moved router over from connect and started refactor + +2.3.0 / 2011-04-25 +================== + + * Added options support to `res.clearCookie()` + * Added `res.helpers()` as alias of `res.locals()` + * Added; json defaults to UTF-8 with `res.send()`. Closes #632. [Daniel * Dependency `connect >= 1.4.0` + * Changed; auto set Content-Type in res.attachement [Aaron Heckmann] + * Renamed "cache views" to "view cache". Closes #628 + * Fixed caching of views when using several apps. Closes #637 + * Fixed gotcha invoking `app.param()` callbacks once per route middleware. +Closes #638 + * Fixed partial lookup precedence. Closes #631 +Shaw] + +2.2.2 / 2011-04-12 +================== + + * Added second callback support for `res.download()` connection errors + * Fixed `filename` option passing to template engine + +2.2.1 / 2011-04-04 +================== + + * Added `layout(path)` helper to change the layout within a view. Closes #610 + * Fixed `partial()` collection object support. + Previously only anything with `.length` would work. + When `.length` is present one must still be aware of holes, + however now `{ collection: {foo: 'bar'}}` is valid, exposes + `keyInCollection` and `keysInCollection`. + + * Performance improved with better view caching + * Removed `request` and `response` locals + * Changed; errorHandler page title is now `Express` instead of `Connect` + +2.2.0 / 2011-03-30 +================== + + * Added `app.lookup.VERB()`, ex `app.lookup.put('/user/:id')`. Closes #606 + * Added `app.match.VERB()`, ex `app.match.put('/user/12')`. Closes #606 + * Added `app.VERB(path)` as alias of `app.lookup.VERB()`. + * Dependency `connect >= 1.2.0` + +2.1.1 / 2011-03-29 +================== + + * Added; expose `err.view` object when failing to locate a view + * Fixed `res.partial()` call `next(err)` when no callback is given [reported by aheckmann] + * Fixed; `res.send(undefined)` responds with 204 [aheckmann] + +2.1.0 / 2011-03-24 +================== + + * Added `/_?` partial lookup support. Closes #447 + * Added `request`, `response`, and `app` local variables + * Added `settings` local variable, containing the app's settings + * Added `req.flash()` exception if `req.session` is not available + * Added `res.send(bool)` support (json response) + * Fixed stylus example for latest version + * Fixed; wrap try/catch around `res.render()` + +2.0.0 / 2011-03-17 +================== + + * Fixed up index view path alternative. + * Changed; `res.locals()` without object returns the locals + +2.0.0rc3 / 2011-03-17 +================== + + * Added `res.locals(obj)` to compliment `res.local(key, val)` + * Added `res.partial()` callback support + * Fixed recursive error reporting issue in `res.render()` + +2.0.0rc2 / 2011-03-17 +================== + + * Changed; `partial()` "locals" are now optional + * Fixed `SlowBuffer` support. Closes #584 [reported by tyrda01] + * Fixed .filename view engine option [reported by drudge] + * Fixed blog example + * Fixed `{req,res}.app` reference when mounting [Ben Weaver] + +2.0.0rc / 2011-03-14 +================== + + * Fixed; expose `HTTPSServer` constructor + * Fixed express(1) default test charset. Closes #579 [reported by secoif] + * Fixed; default charset to utf-8 instead of utf8 for lame IE [reported by NickP] + +2.0.0beta3 / 2011-03-09 +================== + + * Added support for `res.contentType()` literal + The original `res.contentType('.json')`, + `res.contentType('application/json')`, and `res.contentType('json')` + will work now. + * Added `res.render()` status option support back + * Added charset option for `res.render()` + * Added `.charset` support (via connect 1.0.4) + * Added view resolution hints when in development and a lookup fails + * Added layout lookup support relative to the page view. + For example while rendering `./views/user/index.jade` if you create + `./views/user/layout.jade` it will be used in favour of the root layout. + * Fixed `res.redirect()`. RFC states absolute url [reported by unlink] + * Fixed; default `res.send()` string charset to utf8 + * Removed `Partial` constructor (not currently used) + +2.0.0beta2 / 2011-03-07 +================== + + * Added res.render() `.locals` support back to aid in migration process + * Fixed flash example + +2.0.0beta / 2011-03-03 +================== + + * Added HTTPS support + * Added `res.cookie()` maxAge support + * Added `req.header()` _Referrer_ / _Referer_ special-case, either works + * Added mount support for `res.redirect()`, now respects the mount-point + * Added `union()` util, taking place of `merge(clone())` combo + * Added stylus support to express(1) generated app + * Added secret to session middleware used in examples and generated app + * Added `res.local(name, val)` for progressive view locals + * Added default param support to `req.param(name, default)` + * Added `app.disabled()` and `app.enabled()` + * Added `app.register()` support for omitting leading ".", either works + * Added `res.partial()`, using the same interface as `partial()` within a view. Closes #539 + * Added `app.param()` to map route params to async/sync logic + * Added; aliased `app.helpers()` as `app.locals()`. Closes #481 + * Added extname with no leading "." support to `res.contentType()` + * Added `cache views` setting, defaulting to enabled in "production" env + * Added index file partial resolution, eg: partial('user') may try _views/user/index.jade_. + * Added `req.accepts()` support for extensions + * Changed; `res.download()` and `res.sendfile()` now utilize Connect's + static file server `connect.static.send()`. + * Changed; replaced `connect.utils.mime()` with npm _mime_ module + * Changed; allow `req.query` to be pre-defined (via middleware or other parent + * Changed view partial resolution, now relative to parent view + * Changed view engine signature. no longer `engine.render(str, options, callback)`, now `engine.compile(str, options) -> Function`, the returned function accepts `fn(locals)`. + * Fixed `req.param()` bug returning Array.prototype methods. Closes #552 + * Fixed; using `Stream#pipe()` instead of `sys.pump()` in `res.sendfile()` + * Fixed; using _qs_ module instead of _querystring_ + * Fixed; strip unsafe chars from jsonp callbacks + * Removed "stream threshold" setting + +1.0.8 / 2011-03-01 +================== + + * Allow `req.query` to be pre-defined (via middleware or other parent app) + * "connect": ">= 0.5.0 < 1.0.0". Closes #547 + * Removed the long deprecated __EXPRESS_ENV__ support + +1.0.7 / 2011-02-07 +================== + + * Fixed `render()` setting inheritance. + Mounted apps would not inherit "view engine" + +1.0.6 / 2011-02-07 +================== + + * Fixed `view engine` setting bug when period is in dirname + +1.0.5 / 2011-02-05 +================== + + * Added secret to generated app `session()` call + +1.0.4 / 2011-02-05 +================== + + * Added `qs` dependency to _package.json_ + * Fixed namespaced `require()`s for latest connect support + +1.0.3 / 2011-01-13 +================== + + * Remove unsafe characters from JSONP callback names [Ryan Grove] + +1.0.2 / 2011-01-10 +================== + + * Removed nested require, using `connect.router` + +1.0.1 / 2010-12-29 +================== + + * Fixed for middleware stacked via `createServer()` + previously the `foo` middleware passed to `createServer(foo)` + would not have access to Express methods such as `res.send()` + or props like `req.query` etc. + +1.0.0 / 2010-11-16 +================== + + * Added; deduce partial object names from the last segment. + For example by default `partial('forum/post', postObject)` will + give you the _post_ object, providing a meaningful default. + * Added http status code string representation to `res.redirect()` body + * Added; `res.redirect()` supporting _text/plain_ and _text/html_ via __Accept__. + * Added `req.is()` to aid in content negotiation + * Added partial local inheritance [suggested by masylum]. Closes #102 + providing access to parent template locals. + * Added _-s, --session[s]_ flag to express(1) to add session related middleware + * Added _--template_ flag to express(1) to specify the + template engine to use. + * Added _--css_ flag to express(1) to specify the + stylesheet engine to use (or just plain css by default). + * Added `app.all()` support [thanks aheckmann] + * Added partial direct object support. + You may now `partial('user', user)` providing the "user" local, + vs previously `partial('user', { object: user })`. + * Added _route-separation_ example since many people question ways + to do this with CommonJS modules. Also view the _blog_ example for + an alternative. + * Performance; caching view path derived partial object names + * Fixed partial local inheritance precedence. [reported by Nick Poulden] Closes #454 + * Fixed jsonp support; _text/javascript_ as per mailinglist discussion + +1.0.0rc4 / 2010-10-14 +================== + + * Added _NODE_ENV_ support, _EXPRESS_ENV_ is deprecated and will be removed in 1.0.0 + * Added route-middleware support (very helpful, see the [docs](http://expressjs.com/guide.html#Route-Middleware)) + * Added _jsonp callback_ setting to enable/disable jsonp autowrapping [Dav Glass] + * Added callback query check on response.send to autowrap JSON objects for simple webservice implementations [Dav Glass] + * Added `partial()` support for array-like collections. Closes #434 + * Added support for swappable querystring parsers + * Added session usage docs. Closes #443 + * Added dynamic helper caching. Closes #439 [suggested by maritz] + * Added authentication example + * Added basic Range support to `res.sendfile()` (and `res.download()` etc) + * Changed; `express(1)` generated app using 2 spaces instead of 4 + * Default env to "development" again [aheckmann] + * Removed _context_ option is no more, use "scope" + * Fixed; exposing _./support_ libs to examples so they can run without installs + * Fixed mvc example + +1.0.0rc3 / 2010-09-20 +================== + + * Added confirmation for `express(1)` app generation. Closes #391 + * Added extending of flash formatters via `app.flashFormatters` + * Added flash formatter support. Closes #411 + * Added streaming support to `res.sendfile()` using `sys.pump()` when >= "stream threshold" + * Added _stream threshold_ setting for `res.sendfile()` + * Added `res.send()` __HEAD__ support + * Added `res.clearCookie()` + * Added `res.cookie()` + * Added `res.render()` headers option + * Added `res.redirect()` response bodies + * Added `res.render()` status option support. Closes #425 [thanks aheckmann] + * Fixed `res.sendfile()` responding with 403 on malicious path + * Fixed `res.download()` bug; when an error occurs remove _Content-Disposition_ + * Fixed; mounted apps settings now inherit from parent app [aheckmann] + * Fixed; stripping Content-Length / Content-Type when 204 + * Fixed `res.send()` 204. Closes #419 + * Fixed multiple _Set-Cookie_ headers via `res.header()`. Closes #402 + * Fixed bug messing with error handlers when `listenFD()` is called instead of `listen()`. [thanks guillermo] + + +1.0.0rc2 / 2010-08-17 +================== + + * Added `app.register()` for template engine mapping. Closes #390 + * Added `res.render()` callback support as second argument (no options) + * Added callback support to `res.download()` + * Added callback support for `res.sendfile()` + * Added support for middleware access via `express.middlewareName()` vs `connect.middlewareName()` + * Added "partials" setting to docs + * Added default expresso tests to `express(1)` generated app. Closes #384 + * Fixed `res.sendfile()` error handling, defer via `next()` + * Fixed `res.render()` callback when a layout is used [thanks guillermo] + * Fixed; `make install` creating ~/.node_libraries when not present + * Fixed issue preventing error handlers from being defined anywhere. Closes #387 + +1.0.0rc / 2010-07-28 +================== + + * Added mounted hook. Closes #369 + * Added connect dependency to _package.json_ + + * Removed "reload views" setting and support code + development env never caches, production always caches. + + * Removed _param_ in route callbacks, signature is now + simply (req, res, next), previously (req, res, params, next). + Use _req.params_ for path captures, _req.query_ for GET params. + + * Fixed "home" setting + * Fixed middleware/router precedence issue. Closes #366 + * Fixed; _configure()_ callbacks called immediately. Closes #368 + +1.0.0beta2 / 2010-07-23 +================== + + * Added more examples + * Added; exporting `Server` constructor + * Added `Server#helpers()` for view locals + * Added `Server#dynamicHelpers()` for dynamic view locals. Closes #349 + * Added support for absolute view paths + * Added; _home_ setting defaults to `Server#route` for mounted apps. Closes #363 + * Added Guillermo Rauch to the contributor list + * Added support for "as" for non-collection partials. Closes #341 + * Fixed _install.sh_, ensuring _~/.node_libraries_ exists. Closes #362 [thanks jf] + * Fixed `res.render()` exceptions, now passed to `next()` when no callback is given [thanks guillermo] + * Fixed instanceof `Array` checks, now `Array.isArray()` + * Fixed express(1) expansion of public dirs. Closes #348 + * Fixed middleware precedence. Closes #345 + * Fixed view watcher, now async [thanks aheckmann] + +1.0.0beta / 2010-07-15 +================== + + * Re-write + - much faster + - much lighter + - Check [ExpressJS.com](http://expressjs.com) for migration guide and updated docs + +0.14.0 / 2010-06-15 +================== + + * Utilize relative requires + * Added Static bufferSize option [aheckmann] + * Fixed caching of view and partial subdirectories [aheckmann] + * Fixed mime.type() comments now that ".ext" is not supported + * Updated haml submodule + * Updated class submodule + * Removed bin/express + +0.13.0 / 2010-06-01 +================== + + * Added node v0.1.97 compatibility + * Added support for deleting cookies via Request#cookie('key', null) + * Updated haml submodule + * Fixed not-found page, now using using charset utf-8 + * Fixed show-exceptions page, now using using charset utf-8 + * Fixed view support due to fs.readFile Buffers + * Changed; mime.type() no longer accepts ".type" due to node extname() changes + +0.12.0 / 2010-05-22 +================== + + * Added node v0.1.96 compatibility + * Added view `helpers` export which act as additional local variables + * Updated haml submodule + * Changed ETag; removed inode, modified time only + * Fixed LF to CRLF for setting multiple cookies + * Fixed cookie complation; values are now urlencoded + * Fixed cookies parsing; accepts quoted values and url escaped cookies + +0.11.0 / 2010-05-06 +================== + + * Added support for layouts using different engines + - this.render('page.html.haml', { layout: 'super-cool-layout.html.ejs' }) + - this.render('page.html.haml', { layout: 'foo' }) // assumes 'foo.html.haml' + - this.render('page.html.haml', { layout: false }) // no layout + * Updated ext submodule + * Updated haml submodule + * Fixed EJS partial support by passing along the context. Issue #307 + +0.10.1 / 2010-05-03 +================== + + * Fixed binary uploads. + +0.10.0 / 2010-04-30 +================== + + * Added charset support via Request#charset (automatically assigned to 'UTF-8' when respond()'s + encoding is set to 'utf8' or 'utf-8'. + * Added "encoding" option to Request#render(). Closes #299 + * Added "dump exceptions" setting, which is enabled by default. + * Added simple ejs template engine support + * Added error reponse support for text/plain, application/json. Closes #297 + * Added callback function param to Request#error() + * Added Request#sendHead() + * Added Request#stream() + * Added support for Request#respond(304, null) for empty response bodies + * Added ETag support to Request#sendfile() + * Added options to Request#sendfile(), passed to fs.createReadStream() + * Added filename arg to Request#download() + * Performance enhanced due to pre-reversing plugins so that plugins.reverse() is not called on each request + * Performance enhanced by preventing several calls to toLowerCase() in Router#match() + * Changed; Request#sendfile() now streams + * Changed; Renamed Request#halt() to Request#respond(). Closes #289 + * Changed; Using sys.inspect() instead of JSON.encode() for error output + * Changed; run() returns the http.Server instance. Closes #298 + * Changed; Defaulting Server#host to null (INADDR_ANY) + * Changed; Logger "common" format scale of 0.4f + * Removed Logger "request" format + * Fixed; Catching ENOENT in view caching, preventing error when "views/partials" is not found + * Fixed several issues with http client + * Fixed Logger Content-Length output + * Fixed bug preventing Opera from retaining the generated session id. Closes #292 + +0.9.0 / 2010-04-14 +================== + + * Added DSL level error() route support + * Added DSL level notFound() route support + * Added Request#error() + * Added Request#notFound() + * Added Request#render() callback function. Closes #258 + * Added "max upload size" setting + * Added "magic" variables to collection partials (\_\_index\_\_, \_\_length\_\_, \_\_isFirst\_\_, \_\_isLast\_\_). Closes #254 + * Added [haml.js](http://github.com/visionmedia/haml.js) submodule; removed haml-js + * Added callback function support to Request#halt() as 3rd/4th arg + * Added preprocessing of route param wildcards using param(). Closes #251 + * Added view partial support (with collections etc) + * Fixed bug preventing falsey params (such as ?page=0). Closes #286 + * Fixed setting of multiple cookies. Closes #199 + * Changed; view naming convention is now NAME.TYPE.ENGINE (for example page.html.haml) + * Changed; session cookie is now httpOnly + * Changed; Request is no longer global + * Changed; Event is no longer global + * Changed; "sys" module is no longer global + * Changed; moved Request#download to Static plugin where it belongs + * Changed; Request instance created before body parsing. Closes #262 + * Changed; Pre-caching views in memory when "cache view contents" is enabled. Closes #253 + * Changed; Pre-caching view partials in memory when "cache view partials" is enabled + * Updated support to node --version 0.1.90 + * Updated dependencies + * Removed set("session cookie") in favour of use(Session, { cookie: { ... }}) + * Removed utils.mixin(); use Object#mergeDeep() + +0.8.0 / 2010-03-19 +================== + + * Added coffeescript example app. Closes #242 + * Changed; cache api now async friendly. Closes #240 + * Removed deprecated 'express/static' support. Use 'express/plugins/static' + +0.7.6 / 2010-03-19 +================== + + * Added Request#isXHR. Closes #229 + * Added `make install` (for the executable) + * Added `express` executable for setting up simple app templates + * Added "GET /public/*" to Static plugin, defaulting to /public + * Added Static plugin + * Fixed; Request#render() only calls cache.get() once + * Fixed; Namespacing View caches with "view:" + * Fixed; Namespacing Static caches with "static:" + * Fixed; Both example apps now use the Static plugin + * Fixed set("views"). Closes #239 + * Fixed missing space for combined log format + * Deprecated Request#sendfile() and 'express/static' + * Removed Server#running + +0.7.5 / 2010-03-16 +================== + + * Added Request#flash() support without args, now returns all flashes + * Updated ext submodule + +0.7.4 / 2010-03-16 +================== + + * Fixed session reaper + * Changed; class.js replacing js-oo Class implementation (quite a bit faster, no browser cruft) + +0.7.3 / 2010-03-16 +================== + + * Added package.json + * Fixed requiring of haml / sass due to kiwi removal + +0.7.2 / 2010-03-16 +================== + + * Fixed GIT submodules (HAH!) + +0.7.1 / 2010-03-16 +================== + + * Changed; Express now using submodules again until a PM is adopted + * Changed; chat example using millisecond conversions from ext + +0.7.0 / 2010-03-15 +================== + + * Added Request#pass() support (finds the next matching route, or the given path) + * Added Logger plugin (default "common" format replaces CommonLogger) + * Removed Profiler plugin + * Removed CommonLogger plugin + +0.6.0 / 2010-03-11 +================== + + * Added seed.yml for kiwi package management support + * Added HTTP client query string support when method is GET. Closes #205 + + * Added support for arbitrary view engines. + For example "foo.engine.html" will now require('engine'), + the exports from this module are cached after the first require(). + + * Added async plugin support + + * Removed usage of RESTful route funcs as http client + get() etc, use http.get() and friends + + * Removed custom exceptions + +0.5.0 / 2010-03-10 +================== + + * Added ext dependency (library of js extensions) + * Removed extname() / basename() utils. Use path module + * Removed toArray() util. Use arguments.values + * Removed escapeRegexp() util. Use RegExp.escape() + * Removed process.mixin() dependency. Use utils.mixin() + * Removed Collection + * Removed ElementCollection + * Shameless self promotion of ebook "Advanced JavaScript" (http://dev-mag.com) ;) + +0.4.0 / 2010-02-11 +================== + + * Added flash() example to sample upload app + * Added high level restful http client module (express/http) + * Changed; RESTful route functions double as HTTP clients. Closes #69 + * Changed; throwing error when routes are added at runtime + * Changed; defaulting render() context to the current Request. Closes #197 + * Updated haml submodule + +0.3.0 / 2010-02-11 +================== + + * Updated haml / sass submodules. Closes #200 + * Added flash message support. Closes #64 + * Added accepts() now allows multiple args. fixes #117 + * Added support for plugins to halt. Closes #189 + * Added alternate layout support. Closes #119 + * Removed Route#run(). Closes #188 + * Fixed broken specs due to use(Cookie) missing + +0.2.1 / 2010-02-05 +================== + + * Added "plot" format option for Profiler (for gnuplot processing) + * Added request number to Profiler plugin + * Fixed binary encoding for multi-part file uploads, was previously defaulting to UTF8 + * Fixed issue with routes not firing when not files are present. Closes #184 + * Fixed process.Promise -> events.Promise + +0.2.0 / 2010-02-03 +================== + + * Added parseParam() support for name[] etc. (allows for file inputs with "multiple" attr) Closes #180 + * Added Both Cache and Session option "reapInterval" may be "reapEvery". Closes #174 + * Added expiration support to cache api with reaper. Closes #133 + * Added cache Store.Memory#reap() + * Added Cache; cache api now uses first class Cache instances + * Added abstract session Store. Closes #172 + * Changed; cache Memory.Store#get() utilizing Collection + * Renamed MemoryStore -> Store.Memory + * Fixed use() of the same plugin several time will always use latest options. Closes #176 + +0.1.0 / 2010-02-03 +================== + + * Changed; Hooks (before / after) pass request as arg as well as evaluated in their context + * Updated node support to 0.1.27 Closes #169 + * Updated dirname(__filename) -> __dirname + * Updated libxmljs support to v0.2.0 + * Added session support with memory store / reaping + * Added quick uid() helper + * Added multi-part upload support + * Added Sass.js support / submodule + * Added production env caching view contents and static files + * Added static file caching. Closes #136 + * Added cache plugin with memory stores + * Added support to StaticFile so that it works with non-textual files. + * Removed dirname() helper + * Removed several globals (now their modules must be required) + +0.0.2 / 2010-01-10 +================== + + * Added view benchmarks; currently haml vs ejs + * Added Request#attachment() specs. Closes #116 + * Added use of node's parseQuery() util. Closes #123 + * Added `make init` for submodules + * Updated Haml + * Updated sample chat app to show messages on load + * Updated libxmljs parseString -> parseHtmlString + * Fixed `make init` to work with older versions of git + * Fixed specs can now run independant specs for those who cant build deps. Closes #127 + * Fixed issues introduced by the node url module changes. Closes 126. + * Fixed two assertions failing due to Collection#keys() returning strings + * Fixed faulty Collection#toArray() spec due to keys() returning strings + * Fixed `make test` now builds libxmljs.node before testing + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/server/node_modules/express/LICENSE b/server/node_modules/express/LICENSE new file mode 100644 index 0000000..d23e93c --- /dev/null +++ b/server/node_modules/express/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2009-2013 TJ Holowaychuk + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/server/node_modules/express/Makefile b/server/node_modules/express/Makefile new file mode 100644 index 0000000..d0cb2a0 --- /dev/null +++ b/server/node_modules/express/Makefile @@ -0,0 +1,34 @@ + +MOCHA_OPTS= --check-leaks +REPORTER = dot + +check: test + +test: test-unit test-acceptance + +test-unit: + @NODE_ENV=test ./node_modules/.bin/mocha \ + --reporter $(REPORTER) \ + --globals setImmediate,clearImmediate \ + $(MOCHA_OPTS) + +test-acceptance: + @NODE_ENV=test ./node_modules/.bin/mocha \ + --reporter $(REPORTER) \ + --bail \ + test/acceptance/*.js + +test-cov: lib-cov + @EXPRESS_COV=1 $(MAKE) test REPORTER=html-cov > coverage.html + +lib-cov: + @jscoverage lib lib-cov + +benchmark: + @./support/bench + +clean: + rm -f coverage.html + rm -fr lib-cov + +.PHONY: test test-unit test-acceptance benchmark clean diff --git a/server/node_modules/express/Readme.md b/server/node_modules/express/Readme.md new file mode 100644 index 0000000..44d47fe --- /dev/null +++ b/server/node_modules/express/Readme.md @@ -0,0 +1,126 @@ +[![express logo](http://f.cl.ly/items/0V2S1n0K1i3y1c122g04/Screen%20Shot%202012-04-11%20at%209.59.42%20AM.png)](http://expressjs.com/) + + Fast, unopinionated, minimalist web framework for [node](http://nodejs.org). + + [![Build Status](https://secure.travis-ci.org/visionmedia/express.png)](http://travis-ci.org/visionmedia/express) [![Gittip](http://img.shields.io/gittip/visionmedia.png)](https://www.gittip.com/visionmedia/) + +```js +var express = require('express'); +var app = express(); + +app.get('/', function(req, res){ + res.send('Hello World'); +}); + +app.listen(3000); +``` + +## Installation + + $ npm install -g express + +## Quick Start + + The quickest way to get started with express is to utilize the executable `express(1)` to generate an application as shown below: + + Create the app: + + $ npm install -g express + $ express /tmp/foo && cd /tmp/foo + + Install dependencies: + + $ npm install + + Start the server: + + $ node app + +## Features + + * Built on [Connect](http://github.com/senchalabs/connect) + * Robust routing + * HTTP helpers (redirection, caching, etc) + * View system supporting 14+ template engines + * Content negotiation + * Focus on high performance + * Environment based configuration + * Executable for generating applications quickly + * High test coverage + +## Philosophy + + The Express philosophy is to provide small, robust tooling for HTTP servers. Making + it a great solution for single page applications, web sites, hybrids, or public + HTTP APIs. + + Built on Connect you can use _only_ what you need, and nothing more, applications + can be as big or as small as you like, even a single file. Express does + not force you to use any specific ORM or template engine. With support for over + 14 template engines via [Consolidate.js](http://github.com/visionmedia/consolidate.js) + you can quickly craft your perfect framework. + +## More Information + + * [Website and Documentation](http://expressjs.com/) stored at [visionmedia/expressjs.com](https://github.com/visionmedia/expressjs.com) + * Join #express on freenode + * [Google Group](http://groups.google.com/group/express-js) for discussion + * Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates + * Visit the [Wiki](http://github.com/visionmedia/express/wiki) + * [Русскоязычная документация](http://jsman.ru/express/) + * Run express examples [online](https://runnable.com/express) + +## Viewing Examples + +Clone the Express repo, then install the dev dependencies to install all the example / test suite deps: + + $ git clone git://github.com/visionmedia/express.git --depth 1 + $ cd express + $ npm install + +then run whichever tests you want: + + $ node examples/content-negotiation + +You can also view live examples here + + + +## Running Tests + +To run the test suite first invoke the following command within the repo, installing the development dependencies: + + $ npm install + +then run the tests: + + $ make test + +## Contributors + + https://github.com/visionmedia/express/graphs/contributors + +## License + +(The MIT License) + +Copyright (c) 2009-2012 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/server/node_modules/express/bin/express b/server/node_modules/express/bin/express new file mode 100644 index 0000000..69b65d9 --- /dev/null +++ b/server/node_modules/express/bin/express @@ -0,0 +1,423 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('commander') + , mkdirp = require('mkdirp') + , pkg = require('../package.json') + , version = pkg.version + , os = require('os') + , fs = require('fs'); + +// CLI + +program + .version(version) + .usage('[options] [dir]') + .option('-s, --sessions', 'add session support') + .option('-e, --ejs', 'add ejs engine support (defaults to jade)') + .option('-J, --jshtml', 'add jshtml engine support (defaults to jade)') + .option('-H, --hogan', 'add hogan.js engine support') + .option('-c, --css ', 'add stylesheet support (less|stylus) (defaults to plain css)') + .option('-f, --force', 'force on non-empty directory') + .parse(process.argv); + +// Path + +var path = program.args.shift() || '.'; + +// end-of-line code + +var eol = os.EOL + +// Template engine + +program.template = 'jade'; +if (program.ejs) program.template = 'ejs'; +if (program.jshtml) program.template = 'jshtml'; +if (program.hogan) program.template = 'hjs'; + +/** + * Routes index template. + */ + +var index = [ + '' + , '/*' + , ' * GET home page.' + , ' */' + , '' + , 'exports.index = function(req, res){' + , ' res.render(\'index\', { title: \'Express\' });' + , '};' +].join(eol); + +/** + * Routes users template. + */ + +var users = [ + '' + , '/*' + , ' * GET users listing.' + , ' */' + , '' + , 'exports.list = function(req, res){' + , ' res.send("respond with a resource");' + , '};' +].join(eol); + +/** + * Jade layout template. + */ + +var jadeLayout = [ + 'doctype 5' + , 'html' + , ' head' + , ' title= title' + , ' link(rel=\'stylesheet\', href=\'/stylesheets/style.css\')' + , ' body' + , ' block content' +].join(eol); + +/** + * Jade index template. + */ + +var jadeIndex = [ + 'extends layout' + , '' + , 'block content' + , ' h1= title' + , ' p Welcome to #{title}' +].join(eol); + +/** + * EJS index template. + */ + +var ejsIndex = [ + '' + , '' + , ' ' + , ' <%= title %>' + , ' ' + , ' ' + , ' ' + , '

<%= title %>

' + , '

Welcome to <%= title %>

' + , ' ' + , '' +].join(eol); + +/** + * JSHTML layout template. + */ + +var jshtmlLayout = [ + '' + , '' + , ' ' + , ' @write(title) ' + , ' ' + , ' ' + , ' ' + , ' @write(body)' + , ' ' + , '' +].join(eol); + +/** + * JSHTML index template. + */ + +var jshtmlIndex = [ + '

@write(title)

' + , '

Welcome to @write(title)

' +].join(eol); + +/** + * Hogan.js index template. + */ +var hoganIndex = [ + '' + , '' + , ' ' + , ' {{ title }}' + , ' ' + , ' ' + , ' ' + , '

{{ title }}

' + , '

Welcome to {{ title }}

' + , ' ' + , '' +].join(eol); + +/** + * Default css template. + */ + +var css = [ + 'body {' + , ' padding: 50px;' + , ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;' + , '}' + , '' + , 'a {' + , ' color: #00B7FF;' + , '}' +].join(eol); + +/** + * Default less template. + */ + +var less = [ + 'body {' + , ' padding: 50px;' + , ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;' + , '}' + , '' + , 'a {' + , ' color: #00B7FF;' + , '}' +].join(eol); + +/** + * Default stylus template. + */ + +var stylus = [ + 'body' + , ' padding: 50px' + , ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif' + , 'a' + , ' color: #00B7FF' +].join(eol); + +/** + * App template. + */ + +var app = [ + '' + , '/**' + , ' * Module dependencies.' + , ' */' + , '' + , 'var express = require(\'express\');' + , 'var routes = require(\'./routes\');' + , 'var user = require(\'./routes/user\');' + , 'var http = require(\'http\');' + , 'var path = require(\'path\');' + , '' + , 'var app = express();' + , '' + , '// all environments' + , 'app.set(\'port\', process.env.PORT || 3000);' + , 'app.set(\'views\', path.join(__dirname, \'views\'));' + , 'app.set(\'view engine\', \':TEMPLATE\');' + , 'app.use(express.favicon());' + , 'app.use(express.logger(\'dev\'));' + , 'app.use(express.json());' + , 'app.use(express.urlencoded());' + , 'app.use(express.methodOverride());{sess}' + , 'app.use(app.router);{css}' + , 'app.use(express.static(path.join(__dirname, \'public\')));' + , '' + , '// development only' + , 'if (\'development\' == app.get(\'env\')) {' + , ' app.use(express.errorHandler());' + , '}' + , '' + , 'app.get(\'/\', routes.index);' + , 'app.get(\'/users\', user.list);' + , '' + , 'http.createServer(app).listen(app.get(\'port\'), function(){' + , ' console.log(\'Express server listening on port \' + app.get(\'port\'));' + , '});' + , '' +].join(eol); + +// Generate application + +(function createApplication(path) { + emptyDirectory(path, function(empty){ + if (empty || program.force) { + createApplicationAt(path); + } else { + program.confirm('destination is not empty, continue? ', function(ok){ + if (ok) { + process.stdin.destroy(); + createApplicationAt(path); + } else { + abort('aborting'); + } + }); + } + }); +})(path); + +/** + * Create application at the given directory `path`. + * + * @param {String} path + */ + +function createApplicationAt(path) { + console.log(); + process.on('exit', function(){ + console.log(); + console.log(' install dependencies:'); + console.log(' $ cd %s && npm install', path); + console.log(); + console.log(' run the app:'); + console.log(' $ node app'); + console.log(); + }); + + mkdir(path, function(){ + mkdir(path + '/public'); + mkdir(path + '/public/javascripts'); + mkdir(path + '/public/images'); + mkdir(path + '/public/stylesheets', function(){ + switch (program.css) { + case 'less': + write(path + '/public/stylesheets/style.less', less); + break; + case 'stylus': + write(path + '/public/stylesheets/style.styl', stylus); + break; + default: + write(path + '/public/stylesheets/style.css', css); + } + }); + + mkdir(path + '/routes', function(){ + write(path + '/routes/index.js', index); + write(path + '/routes/user.js', users); + }); + + mkdir(path + '/views', function(){ + switch (program.template) { + case 'ejs': + write(path + '/views/index.ejs', ejsIndex); + break; + case 'jade': + write(path + '/views/layout.jade', jadeLayout); + write(path + '/views/index.jade', jadeIndex); + break; + case 'jshtml': + write(path + '/views/layout.jshtml', jshtmlLayout); + write(path + '/views/index.jshtml', jshtmlIndex); + break; + case 'hjs': + write(path + '/views/index.hjs', hoganIndex); + break; + + } + }); + + // CSS Engine support + switch (program.css) { + case 'less': + app = app.replace('{css}', eol + 'app.use(require(\'less-middleware\')({ src: path.join(__dirname, \'public\') }));'); + break; + case 'stylus': + app = app.replace('{css}', eol + 'app.use(require(\'stylus\').middleware(path.join(__dirname, \'public\')));'); + break; + default: + app = app.replace('{css}', ''); + } + + // Session support + app = app.replace('{sess}', program.sessions + ? eol + 'app.use(express.cookieParser(\'your secret here\'));' + eol + 'app.use(express.session());' + : ''); + + // Template support + app = app.replace(':TEMPLATE', program.template); + + // package.json + var pkg = { + name: 'application-name' + , version: '0.0.1' + , private: true + , scripts: { start: 'node app.js' } + , dependencies: { + express: version + } + } + + if (program.template) pkg.dependencies[program.template] = '*'; + + // CSS Engine support + switch (program.css) { + case 'less': + pkg.dependencies['less-middleware'] = '*'; + break; + default: + if (program.css) { + pkg.dependencies[program.css] = '*'; + } + } + + write(path + '/package.json', JSON.stringify(pkg, null, 2)); + write(path + '/app.js', app); + }); +} + +/** + * Check if the given directory `path` is empty. + * + * @param {String} path + * @param {Function} fn + */ + +function emptyDirectory(path, fn) { + fs.readdir(path, function(err, files){ + if (err && 'ENOENT' != err.code) throw err; + fn(!files || !files.length); + }); +} + +/** + * echo str > path. + * + * @param {String} path + * @param {String} str + */ + +function write(path, str) { + fs.writeFile(path, str); + console.log(' \x1b[36mcreate\x1b[0m : ' + path); +} + +/** + * Mkdir -p. + * + * @param {String} path + * @param {Function} fn + */ + +function mkdir(path, fn) { + mkdirp(path, 0755, function(err){ + if (err) throw err; + console.log(' \033[36mcreate\033[0m : ' + path); + fn && fn(); + }); +} + +/** + * Exit with the given `str`. + * + * @param {String} str + */ + +function abort(str) { + console.error(str); + process.exit(1); +} diff --git a/server/node_modules/express/index.js b/server/node_modules/express/index.js new file mode 100644 index 0000000..bfe9934 --- /dev/null +++ b/server/node_modules/express/index.js @@ -0,0 +1,4 @@ + +module.exports = process.env.EXPRESS_COV + ? require('./lib-cov/express') + : require('./lib/express'); \ No newline at end of file diff --git a/server/node_modules/express/lib/application.js b/server/node_modules/express/lib/application.js new file mode 100644 index 0000000..d5806f0 --- /dev/null +++ b/server/node_modules/express/lib/application.js @@ -0,0 +1,534 @@ +/** + * Module dependencies. + */ + +var connect = require('connect') + , Router = require('./router') + , methods = require('methods') + , middleware = require('./middleware') + , debug = require('debug')('express:application') + , locals = require('./utils').locals + , View = require('./view') + , utils = connect.utils + , http = require('http'); + +/** + * Application prototype. + */ + +var app = exports = module.exports = {}; + +/** + * Initialize the server. + * + * - setup default configuration + * - setup default middleware + * - setup route reflection methods + * + * @api private + */ + +app.init = function(){ + this.cache = {}; + this.settings = {}; + this.engines = {}; + this.defaultConfiguration(); +}; + +/** + * Initialize application configuration. + * + * @api private + */ + +app.defaultConfiguration = function(){ + // default settings + this.enable('x-powered-by'); + this.enable('etag'); + this.set('env', process.env.NODE_ENV || 'development'); + this.set('subdomain offset', 2); + debug('booting in %s mode', this.get('env')); + + // implicit middleware + this.use(connect.query()); + this.use(middleware.init(this)); + + // inherit protos + this.on('mount', function(parent){ + this.request.__proto__ = parent.request; + this.response.__proto__ = parent.response; + this.engines.__proto__ = parent.engines; + this.settings.__proto__ = parent.settings; + }); + + // router + this._router = new Router(this); + this.routes = this._router.map; + this.__defineGetter__('router', function(){ + this._usedRouter = true; + this._router.caseSensitive = this.enabled('case sensitive routing'); + this._router.strict = this.enabled('strict routing'); + return this._router.middleware; + }); + + // setup locals + this.locals = locals(this); + + // default locals + this.locals.settings = this.settings; + + // default configuration + this.set('view', View); + this.set('views', process.cwd() + '/views'); + this.set('jsonp callback name', 'callback'); + + this.configure('development', function(){ + this.set('json spaces', 2); + }); + + this.configure('production', function(){ + this.enable('view cache'); + }); +}; + +/** + * Proxy `connect#use()` to apply settings to + * mounted applications. + * + * @param {String|Function|Server} route + * @param {Function|Server} fn + * @return {app} for chaining + * @api public + */ + +app.use = function(route, fn){ + var app; + + // default route to '/' + if ('string' != typeof route) fn = route, route = '/'; + + // express app + if (fn.handle && fn.set) app = fn; + + // restore .app property on req and res + if (app) { + app.route = route; + fn = function(req, res, next) { + var orig = req.app; + app.handle(req, res, function(err){ + req.__proto__ = orig.request; + res.__proto__ = orig.response; + next(err); + }); + }; + } + + connect.proto.use.call(this, route, fn); + + // mounted an app + if (app) { + app.parent = this; + app.emit('mount', this); + } + + return this; +}; + +/** + * Register the given template engine callback `fn` + * as `ext`. + * + * By default will `require()` the engine based on the + * file extension. For example if you try to render + * a "foo.jade" file Express will invoke the following internally: + * + * app.engine('jade', require('jade').__express); + * + * For engines that do not provide `.__express` out of the box, + * or if you wish to "map" a different extension to the template engine + * you may use this method. For example mapping the EJS template engine to + * ".html" files: + * + * app.engine('html', require('ejs').renderFile); + * + * In this case EJS provides a `.renderFile()` method with + * the same signature that Express expects: `(path, options, callback)`, + * though note that it aliases this method as `ejs.__express` internally + * so if you're using ".ejs" extensions you dont need to do anything. + * + * Some template engines do not follow this convention, the + * [Consolidate.js](https://github.com/visionmedia/consolidate.js) + * library was created to map all of node's popular template + * engines to follow this convention, thus allowing them to + * work seamlessly within Express. + * + * @param {String} ext + * @param {Function} fn + * @return {app} for chaining + * @api public + */ + +app.engine = function(ext, fn){ + if ('function' != typeof fn) throw new Error('callback function required'); + if ('.' != ext[0]) ext = '.' + ext; + this.engines[ext] = fn; + return this; +}; + +/** + * Map the given param placeholder `name`(s) to the given callback(s). + * + * Parameter mapping is used to provide pre-conditions to routes + * which use normalized placeholders. For example a _:user_id_ parameter + * could automatically load a user's information from the database without + * any additional code, + * + * The callback uses the same signature as middleware, the only difference + * being that the value of the placeholder is passed, in this case the _id_ + * of the user. Once the `next()` function is invoked, just like middleware + * it will continue on to execute the route, or subsequent parameter functions. + * + * app.param('user_id', function(req, res, next, id){ + * User.find(id, function(err, user){ + * if (err) { + * next(err); + * } else if (user) { + * req.user = user; + * next(); + * } else { + * next(new Error('failed to load user')); + * } + * }); + * }); + * + * @param {String|Array} name + * @param {Function} fn + * @return {app} for chaining + * @api public + */ + +app.param = function(name, fn){ + var self = this + , fns = [].slice.call(arguments, 1); + + // array + if (Array.isArray(name)) { + name.forEach(function(name){ + fns.forEach(function(fn){ + self.param(name, fn); + }); + }); + // param logic + } else if ('function' == typeof name) { + this._router.param(name); + // single + } else { + if (':' == name[0]) name = name.substr(1); + fns.forEach(function(fn){ + self._router.param(name, fn); + }); + } + + return this; +}; + +/** + * Assign `setting` to `val`, or return `setting`'s value. + * + * app.set('foo', 'bar'); + * app.get('foo'); + * // => "bar" + * + * Mounted servers inherit their parent server's settings. + * + * @param {String} setting + * @param {String} val + * @return {Server} for chaining + * @api public + */ + +app.set = function(setting, val){ + if (1 == arguments.length) { + return this.settings[setting]; + } else { + this.settings[setting] = val; + return this; + } +}; + +/** + * Return the app's absolute pathname + * based on the parent(s) that have + * mounted it. + * + * For example if the application was + * mounted as "/admin", which itself + * was mounted as "/blog" then the + * return value would be "/blog/admin". + * + * @return {String} + * @api private + */ + +app.path = function(){ + return this.parent + ? this.parent.path() + this.route + : ''; +}; + +/** + * Check if `setting` is enabled (truthy). + * + * app.enabled('foo') + * // => false + * + * app.enable('foo') + * app.enabled('foo') + * // => true + * + * @param {String} setting + * @return {Boolean} + * @api public + */ + +app.enabled = function(setting){ + return !!this.set(setting); +}; + +/** + * Check if `setting` is disabled. + * + * app.disabled('foo') + * // => true + * + * app.enable('foo') + * app.disabled('foo') + * // => false + * + * @param {String} setting + * @return {Boolean} + * @api public + */ + +app.disabled = function(setting){ + return !this.set(setting); +}; + +/** + * Enable `setting`. + * + * @param {String} setting + * @return {app} for chaining + * @api public + */ + +app.enable = function(setting){ + return this.set(setting, true); +}; + +/** + * Disable `setting`. + * + * @param {String} setting + * @return {app} for chaining + * @api public + */ + +app.disable = function(setting){ + return this.set(setting, false); +}; + +/** + * Configure callback for zero or more envs, + * when no `env` is specified that callback will + * be invoked for all environments. Any combination + * can be used multiple times, in any order desired. + * + * Examples: + * + * app.configure(function(){ + * // executed for all envs + * }); + * + * app.configure('stage', function(){ + * // executed staging env + * }); + * + * app.configure('stage', 'production', function(){ + * // executed for stage and production + * }); + * + * Note: + * + * These callbacks are invoked immediately, and + * are effectively sugar for the following: + * + * var env = process.env.NODE_ENV || 'development'; + * + * switch (env) { + * case 'development': + * ... + * break; + * case 'stage': + * ... + * break; + * case 'production': + * ... + * break; + * } + * + * @param {String} env... + * @param {Function} fn + * @return {app} for chaining + * @api public + */ + +app.configure = function(env, fn){ + var envs = 'all' + , args = [].slice.call(arguments); + fn = args.pop(); + if (args.length) envs = args; + if ('all' == envs || ~envs.indexOf(this.settings.env)) fn.call(this); + return this; +}; + +/** + * Delegate `.VERB(...)` calls to `router.VERB(...)`. + */ + +methods.forEach(function(method){ + app[method] = function(path){ + if ('get' == method && 1 == arguments.length) return this.set(path); + + // deprecated + if (Array.isArray(path)) { + console.trace('passing an array to app.VERB() is deprecated and will be removed in 4.0'); + } + + // if no router attached yet, attach the router + if (!this._usedRouter) this.use(this.router); + + // setup route + this._router[method].apply(this._router, arguments); + return this; + }; +}); + +/** + * Special-cased "all" method, applying the given route `path`, + * middleware, and callback to _every_ HTTP method. + * + * @param {String} path + * @param {Function} ... + * @return {app} for chaining + * @api public + */ + +app.all = function(path){ + var args = arguments; + methods.forEach(function(method){ + app[method].apply(this, args); + }, this); + return this; +}; + +// del -> delete alias + +app.del = app.delete; + +/** + * Render the given view `name` name with `options` + * and a callback accepting an error and the + * rendered template string. + * + * Example: + * + * app.render('email', { name: 'Tobi' }, function(err, html){ + * // ... + * }) + * + * @param {String} name + * @param {String|Function} options or fn + * @param {Function} fn + * @api public + */ + +app.render = function(name, options, fn){ + var opts = {} + , cache = this.cache + , engines = this.engines + , view; + + // support callback function as second arg + if ('function' == typeof options) { + fn = options, options = {}; + } + + // merge app.locals + utils.merge(opts, this.locals); + + // merge options._locals + if (options._locals) utils.merge(opts, options._locals); + + // merge options + utils.merge(opts, options); + + // set .cache unless explicitly provided + opts.cache = null == opts.cache + ? this.enabled('view cache') + : opts.cache; + + // primed cache + if (opts.cache) view = cache[name]; + + // view + if (!view) { + view = new (this.get('view'))(name, { + defaultEngine: this.get('view engine'), + root: this.get('views'), + engines: engines + }); + + if (!view.path) { + var err = new Error('Failed to lookup view "' + name + '"'); + err.view = view; + return fn(err); + } + + // prime the cache + if (opts.cache) cache[name] = view; + } + + // render + try { + view.render(opts, fn); + } catch (err) { + fn(err); + } +}; + +/** + * Listen for connections. + * + * A node `http.Server` is returned, with this + * application (which is a `Function`) as its + * callback. If you wish to create both an HTTP + * and HTTPS server you may do so with the "http" + * and "https" modules as shown here: + * + * var http = require('http') + * , https = require('https') + * , express = require('express') + * , app = express(); + * + * http.createServer(app).listen(80); + * https.createServer({ ... }, app).listen(443); + * + * @return {http.Server} + * @api public + */ + +app.listen = function(){ + var server = http.createServer(this); + return server.listen.apply(server, arguments); +}; diff --git a/server/node_modules/express/lib/express.js b/server/node_modules/express/lib/express.js new file mode 100644 index 0000000..c204849 --- /dev/null +++ b/server/node_modules/express/lib/express.js @@ -0,0 +1,86 @@ +/** + * Module dependencies. + */ + +var connect = require('connect') + , proto = require('./application') + , Route = require('./router/route') + , Router = require('./router') + , req = require('./request') + , res = require('./response') + , utils = connect.utils; + +/** + * Expose `createApplication()`. + */ + +exports = module.exports = createApplication; + +/** + * Expose mime. + */ + +exports.mime = connect.mime; + +/** + * Create an express application. + * + * @return {Function} + * @api public + */ + +function createApplication() { + var app = connect(); + utils.merge(app, proto); + app.request = { __proto__: req, app: app }; + app.response = { __proto__: res, app: app }; + app.init(); + return app; +} + +/** + * Expose connect.middleware as express.* + * for example `express.logger` etc. + */ + +for (var key in connect.middleware) { + Object.defineProperty( + exports + , key + , Object.getOwnPropertyDescriptor(connect.middleware, key)); +} + +/** + * Error on createServer(). + */ + +exports.createServer = function(){ + console.warn('Warning: express.createServer() is deprecated, express'); + console.warn('applications no longer inherit from http.Server,'); + console.warn('please use:'); + console.warn(''); + console.warn(' var express = require("express");'); + console.warn(' var app = express();'); + console.warn(''); + return createApplication(); +}; + +/** + * Expose the prototypes. + */ + +exports.application = proto; +exports.request = req; +exports.response = res; + +/** + * Expose constructors. + */ + +exports.Route = Route; +exports.Router = Router; + +// Error handler title + +exports.errorHandler.title = 'Express'; + diff --git a/server/node_modules/express/lib/middleware.js b/server/node_modules/express/lib/middleware.js new file mode 100644 index 0000000..e07dd4c --- /dev/null +++ b/server/node_modules/express/lib/middleware.js @@ -0,0 +1,32 @@ + +/** + * Module dependencies. + */ + +var utils = require('./utils'); + +/** + * Initialization middleware, exposing the + * request and response to eachother, as well + * as defaulting the X-Powered-By header field. + * + * @param {Function} app + * @return {Function} + * @api private + */ + +exports.init = function(app){ + return function expressInit(req, res, next){ + if (app.enabled('x-powered-by')) res.setHeader('X-Powered-By', 'Express'); + req.res = res; + res.req = req; + req.next = next; + + req.__proto__ = app.request; + res.__proto__ = app.response; + + res.locals = res.locals || utils.locals(res); + + next(); + } +}; diff --git a/server/node_modules/express/lib/request.js b/server/node_modules/express/lib/request.js new file mode 100644 index 0000000..3d41617 --- /dev/null +++ b/server/node_modules/express/lib/request.js @@ -0,0 +1,529 @@ + +/** + * Module dependencies. + */ + +var http = require('http') + , utils = require('./utils') + , connect = require('connect') + , fresh = require('fresh') + , parseRange = require('range-parser') + , parse = connect.utils.parseUrl + , mime = connect.mime; + +/** + * Request prototype. + */ + +var req = exports = module.exports = { + __proto__: http.IncomingMessage.prototype +}; + +/** + * Return request header. + * + * The `Referrer` header field is special-cased, + * both `Referrer` and `Referer` are interchangeable. + * + * Examples: + * + * req.get('Content-Type'); + * // => "text/plain" + * + * req.get('content-type'); + * // => "text/plain" + * + * req.get('Something'); + * // => undefined + * + * Aliased as `req.header()`. + * + * @param {String} name + * @return {String} + * @api public + */ + +req.get = +req.header = function(name){ + switch (name = name.toLowerCase()) { + case 'referer': + case 'referrer': + return this.headers.referrer + || this.headers.referer; + default: + return this.headers[name]; + } +}; + +/** + * Check if the given `type(s)` is acceptable, returning + * the best match when true, otherwise `undefined`, in which + * case you should respond with 406 "Not Acceptable". + * + * The `type` value may be a single mime type string + * such as "application/json", the extension name + * such as "json", a comma-delimted list such as "json, html, text/plain", + * an argument list such as `"json", "html", "text/plain"`, + * or an array `["json", "html", "text/plain"]`. When a list + * or array is given the _best_ match, if any is returned. + * + * Examples: + * + * // Accept: text/html + * req.accepts('html'); + * // => "html" + * + * // Accept: text/*, application/json + * req.accepts('html'); + * // => "html" + * req.accepts('text/html'); + * // => "text/html" + * req.accepts('json, text'); + * // => "json" + * req.accepts('application/json'); + * // => "application/json" + * + * // Accept: text/*, application/json + * req.accepts('image/png'); + * req.accepts('png'); + * // => undefined + * + * // Accept: text/*;q=.5, application/json + * req.accepts(['html', 'json']); + * req.accepts('html', 'json'); + * req.accepts('html, json'); + * // => "json" + * + * @param {String|Array} type(s) + * @return {String} + * @api public + */ + +req.accepts = function(type){ + var args = arguments.length > 1 ? [].slice.apply(arguments) : type; + return utils.accepts(args, this.get('Accept')); +}; + +/** + * Check if the given `encoding` is accepted. + * + * @param {String} encoding + * @return {Boolean} + * @api public + */ + +req.acceptsEncoding = function(encoding){ + return !! ~this.acceptedEncodings.indexOf(encoding); +}; + +/** + * Check if the given `charset` is acceptable, + * otherwise you should respond with 406 "Not Acceptable". + * + * @param {String} charset + * @return {Boolean} + * @api public + */ + +req.acceptsCharset = function(charset){ + var accepted = this.acceptedCharsets; + return accepted.length + ? !! ~accepted.indexOf(charset) + : true; +}; + +/** + * Check if the given `lang` is acceptable, + * otherwise you should respond with 406 "Not Acceptable". + * + * @param {String} lang + * @return {Boolean} + * @api public + */ + +req.acceptsLanguage = function(lang){ + var accepted = this.acceptedLanguages; + return accepted.length + ? !! ~accepted.indexOf(lang) + : true; +}; + +/** + * Parse Range header field, + * capping to the given `size`. + * + * Unspecified ranges such as "0-" require + * knowledge of your resource length. In + * the case of a byte range this is of course + * the total number of bytes. If the Range + * header field is not given `null` is returned, + * `-1` when unsatisfiable, `-2` when syntactically invalid. + * + * NOTE: remember that ranges are inclusive, so + * for example "Range: users=0-3" should respond + * with 4 users when available, not 3. + * + * @param {Number} size + * @return {Array} + * @api public + */ + +req.range = function(size){ + var range = this.get('Range'); + if (!range) return; + return parseRange(size, range); +}; + +/** + * Return an array of encodings. + * + * Examples: + * + * ['gzip', 'deflate'] + * + * @return {Array} + * @api public + */ + +req.__defineGetter__('acceptedEncodings', function(){ + var accept = this.get('Accept-Encoding'); + return accept + ? accept.trim().split(/ *, */) + : []; +}); + +/** + * Return an array of Accepted media types + * ordered from highest quality to lowest. + * + * Examples: + * + * [ { value: 'application/json', + * quality: 1, + * type: 'application', + * subtype: 'json' }, + * { value: 'text/html', + * quality: 0.5, + * type: 'text', + * subtype: 'html' } ] + * + * @return {Array} + * @api public + */ + +req.__defineGetter__('accepted', function(){ + var accept = this.get('Accept'); + return accept + ? utils.parseAccept(accept) + : []; +}); + +/** + * Return an array of Accepted languages + * ordered from highest quality to lowest. + * + * Examples: + * + * Accept-Language: en;q=.5, en-us + * ['en-us', 'en'] + * + * @return {Array} + * @api public + */ + +req.__defineGetter__('acceptedLanguages', function(){ + var accept = this.get('Accept-Language'); + return accept + ? utils + .parseParams(accept) + .map(function(obj){ + return obj.value; + }) + : []; +}); + +/** + * Return an array of Accepted charsets + * ordered from highest quality to lowest. + * + * Examples: + * + * Accept-Charset: iso-8859-5;q=.2, unicode-1-1;q=0.8 + * ['unicode-1-1', 'iso-8859-5'] + * + * @return {Array} + * @api public + */ + +req.__defineGetter__('acceptedCharsets', function(){ + var accept = this.get('Accept-Charset'); + return accept + ? utils + .parseParams(accept) + .map(function(obj){ + return obj.value; + }) + : []; +}); + +/** + * Return the value of param `name` when present or `defaultValue`. + * + * - Checks route placeholders, ex: _/user/:id_ + * - Checks body params, ex: id=12, {"id":12} + * - Checks query string params, ex: ?id=12 + * + * To utilize request bodies, `req.body` + * should be an object. This can be done by using + * the `connect.bodyParser()` middleware. + * + * @param {String} name + * @param {Mixed} [defaultValue] + * @return {String} + * @api public + */ + +req.param = function(name, defaultValue){ + var params = this.params || {}; + var body = this.body || {}; + var query = this.query || {}; + if (null != params[name] && params.hasOwnProperty(name)) return params[name]; + if (null != body[name]) return body[name]; + if (null != query[name]) return query[name]; + return defaultValue; +}; + +/** + * Check if the incoming request contains the "Content-Type" + * header field, and it contains the give mime `type`. + * + * Examples: + * + * // With Content-Type: text/html; charset=utf-8 + * req.is('html'); + * req.is('text/html'); + * req.is('text/*'); + * // => true + * + * // When Content-Type is application/json + * req.is('json'); + * req.is('application/json'); + * req.is('application/*'); + * // => true + * + * req.is('html'); + * // => false + * + * @param {String} type + * @return {Boolean} + * @api public + */ + +req.is = function(type){ + var ct = this.get('Content-Type'); + if (!ct) return false; + ct = ct.split(';')[0]; + if (!~type.indexOf('/')) type = mime.lookup(type); + if (~type.indexOf('*')) { + type = type.split('/'); + ct = ct.split('/'); + if ('*' == type[0] && type[1] == ct[1]) return true; + if ('*' == type[1] && type[0] == ct[0]) return true; + return false; + } + return !! ~ct.indexOf(type); +}; + +/** + * Return the protocol string "http" or "https" + * when requested with TLS. When the "trust proxy" + * setting is enabled the "X-Forwarded-Proto" header + * field will be trusted. If you're running behind + * a reverse proxy that supplies https for you this + * may be enabled. + * + * @return {String} + * @api public + */ + +req.__defineGetter__('protocol', function(){ + var trustProxy = this.app.get('trust proxy'); + if (this.connection.encrypted) return 'https'; + if (!trustProxy) return 'http'; + var proto = this.get('X-Forwarded-Proto') || 'http'; + return proto.split(/\s*,\s*/)[0]; +}); + +/** + * Short-hand for: + * + * req.protocol == 'https' + * + * @return {Boolean} + * @api public + */ + +req.__defineGetter__('secure', function(){ + return 'https' == this.protocol; +}); + +/** + * Return the remote address, or when + * "trust proxy" is `true` return + * the upstream addr. + * + * @return {String} + * @api public + */ + +req.__defineGetter__('ip', function(){ + return this.ips[0] || this.connection.remoteAddress; +}); + +/** + * When "trust proxy" is `true`, parse + * the "X-Forwarded-For" ip address list. + * + * For example if the value were "client, proxy1, proxy2" + * you would receive the array `["client", "proxy1", "proxy2"]` + * where "proxy2" is the furthest down-stream. + * + * @return {Array} + * @api public + */ + +req.__defineGetter__('ips', function(){ + var trustProxy = this.app.get('trust proxy'); + var val = this.get('X-Forwarded-For'); + return trustProxy && val + ? val.split(/ *, */) + : []; +}); + +/** + * Return basic auth credentials. + * + * Examples: + * + * // http://tobi:hello@example.com + * req.auth + * // => { username: 'tobi', password: 'hello' } + * + * @return {Object} or undefined + * @api public + */ + +req.__defineGetter__('auth', function(){ + // missing + var auth = this.get('Authorization'); + if (!auth) return; + + // malformed + var parts = auth.split(' '); + if ('basic' != parts[0].toLowerCase()) return; + if (!parts[1]) return; + auth = parts[1]; + + // credentials + auth = new Buffer(auth, 'base64').toString().match(/^([^:]*):(.*)$/); + if (!auth) return; + return { username: auth[1], password: auth[2] }; +}); + +/** + * Return subdomains as an array. + * + * Subdomains are the dot-separated parts of the host before the main domain of + * the app. By default, the domain of the app is assumed to be the last two + * parts of the host. This can be changed by setting "subdomain offset". + * + * For example, if the domain is "tobi.ferrets.example.com": + * If "subdomain offset" is not set, req.subdomains is `["ferrets", "tobi"]`. + * If "subdomain offset" is 3, req.subdomains is `["tobi"]`. + * + * @return {Array} + * @api public + */ + +req.__defineGetter__('subdomains', function(){ + var offset = this.app.get('subdomain offset'); + return (this.host || '') + .split('.') + .reverse() + .slice(offset); +}); + +/** + * Short-hand for `url.parse(req.url).pathname`. + * + * @return {String} + * @api public + */ + +req.__defineGetter__('path', function(){ + return parse(this).pathname; +}); + +/** + * Parse the "Host" header field hostname. + * + * @return {String} + * @api public + */ + +req.__defineGetter__('host', function(){ + var trustProxy = this.app.get('trust proxy'); + var host = trustProxy && this.get('X-Forwarded-Host'); + host = host || this.get('Host'); + if (!host) return; + return host.split(':')[0]; +}); + +/** + * Check if the request is fresh, aka + * Last-Modified and/or the ETag + * still match. + * + * @return {Boolean} + * @api public + */ + +req.__defineGetter__('fresh', function(){ + var method = this.method; + var s = this.res.statusCode; + + // GET or HEAD for weak freshness validation only + if ('GET' != method && 'HEAD' != method) return false; + + // 2xx or 304 as per rfc2616 14.26 + if ((s >= 200 && s < 300) || 304 == s) { + return fresh(this.headers, this.res._headers); + } + + return false; +}); + +/** + * Check if the request is stale, aka + * "Last-Modified" and / or the "ETag" for the + * resource has changed. + * + * @return {Boolean} + * @api public + */ + +req.__defineGetter__('stale', function(){ + return !this.fresh; +}); + +/** + * Check if the request was an _XMLHttpRequest_. + * + * @return {Boolean} + * @api public + */ + +req.__defineGetter__('xhr', function(){ + var val = this.get('X-Requested-With') || ''; + return 'xmlhttprequest' == val.toLowerCase(); +}); diff --git a/server/node_modules/express/lib/response.js b/server/node_modules/express/lib/response.js new file mode 100644 index 0000000..b0816b3 --- /dev/null +++ b/server/node_modules/express/lib/response.js @@ -0,0 +1,802 @@ +/** + * Module dependencies. + */ + +var http = require('http') + , path = require('path') + , connect = require('connect') + , utils = connect.utils + , sign = require('cookie-signature').sign + , normalizeType = require('./utils').normalizeType + , normalizeTypes = require('./utils').normalizeTypes + , etag = require('./utils').etag + , statusCodes = http.STATUS_CODES + , cookie = require('cookie') + , send = require('send') + , mime = connect.mime + , basename = path.basename + , extname = path.extname; + +/** + * Response prototype. + */ + +var res = module.exports = { + __proto__: http.ServerResponse.prototype +}; + +/** + * Set status `code`. + * + * @param {Number} code + * @return {ServerResponse} + * @api public + */ + +res.status = function(code){ + this.statusCode = code; + return this; +}; + +/** + * Set Link header field with the given `links`. + * + * Examples: + * + * res.links({ + * next: 'http://api.example.com/users?page=2', + * last: 'http://api.example.com/users?page=5' + * }); + * + * @param {Object} links + * @return {ServerResponse} + * @api public + */ + +res.links = function(links){ + var link = this.get('Link') || ''; + if (link) link += ', '; + return this.set('Link', link + Object.keys(links).map(function(rel){ + return '<' + links[rel] + '>; rel="' + rel + '"'; + }).join(', ')); +}; + +/** + * Send a response. + * + * Examples: + * + * res.send(new Buffer('wahoo')); + * res.send({ some: 'json' }); + * res.send('

some html

'); + * res.send(404, 'Sorry, cant find that'); + * res.send(404); + * + * @param {Mixed} body or status + * @param {Mixed} body + * @return {ServerResponse} + * @api public + */ + +res.send = function(body){ + var req = this.req; + var head = 'HEAD' == req.method; + var len; + + // settings + var app = this.app; + + // allow status / body + if (2 == arguments.length) { + // res.send(body, status) backwards compat + if ('number' != typeof body && 'number' == typeof arguments[1]) { + this.statusCode = arguments[1]; + } else { + this.statusCode = body; + body = arguments[1]; + } + } + + switch (typeof body) { + // response status + case 'number': + this.get('Content-Type') || this.type('txt'); + this.statusCode = body; + body = http.STATUS_CODES[body]; + break; + // string defaulting to html + case 'string': + if (!this.get('Content-Type')) { + this.charset = this.charset || 'utf-8'; + this.type('html'); + } + break; + case 'boolean': + case 'object': + if (null == body) { + body = ''; + } else if (Buffer.isBuffer(body)) { + this.get('Content-Type') || this.type('bin'); + } else { + return this.json(body); + } + break; + } + + // populate Content-Length + if (undefined !== body && !this.get('Content-Length')) { + this.set('Content-Length', len = Buffer.isBuffer(body) + ? body.length + : Buffer.byteLength(body)); + } + + // ETag support + // TODO: W/ support + if (app.settings.etag && len > 1024 && 'GET' == req.method) { + if (!this.get('ETag')) { + this.set('ETag', etag(body)); + } + } + + // freshness + if (req.fresh) this.statusCode = 304; + + // strip irrelevant headers + if (204 == this.statusCode || 304 == this.statusCode) { + this.removeHeader('Content-Type'); + this.removeHeader('Content-Length'); + this.removeHeader('Transfer-Encoding'); + body = ''; + } + + // respond + this.end(head ? null : body); + return this; +}; + +/** + * Send JSON response. + * + * Examples: + * + * res.json(null); + * res.json({ user: 'tj' }); + * res.json(500, 'oh noes!'); + * res.json(404, 'I dont have that'); + * + * @param {Mixed} obj or status + * @param {Mixed} obj + * @return {ServerResponse} + * @api public + */ + +res.json = function(obj){ + // allow status / body + if (2 == arguments.length) { + // res.json(body, status) backwards compat + if ('number' == typeof arguments[1]) { + this.statusCode = arguments[1]; + } else { + this.statusCode = obj; + obj = arguments[1]; + } + } + + // settings + var app = this.app; + var replacer = app.get('json replacer'); + var spaces = app.get('json spaces'); + var body = JSON.stringify(obj, replacer, spaces); + + // content-type + this.charset = this.charset || 'utf-8'; + this.get('Content-Type') || this.set('Content-Type', 'application/json'); + + return this.send(body); +}; + +/** + * Send JSON response with JSONP callback support. + * + * Examples: + * + * res.jsonp(null); + * res.jsonp({ user: 'tj' }); + * res.jsonp(500, 'oh noes!'); + * res.jsonp(404, 'I dont have that'); + * + * @param {Mixed} obj or status + * @param {Mixed} obj + * @return {ServerResponse} + * @api public + */ + +res.jsonp = function(obj){ + // allow status / body + if (2 == arguments.length) { + // res.json(body, status) backwards compat + if ('number' == typeof arguments[1]) { + this.statusCode = arguments[1]; + } else { + this.statusCode = obj; + obj = arguments[1]; + } + } + + // settings + var app = this.app; + var replacer = app.get('json replacer'); + var spaces = app.get('json spaces'); + var body = JSON.stringify(obj, replacer, spaces) + .replace(/\u2028/g, '\\u2028') + .replace(/\u2029/g, '\\u2029'); + var callback = this.req.query[app.get('jsonp callback name')]; + + // content-type + this.charset = this.charset || 'utf-8'; + this.set('Content-Type', 'application/json'); + + // jsonp + if (callback) { + if (Array.isArray(callback)) callback = callback[0]; + this.set('Content-Type', 'text/javascript'); + var cb = callback.replace(/[^\[\]\w$.]/g, ''); + body = 'typeof ' + cb + ' === \'function\' && ' + cb + '(' + body + ');'; + } + + return this.send(body); +}; + +/** + * Transfer the file at the given `path`. + * + * Automatically sets the _Content-Type_ response header field. + * The callback `fn(err)` is invoked when the transfer is complete + * or when an error occurs. Be sure to check `res.sentHeader` + * if you wish to attempt responding, as the header and some data + * may have already been transferred. + * + * Options: + * + * - `maxAge` defaulting to 0 + * - `root` root directory for relative filenames + * + * Examples: + * + * The following example illustrates how `res.sendfile()` may + * be used as an alternative for the `static()` middleware for + * dynamic situations. The code backing `res.sendfile()` is actually + * the same code, so HTTP cache support etc is identical. + * + * app.get('/user/:uid/photos/:file', function(req, res){ + * var uid = req.params.uid + * , file = req.params.file; + * + * req.user.mayViewFilesFrom(uid, function(yes){ + * if (yes) { + * res.sendfile('/uploads/' + uid + '/' + file); + * } else { + * res.send(403, 'Sorry! you cant see that.'); + * } + * }); + * }); + * + * @param {String} path + * @param {Object|Function} options or fn + * @param {Function} fn + * @api public + */ + +res.sendfile = function(path, options, fn){ + var self = this + , req = self.req + , next = this.req.next + , options = options || {} + , done; + + // support function as second arg + if ('function' == typeof options) { + fn = options; + options = {}; + } + + // socket errors + req.socket.on('error', error); + + // errors + function error(err) { + if (done) return; + done = true; + + // clean up + cleanup(); + if (!self.headerSent) self.removeHeader('Content-Disposition'); + + // callback available + if (fn) return fn(err); + + // list in limbo if there's no callback + if (self.headerSent) return; + + // delegate + next(err); + } + + // streaming + function stream(stream) { + if (done) return; + cleanup(); + if (fn) stream.on('end', fn); + } + + // cleanup + function cleanup() { + req.socket.removeListener('error', error); + } + + // transfer + var file = send(req, path); + if (options.root) file.root(options.root); + file.maxage(options.maxAge || 0); + file.on('error', error); + file.on('directory', next); + file.on('stream', stream); + file.pipe(this); + this.on('finish', cleanup); +}; + +/** + * Transfer the file at the given `path` as an attachment. + * + * Optionally providing an alternate attachment `filename`, + * and optional callback `fn(err)`. The callback is invoked + * when the data transfer is complete, or when an error has + * ocurred. Be sure to check `res.headerSent` if you plan to respond. + * + * This method uses `res.sendfile()`. + * + * @param {String} path + * @param {String|Function} filename or fn + * @param {Function} fn + * @api public + */ + +res.download = function(path, filename, fn){ + // support function as second arg + if ('function' == typeof filename) { + fn = filename; + filename = null; + } + + filename = filename || path; + this.set('Content-Disposition', 'attachment; filename="' + basename(filename) + '"'); + return this.sendfile(path, fn); +}; + +/** + * Set _Content-Type_ response header with `type` through `mime.lookup()` + * when it does not contain "/", or set the Content-Type to `type` otherwise. + * + * Examples: + * + * res.type('.html'); + * res.type('html'); + * res.type('json'); + * res.type('application/json'); + * res.type('png'); + * + * @param {String} type + * @return {ServerResponse} for chaining + * @api public + */ + +res.contentType = +res.type = function(type){ + return this.set('Content-Type', ~type.indexOf('/') + ? type + : mime.lookup(type)); +}; + +/** + * Respond to the Acceptable formats using an `obj` + * of mime-type callbacks. + * + * This method uses `req.accepted`, an array of + * acceptable types ordered by their quality values. + * When "Accept" is not present the _first_ callback + * is invoked, otherwise the first match is used. When + * no match is performed the server responds with + * 406 "Not Acceptable". + * + * Content-Type is set for you, however if you choose + * you may alter this within the callback using `res.type()` + * or `res.set('Content-Type', ...)`. + * + * res.format({ + * 'text/plain': function(){ + * res.send('hey'); + * }, + * + * 'text/html': function(){ + * res.send('

hey

'); + * }, + * + * 'appliation/json': function(){ + * res.send({ message: 'hey' }); + * } + * }); + * + * In addition to canonicalized MIME types you may + * also use extnames mapped to these types: + * + * res.format({ + * text: function(){ + * res.send('hey'); + * }, + * + * html: function(){ + * res.send('

hey

'); + * }, + * + * json: function(){ + * res.send({ message: 'hey' }); + * } + * }); + * + * By default Express passes an `Error` + * with a `.status` of 406 to `next(err)` + * if a match is not made. If you provide + * a `.default` callback it will be invoked + * instead. + * + * @param {Object} obj + * @return {ServerResponse} for chaining + * @api public + */ + +res.format = function(obj){ + var req = this.req + , next = req.next; + + var fn = obj.default; + if (fn) delete obj.default; + var keys = Object.keys(obj); + + var key = req.accepts(keys); + + this.vary("Accept"); + + if (key) { + var type = normalizeType(key).value; + var charset = mime.charsets.lookup(type); + if (charset) type += '; charset=' + charset; + this.set('Content-Type', type); + obj[key](req, this, next); + } else if (fn) { + fn(); + } else { + var err = new Error('Not Acceptable'); + err.status = 406; + err.types = normalizeTypes(keys).map(function(o){ return o.value }); + next(err); + } + + return this; +}; + +/** + * Set _Content-Disposition_ header to _attachment_ with optional `filename`. + * + * @param {String} filename + * @return {ServerResponse} + * @api public + */ + +res.attachment = function(filename){ + if (filename) this.type(extname(filename)); + this.set('Content-Disposition', filename + ? 'attachment; filename="' + basename(filename) + '"' + : 'attachment'); + return this; +}; + +/** + * Set header `field` to `val`, or pass + * an object of header fields. + * + * Examples: + * + * res.set('Foo', ['bar', 'baz']); + * res.set('Accept', 'application/json'); + * res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' }); + * + * Aliased as `res.header()`. + * + * @param {String|Object|Array} field + * @param {String} val + * @return {ServerResponse} for chaining + * @api public + */ + +res.set = +res.header = function(field, val){ + if (2 == arguments.length) { + if (Array.isArray(val)) val = val.map(String); + else val = String(val); + this.setHeader(field, val); + } else { + for (var key in field) { + this.set(key, field[key]); + } + } + return this; +}; + +/** + * Get value for header `field`. + * + * @param {String} field + * @return {String} + * @api public + */ + +res.get = function(field){ + return this.getHeader(field); +}; + +/** + * Clear cookie `name`. + * + * @param {String} name + * @param {Object} options + * @param {ServerResponse} for chaining + * @api public + */ + +res.clearCookie = function(name, options){ + var opts = { expires: new Date(1), path: '/' }; + return this.cookie(name, '', options + ? utils.merge(opts, options) + : opts); +}; + +/** + * Set cookie `name` to `val`, with the given `options`. + * + * Options: + * + * - `maxAge` max-age in milliseconds, converted to `expires` + * - `signed` sign the cookie + * - `path` defaults to "/" + * + * Examples: + * + * // "Remember Me" for 15 minutes + * res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true }); + * + * // save as above + * res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true }) + * + * @param {String} name + * @param {String|Object} val + * @param {Options} options + * @api public + */ + +res.cookie = function(name, val, options){ + options = utils.merge({}, options); + var secret = this.req.secret; + var signed = options.signed; + if (signed && !secret) throw new Error('connect.cookieParser("secret") required for signed cookies'); + if ('number' == typeof val) val = val.toString(); + if ('object' == typeof val) val = 'j:' + JSON.stringify(val); + if (signed) val = 's:' + sign(val, secret); + if ('maxAge' in options) { + options.expires = new Date(Date.now() + options.maxAge); + options.maxAge /= 1000; + } + if (null == options.path) options.path = '/'; + this.set('Set-Cookie', cookie.serialize(name, String(val), options)); + return this; +}; + + +/** + * Set the location header to `url`. + * + * The given `url` can also be the name of a mapped url, for + * example by default express supports "back" which redirects + * to the _Referrer_ or _Referer_ headers or "/". + * + * Examples: + * + * res.location('/foo/bar').; + * res.location('http://example.com'); + * res.location('../login'); // /blog/post/1 -> /blog/login + * + * Mounting: + * + * When an application is mounted and `res.location()` + * is given a path that does _not_ lead with "/" it becomes + * relative to the mount-point. For example if the application + * is mounted at "/blog", the following would become "/blog/login". + * + * res.location('login'); + * + * While the leading slash would result in a location of "/login": + * + * res.location('/login'); + * + * @param {String} url + * @api public + */ + +res.location = function(url){ + var app = this.app + , req = this.req; + + // setup redirect map + var map = { back: req.get('Referrer') || '/' }; + + // perform redirect + url = map[url] || url; + + // relative + if (!~url.indexOf('://') && 0 != url.indexOf('//')) { + var path + + // relative to path + if ('.' == url[0]) { + path = req.originalUrl.split('?')[0] + url = path + ('/' == path[path.length - 1] ? '' : '/') + url; + // relative to mount-point + } else if ('/' != url[0]) { + path = app.path(); + url = path + '/' + url; + } + } + + // Respond + this.set('Location', url); + return this; +}; + +/** + * Redirect to the given `url` with optional response `status` + * defaulting to 302. + * + * The resulting `url` is determined by `res.location()`, so + * it will play nicely with mounted apps, relative paths, + * `"back"` etc. + * + * Examples: + * + * res.redirect('/foo/bar'); + * res.redirect('http://example.com'); + * res.redirect(301, 'http://example.com'); + * res.redirect('http://example.com', 301); + * res.redirect('../login'); // /blog/post/1 -> /blog/login + * + * @param {String} url + * @param {Number} code + * @api public + */ + +res.redirect = function(url){ + var head = 'HEAD' == this.req.method + , status = 302 + , body; + + // allow status / url + if (2 == arguments.length) { + if ('number' == typeof url) { + status = url; + url = arguments[1]; + } else { + status = arguments[1]; + } + } + + // Set location header + this.location(url); + url = this.get('Location'); + + // Support text/{plain,html} by default + this.format({ + text: function(){ + body = statusCodes[status] + '. Redirecting to ' + encodeURI(url); + }, + + html: function(){ + var u = utils.escape(url); + body = '

' + statusCodes[status] + '. Redirecting to ' + u + '

'; + }, + + default: function(){ + body = ''; + } + }); + + // Respond + this.statusCode = status; + this.set('Content-Length', Buffer.byteLength(body)); + this.end(head ? null : body); +}; + +/** + * Add `field` to Vary. If already present in the Vary set, then + * this call is simply ignored. + * + * @param {Array|String} field + * @param {ServerResponse} for chaining + * @api public + */ + +res.vary = function(field){ + var self = this; + + // nothing + if (!field) return this; + + // array + if (Array.isArray(field)) { + field.forEach(function(field){ + self.vary(field); + }); + return; + } + + var vary = this.get('Vary'); + + // append + if (vary) { + vary = vary.split(/ *, */); + if (!~vary.indexOf(field)) vary.push(field); + this.set('Vary', vary.join(', ')); + return this; + } + + // set + this.set('Vary', field); + return this; +}; + +/** + * Render `view` with the given `options` and optional callback `fn`. + * When a callback function is given a response will _not_ be made + * automatically, otherwise a response of _200_ and _text/html_ is given. + * + * Options: + * + * - `cache` boolean hinting to the engine it should cache + * - `filename` filename of the view being rendered + * + * @param {String} view + * @param {Object|Function} options or callback function + * @param {Function} fn + * @api public + */ + +res.render = function(view, options, fn){ + var self = this + , options = options || {} + , req = this.req + , app = req.app; + + // support callback function as second arg + if ('function' == typeof options) { + fn = options, options = {}; + } + + // merge res.locals + options._locals = self.locals; + + // default callback to respond + fn = fn || function(err, str){ + if (err) return req.next(err); + self.send(str); + }; + + // render + app.render(view, options, fn); +}; diff --git a/server/node_modules/express/lib/router/index.js b/server/node_modules/express/lib/router/index.js new file mode 100644 index 0000000..12f112d --- /dev/null +++ b/server/node_modules/express/lib/router/index.js @@ -0,0 +1,311 @@ +/** + * Module dependencies. + */ + +var Route = require('./route') + , utils = require('../utils') + , methods = require('methods') + , debug = require('debug')('express:router') + , parse = require('connect').utils.parseUrl; + +/** + * Expose `Router` constructor. + */ + +exports = module.exports = Router; + +/** + * Initialize a new `Router` with the given `options`. + * + * @param {Object} options + * @api private + */ + +function Router(options) { + options = options || {}; + var self = this; + this.map = {}; + this.params = {}; + this._params = []; + this.caseSensitive = options.caseSensitive; + this.strict = options.strict; + this.middleware = function router(req, res, next){ + self._dispatch(req, res, next); + }; +} + +/** + * Register a param callback `fn` for the given `name`. + * + * @param {String|Function} name + * @param {Function} fn + * @return {Router} for chaining + * @api public + */ + +Router.prototype.param = function(name, fn){ + // param logic + if ('function' == typeof name) { + this._params.push(name); + return; + } + + // apply param functions + var params = this._params + , len = params.length + , ret; + + for (var i = 0; i < len; ++i) { + if (ret = params[i](name, fn)) { + fn = ret; + } + } + + // ensure we end up with a + // middleware function + if ('function' != typeof fn) { + throw new Error('invalid param() call for ' + name + ', got ' + fn); + } + + (this.params[name] = this.params[name] || []).push(fn); + return this; +}; + +/** + * Route dispatcher aka the route "middleware". + * + * @param {IncomingMessage} req + * @param {ServerResponse} res + * @param {Function} next + * @api private + */ + +Router.prototype._dispatch = function(req, res, next){ + var params = this.params + , self = this; + + debug('dispatching %s %s (%s)', req.method, req.url, req.originalUrl); + + // route dispatch + (function pass(i, err){ + var paramCallbacks + , paramIndex = 0 + , paramVal + , route + , keys + , key; + + // match next route + function nextRoute(err) { + pass(req._route_index + 1, err); + } + + // match route + req.route = route = self.matchRequest(req, i); + + // implied OPTIONS + if (!route && 'OPTIONS' == req.method) return self._options(req, res); + + // no route + if (!route) return next(err); + debug('matched %s %s', route.method, route.path); + + // we have a route + // start at param 0 + req.params = route.params; + keys = route.keys; + i = 0; + + // param callbacks + function param(err) { + paramIndex = 0; + key = keys[i++]; + paramVal = key && req.params[key.name]; + paramCallbacks = key && params[key.name]; + + try { + if ('route' == err) { + nextRoute(); + } else if (err) { + i = 0; + callbacks(err); + } else if (paramCallbacks && undefined !== paramVal) { + paramCallback(); + } else if (key) { + param(); + } else { + i = 0; + callbacks(); + } + } catch (err) { + param(err); + } + }; + + param(err); + + // single param callbacks + function paramCallback(err) { + var fn = paramCallbacks[paramIndex++]; + if (err || !fn) return param(err); + fn(req, res, paramCallback, paramVal, key.name); + } + + // invoke route callbacks + function callbacks(err) { + var fn = route.callbacks[i++]; + try { + if ('route' == err) { + nextRoute(); + } else if (err && fn) { + if (fn.length < 4) return callbacks(err); + fn(err, req, res, callbacks); + } else if (fn) { + if (fn.length < 4) return fn(req, res, callbacks); + callbacks(); + } else { + nextRoute(err); + } + } catch (err) { + callbacks(err); + } + } + })(0); +}; + +/** + * Respond to __OPTIONS__ method. + * + * @param {IncomingMessage} req + * @param {ServerResponse} res + * @api private + */ + +Router.prototype._options = function(req, res){ + var path = parse(req).pathname + , body = this._optionsFor(path).join(','); + res.set('Allow', body).send(body); +}; + +/** + * Return an array of HTTP verbs or "options" for `path`. + * + * @param {String} path + * @return {Array} + * @api private + */ + +Router.prototype._optionsFor = function(path){ + var self = this; + return methods.filter(function(method){ + var routes = self.map[method]; + if (!routes || 'options' == method) return; + for (var i = 0, len = routes.length; i < len; ++i) { + if (routes[i].match(path)) return true; + } + }).map(function(method){ + return method.toUpperCase(); + }); +}; + +/** + * Attempt to match a route for `req` + * with optional starting index of `i` + * defaulting to 0. + * + * @param {IncomingMessage} req + * @param {Number} i + * @return {Route} + * @api private + */ + +Router.prototype.matchRequest = function(req, i, head){ + var method = req.method.toLowerCase() + , url = parse(req) + , path = url.pathname + , routes = this.map + , i = i || 0 + , route; + + // HEAD support + if (!head && 'head' == method) { + route = this.matchRequest(req, i, true); + if (route) return route; + method = 'get'; + } + + // routes for this method + if (routes = routes[method]) { + + // matching routes + for (var len = routes.length; i < len; ++i) { + route = routes[i]; + if (route.match(path)) { + req._route_index = i; + return route; + } + } + } +}; + +/** + * Attempt to match a route for `method` + * and `url` with optional starting + * index of `i` defaulting to 0. + * + * @param {String} method + * @param {String} url + * @param {Number} i + * @return {Route} + * @api private + */ + +Router.prototype.match = function(method, url, i, head){ + var req = { method: method, url: url }; + return this.matchRequest(req, i, head); +}; + +/** + * Route `method`, `path`, and one or more callbacks. + * + * @param {String} method + * @param {String} path + * @param {Function} callback... + * @return {Router} for chaining + * @api private + */ + +Router.prototype.route = function(method, path, callbacks){ + var method = method.toLowerCase() + , callbacks = utils.flatten([].slice.call(arguments, 2)); + + // ensure path was given + if (!path) throw new Error('Router#' + method + '() requires a path'); + + // ensure all callbacks are functions + callbacks.forEach(function(fn){ + if ('function' == typeof fn) return; + var type = {}.toString.call(fn); + var msg = '.' + method + '() requires callback functions but got a ' + type; + throw new Error(msg); + }); + + // create the route + debug('defined %s %s', method, path); + var route = new Route(method, path, callbacks, { + sensitive: this.caseSensitive, + strict: this.strict + }); + + // add it + (this.map[method] = this.map[method] || []).push(route); + return this; +}; + +methods.forEach(function(method){ + Router.prototype[method] = function(path){ + var args = [method].concat([].slice.call(arguments)); + this.route.apply(this, args); + return this; + }; +}); diff --git a/server/node_modules/express/lib/router/route.js b/server/node_modules/express/lib/router/route.js new file mode 100644 index 0000000..4334c88 --- /dev/null +++ b/server/node_modules/express/lib/router/route.js @@ -0,0 +1,72 @@ + +/** + * Module dependencies. + */ + +var utils = require('../utils'); + +/** + * Expose `Route`. + */ + +module.exports = Route; + +/** + * Initialize `Route` with the given HTTP `method`, `path`, + * and an array of `callbacks` and `options`. + * + * Options: + * + * - `sensitive` enable case-sensitive routes + * - `strict` enable strict matching for trailing slashes + * + * @param {String} method + * @param {String} path + * @param {Array} callbacks + * @param {Object} options. + * @api private + */ + +function Route(method, path, callbacks, options) { + options = options || {}; + this.path = path; + this.method = method; + this.callbacks = callbacks; + this.regexp = utils.pathRegexp(path + , this.keys = [] + , options.sensitive + , options.strict); +} + +/** + * Check if this route matches `path`, if so + * populate `.params`. + * + * @param {String} path + * @return {Boolean} + * @api private + */ + +Route.prototype.match = function(path){ + var keys = this.keys + , params = this.params = [] + , m = this.regexp.exec(path); + + if (!m) return false; + + for (var i = 1, len = m.length; i < len; ++i) { + var key = keys[i - 1]; + + var val = 'string' == typeof m[i] + ? utils.decode(m[i]) + : m[i]; + + if (key) { + params[key.name] = val; + } else { + params.push(val); + } + } + + return true; +}; diff --git a/server/node_modules/express/lib/utils.js b/server/node_modules/express/lib/utils.js new file mode 100644 index 0000000..f19061b --- /dev/null +++ b/server/node_modules/express/lib/utils.js @@ -0,0 +1,333 @@ + +/** + * Module dependencies. + */ + +var mime = require('connect').mime + , crc32 = require('buffer-crc32'); + +/** + * toString ref. + */ + +var toString = {}.toString; + +/** + * Return ETag for `body`. + * + * @param {String|Buffer} body + * @return {String} + * @api private + */ + +exports.etag = function(body){ + return '"' + crc32.signed(body) + '"'; +}; + +/** + * Make `locals()` bound to the given `obj`. + * + * This is used for `app.locals` and `res.locals`. + * + * @param {Object} obj + * @return {Function} + * @api private + */ + +exports.locals = function(){ + function locals(obj){ + for (var key in obj) locals[key] = obj[key]; + return obj; + }; + + return locals; +}; + +/** + * Check if `path` looks absolute. + * + * @param {String} path + * @return {Boolean} + * @api private + */ + +exports.isAbsolute = function(path){ + if ('/' == path[0]) return true; + if (':' == path[1] && '\\' == path[2]) return true; + if ('\\\\' == path.substring(0, 2)) return true; // Microsoft Azure absolute path +}; + +/** + * Flatten the given `arr`. + * + * @param {Array} arr + * @return {Array} + * @api private + */ + +exports.flatten = function(arr, ret){ + var ret = ret || [] + , len = arr.length; + for (var i = 0; i < len; ++i) { + if (Array.isArray(arr[i])) { + exports.flatten(arr[i], ret); + } else { + ret.push(arr[i]); + } + } + return ret; +}; + +/** + * Normalize the given `type`, for example "html" becomes "text/html". + * + * @param {String} type + * @return {Object} + * @api private + */ + +exports.normalizeType = function(type){ + return ~type.indexOf('/') + ? acceptParams(type) + : { value: mime.lookup(type), params: {} }; +}; + +/** + * Normalize `types`, for example "html" becomes "text/html". + * + * @param {Array} types + * @return {Array} + * @api private + */ + +exports.normalizeTypes = function(types){ + var ret = []; + + for (var i = 0; i < types.length; ++i) { + ret.push(exports.normalizeType(types[i])); + } + + return ret; +}; + +/** + * Return the acceptable type in `types`, if any. + * + * @param {Array} types + * @param {String} str + * @return {String} + * @api private + */ + +exports.acceptsArray = function(types, str){ + // accept anything when Accept is not present + if (!str) return types[0]; + + // parse + var accepted = exports.parseAccept(str) + , normalized = exports.normalizeTypes(types) + , len = accepted.length; + + for (var i = 0; i < len; ++i) { + for (var j = 0, jlen = types.length; j < jlen; ++j) { + if (exports.accept(normalized[j], accepted[i])) { + return types[j]; + } + } + } +}; + +/** + * Check if `type(s)` are acceptable based on + * the given `str`. + * + * @param {String|Array} type(s) + * @param {String} str + * @return {Boolean|String} + * @api private + */ + +exports.accepts = function(type, str){ + if ('string' == typeof type) type = type.split(/ *, */); + return exports.acceptsArray(type, str); +}; + +/** + * Check if `type` array is acceptable for `other`. + * + * @param {Object} type + * @param {Object} other + * @return {Boolean} + * @api private + */ + +exports.accept = function(type, other){ + var t = type.value.split('/'); + return (t[0] == other.type || '*' == other.type) + && (t[1] == other.subtype || '*' == other.subtype) + && paramsEqual(type.params, other.params); +}; + +/** + * Check if accept params are equal. + * + * @param {Object} a + * @param {Object} b + * @return {Boolean} + * @api private + */ + +function paramsEqual(a, b){ + return !Object.keys(a).some(function(k) { + return a[k] != b[k]; + }); +} + +/** + * Parse accept `str`, returning + * an array objects containing + * `.type` and `.subtype` along + * with the values provided by + * `parseQuality()`. + * + * @param {Type} name + * @return {Type} + * @api private + */ + +exports.parseAccept = function(str){ + return exports + .parseParams(str) + .map(function(obj){ + var parts = obj.value.split('/'); + obj.type = parts[0]; + obj.subtype = parts[1]; + return obj; + }); +}; + +/** + * Parse quality `str`, returning an + * array of objects with `.value`, + * `.quality` and optional `.params` + * + * @param {String} str + * @return {Array} + * @api private + */ + +exports.parseParams = function(str){ + return str + .split(/ *, */) + .map(acceptParams) + .filter(function(obj){ + return obj.quality; + }) + .sort(function(a, b){ + if (a.quality === b.quality) { + return a.originalIndex - b.originalIndex; + } else { + return b.quality - a.quality; + } + }); +}; + +/** + * Parse accept params `str` returning an + * object with `.value`, `.quality` and `.params`. + * also includes `.originalIndex` for stable sorting + * + * @param {String} str + * @return {Object} + * @api private + */ + +function acceptParams(str, index) { + var parts = str.split(/ *; */); + var ret = { value: parts[0], quality: 1, params: {}, originalIndex: index }; + + for (var i = 1; i < parts.length; ++i) { + var pms = parts[i].split(/ *= */); + if ('q' == pms[0]) { + ret.quality = parseFloat(pms[1]); + } else { + ret.params[pms[0]] = pms[1]; + } + } + + return ret; +} + +/** + * Escape special characters in the given string of html. + * + * @param {String} html + * @return {String} + * @api private + */ + +exports.escape = function(html) { + return String(html) + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(//g, '>'); +}; + +/** + * Normalize the given path string, + * returning a regular expression. + * + * An empty array should be passed, + * which will contain the placeholder + * key names. For example "/user/:id" will + * then contain ["id"]. + * + * @param {String|RegExp|Array} path + * @param {Array} keys + * @param {Boolean} sensitive + * @param {Boolean} strict + * @return {RegExp} + * @api private + */ + +exports.pathRegexp = function(path, keys, sensitive, strict) { + if (toString.call(path) == '[object RegExp]') return path; + if (Array.isArray(path)) path = '(' + path.join('|') + ')'; + path = path + .concat(strict ? '' : '/?') + .replace(/\/\(/g, '(?:/') + .replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?(\*)?/g, function(_, slash, format, key, capture, optional, star){ + keys.push({ name: key, optional: !! optional }); + slash = slash || ''; + return '' + + (optional ? '' : slash) + + '(?:' + + (optional ? slash : '') + + (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')' + + (optional || '') + + (star ? '(/*)?' : ''); + }) + .replace(/([\/.])/g, '\\$1') + .replace(/\*/g, '(.*)'); + return new RegExp('^' + path + '$', sensitive ? '' : 'i'); +} + + +/** + * Decodes a URI component. Returns + * the original string if the component + * is malformed. + * + * @param {String} str + * @return {String} + * @api private + */ + +exports.decode = function(str) { + try { + return decodeURIComponent(str); + } catch (e) { + return str; + } +} diff --git a/server/node_modules/express/lib/view.js b/server/node_modules/express/lib/view.js new file mode 100644 index 0000000..b9dc69e --- /dev/null +++ b/server/node_modules/express/lib/view.js @@ -0,0 +1,77 @@ +/** + * Module dependencies. + */ + +var path = require('path') + , fs = require('fs') + , utils = require('./utils') + , dirname = path.dirname + , basename = path.basename + , extname = path.extname + , exists = fs.existsSync || path.existsSync + , join = path.join; + +/** + * Expose `View`. + */ + +module.exports = View; + +/** + * Initialize a new `View` with the given `name`. + * + * Options: + * + * - `defaultEngine` the default template engine name + * - `engines` template engine require() cache + * - `root` root path for view lookup + * + * @param {String} name + * @param {Object} options + * @api private + */ + +function View(name, options) { + options = options || {}; + this.name = name; + this.root = options.root; + var engines = options.engines; + this.defaultEngine = options.defaultEngine; + var ext = this.ext = extname(name); + if (!ext && !this.defaultEngine) throw new Error('No default engine was specified and no extension was provided.'); + if (!ext) name += (ext = this.ext = ('.' != this.defaultEngine[0] ? '.' : '') + this.defaultEngine); + this.engine = engines[ext] || (engines[ext] = require(ext.slice(1)).__express); + this.path = this.lookup(name); +} + +/** + * Lookup view by the given `path` + * + * @param {String} path + * @return {String} + * @api private + */ + +View.prototype.lookup = function(path){ + var ext = this.ext; + + // . + if (!utils.isAbsolute(path)) path = join(this.root, path); + if (exists(path)) return path; + + // /index. + path = join(dirname(path), basename(path, ext), 'index' + ext); + if (exists(path)) return path; +}; + +/** + * Render with the given `options` and callback `fn(err, str)`. + * + * @param {Object} options + * @param {Function} fn + * @api private + */ + +View.prototype.render = function(options, fn){ + this.engine(this.path, options, fn); +}; diff --git a/server/node_modules/express/node_modules/buffer-crc32/.npmignore b/server/node_modules/express/node_modules/buffer-crc32/.npmignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/server/node_modules/express/node_modules/buffer-crc32/.npmignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/server/node_modules/express/node_modules/buffer-crc32/.travis.yml b/server/node_modules/express/node_modules/buffer-crc32/.travis.yml new file mode 100644 index 0000000..7a902e8 --- /dev/null +++ b/server/node_modules/express/node_modules/buffer-crc32/.travis.yml @@ -0,0 +1,8 @@ +language: node_js +node_js: + - 0.6 + - 0.8 +notifications: + email: + recipients: + - brianloveswords@gmail.com \ No newline at end of file diff --git a/server/node_modules/express/node_modules/buffer-crc32/README.md b/server/node_modules/express/node_modules/buffer-crc32/README.md new file mode 100644 index 0000000..0d9d8b8 --- /dev/null +++ b/server/node_modules/express/node_modules/buffer-crc32/README.md @@ -0,0 +1,47 @@ +# buffer-crc32 + +[![Build Status](https://secure.travis-ci.org/brianloveswords/buffer-crc32.png?branch=master)](http://travis-ci.org/brianloveswords/buffer-crc32) + +crc32 that works with binary data and fancy character sets, outputs +buffer, signed or unsigned data and has tests. + +Derived from the sample CRC implementation in the PNG specification: http://www.w3.org/TR/PNG/#D-CRCAppendix + +# install +``` +npm install buffer-crc32 +``` + +# example +```js +var crc32 = require('buffer-crc32'); +// works with buffers +var buf = Buffer([0x00, 0x73, 0x75, 0x70, 0x20, 0x62, 0x72, 0x6f, 0x00]) +crc32(buf) // -> + +// has convenience methods for getting signed or unsigned ints +crc32.signed(buf) // -> -1805997238 +crc32.unsigned(buf) // -> 2488970058 + +// will cast to buffer if given a string, so you can +// directly use foreign characters safely +crc32('自動販売機') // -> + +// and works in append mode too +var partialCrc = crc32('hey'); +var partialCrc = crc32(' ', partialCrc); +var partialCrc = crc32('sup', partialCrc); +var partialCrc = crc32(' ', partialCrc); +var finalCrc = crc32('bros', partialCrc); // -> +``` + +# tests +This was tested against the output of zlib's crc32 method. You can run +the tests with`npm test` (requires tap) + +# see also +https://github.com/alexgorbatchev/node-crc, `crc.buffer.crc32` also +supports buffer inputs and return unsigned ints (thanks @tjholowaychuk). + +# license +MIT/X11 diff --git a/server/node_modules/express/node_modules/buffer-crc32/index.js b/server/node_modules/express/node_modules/buffer-crc32/index.js new file mode 100644 index 0000000..e29ce3e --- /dev/null +++ b/server/node_modules/express/node_modules/buffer-crc32/index.js @@ -0,0 +1,88 @@ +var Buffer = require('buffer').Buffer; + +var CRC_TABLE = [ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, + 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, + 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, + 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, + 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, + 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, + 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, + 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, + 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, + 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, + 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, + 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, + 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, + 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, + 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, + 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, + 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, + 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, + 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, + 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, + 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, + 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, + 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, + 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, + 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, + 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, + 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, + 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, + 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, + 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, + 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, + 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, + 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, + 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, + 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, + 0x2d02ef8d +]; + +function bufferizeInt(num) { + var tmp = Buffer(4); + tmp.writeInt32BE(num, 0); + return tmp; +} + +function _crc32(buf, previous) { + if (!Buffer.isBuffer(buf)) { + buf = Buffer(buf); + } + if (Buffer.isBuffer(previous)) { + previous = previous.readUInt32BE(0); + } + var crc = ~~previous ^ -1; + for (var n = 0; n < buf.length; n++) { + crc = CRC_TABLE[(crc ^ buf[n]) & 0xff] ^ (crc >>> 8); + } + return (crc ^ -1); +} + +function crc32() { + return bufferizeInt(_crc32.apply(null, arguments)); +} +crc32.signed = function () { + return _crc32.apply(null, arguments); +}; +crc32.unsigned = function () { + return _crc32.apply(null, arguments) >>> 0; +}; + +module.exports = crc32; diff --git a/server/node_modules/express/node_modules/buffer-crc32/package.json b/server/node_modules/express/node_modules/buffer-crc32/package.json new file mode 100644 index 0000000..ebed868 --- /dev/null +++ b/server/node_modules/express/node_modules/buffer-crc32/package.json @@ -0,0 +1,43 @@ +{ + "author": { + "name": "Brian J. Brennan", + "email": "brianloveswords@gmail.com", + "url": "http://bjb.io" + }, + "name": "buffer-crc32", + "description": "A pure javascript CRC32 algorithm that plays nice with binary data", + "version": "0.2.1", + "contributors": [ + { + "name": "Vladimir Kuznetsov" + } + ], + "homepage": "https://github.com/brianloveswords/buffer-crc32", + "repository": { + "type": "git", + "url": "git://github.com/brianloveswords/buffer-crc32.git" + }, + "main": "index.js", + "scripts": { + "test": "./node_modules/.bin/tap tests/*.test.js" + }, + "dependencies": {}, + "devDependencies": { + "tap": "~0.2.5" + }, + "optionalDependencies": {}, + "engines": { + "node": "*" + }, + "readme": "# buffer-crc32\n\n[![Build Status](https://secure.travis-ci.org/brianloveswords/buffer-crc32.png?branch=master)](http://travis-ci.org/brianloveswords/buffer-crc32)\n\ncrc32 that works with binary data and fancy character sets, outputs\nbuffer, signed or unsigned data and has tests.\n\nDerived from the sample CRC implementation in the PNG specification: http://www.w3.org/TR/PNG/#D-CRCAppendix\n\n# install\n```\nnpm install buffer-crc32\n```\n\n# example\n```js\nvar crc32 = require('buffer-crc32');\n// works with buffers\nvar buf = Buffer([0x00, 0x73, 0x75, 0x70, 0x20, 0x62, 0x72, 0x6f, 0x00])\ncrc32(buf) // -> \n\n// has convenience methods for getting signed or unsigned ints\ncrc32.signed(buf) // -> -1805997238\ncrc32.unsigned(buf) // -> 2488970058\n\n// will cast to buffer if given a string, so you can\n// directly use foreign characters safely\ncrc32('自動販売機') // -> \n\n// and works in append mode too\nvar partialCrc = crc32('hey');\nvar partialCrc = crc32(' ', partialCrc);\nvar partialCrc = crc32('sup', partialCrc);\nvar partialCrc = crc32(' ', partialCrc);\nvar finalCrc = crc32('bros', partialCrc); // -> \n```\n\n# tests\nThis was tested against the output of zlib's crc32 method. You can run\nthe tests with`npm test` (requires tap)\n\n# see also\nhttps://github.com/alexgorbatchev/node-crc, `crc.buffer.crc32` also\nsupports buffer inputs and return unsigned ints (thanks @tjholowaychuk).\n\n# license\nMIT/X11\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/brianloveswords/buffer-crc32/issues" + }, + "_id": "buffer-crc32@0.2.1", + "dist": { + "shasum": "f69b85da4d6e160c3d450a41ae7cddb5cbec4c95" + }, + "_from": "buffer-crc32@0.2.1", + "_resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.1.tgz" +} diff --git a/server/node_modules/express/node_modules/buffer-crc32/tests/crc.test.js b/server/node_modules/express/node_modules/buffer-crc32/tests/crc.test.js new file mode 100644 index 0000000..bb0f9ef --- /dev/null +++ b/server/node_modules/express/node_modules/buffer-crc32/tests/crc.test.js @@ -0,0 +1,89 @@ +var crc32 = require('..'); +var test = require('tap').test; + +test('simple crc32 is no problem', function (t) { + var input = Buffer('hey sup bros'); + var expected = Buffer([0x47, 0xfa, 0x55, 0x70]); + t.same(crc32(input), expected); + t.end(); +}); + +test('another simple one', function (t) { + var input = Buffer('IEND'); + var expected = Buffer([0xae, 0x42, 0x60, 0x82]); + t.same(crc32(input), expected); + t.end(); +}); + +test('slightly more complex', function (t) { + var input = Buffer([0x00, 0x00, 0x00]); + var expected = Buffer([0xff, 0x41, 0xd9, 0x12]); + t.same(crc32(input), expected); + t.end(); +}); + +test('complex crc32 gets calculated like a champ', function (t) { + var input = Buffer('शीर्षक'); + var expected = Buffer([0x17, 0xb8, 0xaf, 0xf1]); + t.same(crc32(input), expected); + t.end(); +}); + +test('casts to buffer if necessary', function (t) { + var input = 'शीर्षक'; + var expected = Buffer([0x17, 0xb8, 0xaf, 0xf1]); + t.same(crc32(input), expected); + t.end(); +}); + +test('can do signed', function (t) { + var input = 'ham sandwich'; + var expected = -1891873021; + t.same(crc32.signed(input), expected); + t.end(); +}); + +test('can do unsigned', function (t) { + var input = 'bear sandwich'; + var expected = 3711466352; + t.same(crc32.unsigned(input), expected); + t.end(); +}); + + +test('simple crc32 in append mode', function (t) { + var input = [Buffer('hey'), Buffer(' '), Buffer('sup'), Buffer(' '), Buffer('bros')]; + var expected = Buffer([0x47, 0xfa, 0x55, 0x70]); + for (var crc = 0, i = 0; i < input.length; i++) { + crc = crc32(input[i], crc); + } + t.same(crc, expected); + t.end(); +}); + + +test('can do signed in append mode', function (t) { + var input1 = 'ham'; + var input2 = ' '; + var input3 = 'sandwich'; + var expected = -1891873021; + + var crc = crc32.signed(input1); + crc = crc32.signed(input2, crc); + crc = crc32.signed(input3, crc); + + t.same(crc, expected); + t.end(); +}); + +test('can do unsigned in append mode', function (t) { + var input1 = 'bear san'; + var input2 = 'dwich'; + var expected = 3711466352; + + var crc = crc32.unsigned(input1); + crc = crc32.unsigned(input2, crc); + t.same(crc, expected); + t.end(); +}); + diff --git a/server/node_modules/express/node_modules/commander/History.md b/server/node_modules/express/node_modules/commander/History.md new file mode 100644 index 0000000..ce046f6 --- /dev/null +++ b/server/node_modules/express/node_modules/commander/History.md @@ -0,0 +1,174 @@ + +1.3.2 / 2013-07-18 +================== + + * add support for sub-commands to co-exist with the original command + +1.3.1 / 2013-07-18 +================== + + * add quick .runningCommand hack so you can opt-out of other logic when running a sub command + +1.3.0 / 2013-07-09 +================== + + * add EACCES error handling + * fix sub-command --help + +1.2.0 / 2013-06-13 +================== + + * allow "-" hyphen as an option argument + * support for RegExp coercion + +1.1.1 / 2012-11-20 +================== + + * add more sub-command padding + * fix .usage() when args are present. Closes #106 + +1.1.0 / 2012-11-16 +================== + + * add git-style executable subcommand support. Closes #94 + +1.0.5 / 2012-10-09 +================== + + * fix `--name` clobbering. Closes #92 + * fix examples/help. Closes #89 + +1.0.4 / 2012-09-03 +================== + + * add `outputHelp()` method. + +1.0.3 / 2012-08-30 +================== + + * remove invalid .version() defaulting + +1.0.2 / 2012-08-24 +================== + + * add `--foo=bar` support [arv] + * fix password on node 0.8.8. Make backward compatible with 0.6 [focusaurus] + +1.0.1 / 2012-08-03 +================== + + * fix issue #56 + * fix tty.setRawMode(mode) was moved to tty.ReadStream#setRawMode() (i.e. process.stdin.setRawMode()) + +1.0.0 / 2012-07-05 +================== + + * add support for optional option descriptions + * add defaulting of `.version()` to package.json's version + +0.6.1 / 2012-06-01 +================== + + * Added: append (yes or no) on confirmation + * Added: allow node.js v0.7.x + +0.6.0 / 2012-04-10 +================== + + * Added `.prompt(obj, callback)` support. Closes #49 + * Added default support to .choose(). Closes #41 + * Fixed the choice example + +0.5.1 / 2011-12-20 +================== + + * Fixed `password()` for recent nodes. Closes #36 + +0.5.0 / 2011-12-04 +================== + + * Added sub-command option support [itay] + +0.4.3 / 2011-12-04 +================== + + * Fixed custom help ordering. Closes #32 + +0.4.2 / 2011-11-24 +================== + + * Added travis support + * Fixed: line-buffered input automatically trimmed. Closes #31 + +0.4.1 / 2011-11-18 +================== + + * Removed listening for "close" on --help + +0.4.0 / 2011-11-15 +================== + + * Added support for `--`. Closes #24 + +0.3.3 / 2011-11-14 +================== + + * Fixed: wait for close event when writing help info [Jerry Hamlet] + +0.3.2 / 2011-11-01 +================== + + * Fixed long flag definitions with values [felixge] + +0.3.1 / 2011-10-31 +================== + + * Changed `--version` short flag to `-V` from `-v` + * Changed `.version()` so it's configurable [felixge] + +0.3.0 / 2011-10-31 +================== + + * Added support for long flags only. Closes #18 + +0.2.1 / 2011-10-24 +================== + + * "node": ">= 0.4.x < 0.7.0". Closes #20 + +0.2.0 / 2011-09-26 +================== + + * Allow for defaults that are not just boolean. Default peassignment only occurs for --no-*, optional, and required arguments. [Jim Isaacs] + +0.1.0 / 2011-08-24 +================== + + * Added support for custom `--help` output + +0.0.5 / 2011-08-18 +================== + + * Changed: when the user enters nothing prompt for password again + * Fixed issue with passwords beginning with numbers [NuckChorris] + +0.0.4 / 2011-08-15 +================== + + * Fixed `Commander#args` + +0.0.3 / 2011-08-15 +================== + + * Added default option value support + +0.0.2 / 2011-08-15 +================== + + * Added mask support to `Command#password(str[, mask], fn)` + * Added `Command#password(str, fn)` + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/server/node_modules/express/node_modules/commander/Readme.md b/server/node_modules/express/node_modules/commander/Readme.md new file mode 100644 index 0000000..ed0aeb2 --- /dev/null +++ b/server/node_modules/express/node_modules/commander/Readme.md @@ -0,0 +1,276 @@ +# Commander.js + + The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/visionmedia/commander). + + [![Build Status](https://secure.travis-ci.org/visionmedia/commander.js.png)](http://travis-ci.org/visionmedia/commander.js) + +## Installation + + $ npm install commander + +## Option parsing + + Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options. + +```js +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('commander'); + +program + .version('0.0.1') + .option('-p, --peppers', 'Add peppers') + .option('-P, --pineapple', 'Add pineapple') + .option('-b, --bbq', 'Add bbq sauce') + .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble') + .parse(process.argv); + +console.log('you ordered a pizza with:'); +if (program.peppers) console.log(' - peppers'); +if (program.pineapple) console.log(' - pineapple'); +if (program.bbq) console.log(' - bbq'); +console.log(' - %s cheese', program.cheese); +``` + + Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as "--template-engine" are camel-cased, becoming `program.templateEngine` etc. + +## Automated --help + + The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free: + +``` + $ ./examples/pizza --help + + Usage: pizza [options] + + Options: + + -V, --version output the version number + -p, --peppers Add peppers + -P, --pineapple Add pineapple + -b, --bbq Add bbq sauce + -c, --cheese Add the specified type of cheese [marble] + -h, --help output usage information + +``` + +## Coercion + +```js +function range(val) { + return val.split('..').map(Number); +} + +function list(val) { + return val.split(','); +} + +program + .version('0.0.1') + .usage('[options] ') + .option('-i, --integer ', 'An integer argument', parseInt) + .option('-f, --float ', 'A float argument', parseFloat) + .option('-r, --range ..', 'A range', range) + .option('-l, --list ', 'A list', list) + .option('-o, --optional [value]', 'An optional value') + .parse(process.argv); + +console.log(' int: %j', program.integer); +console.log(' float: %j', program.float); +console.log(' optional: %j', program.optional); +program.range = program.range || []; +console.log(' range: %j..%j', program.range[0], program.range[1]); +console.log(' list: %j', program.list); +console.log(' args: %j', program.args); +``` + +## Custom help + + You can display arbitrary `-h, --help` information + by listening for "--help". Commander will automatically + exit once you are done so that the remainder of your program + does not execute causing undesired behaviours, for example + in the following executable "stuff" will not output when + `--help` is used. + +```js +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var program = require('../'); + +function list(val) { + return val.split(',').map(Number); +} + +program + .version('0.0.1') + .option('-f, --foo', 'enable some foo') + .option('-b, --bar', 'enable some bar') + .option('-B, --baz', 'enable some baz'); + +// must be before .parse() since +// node's emit() is immediate + +program.on('--help', function(){ + console.log(' Examples:'); + console.log(''); + console.log(' $ custom-help --help'); + console.log(' $ custom-help -h'); + console.log(''); +}); + +program.parse(process.argv); + +console.log('stuff'); +``` + +yielding the following help output: + +``` + +Usage: custom-help [options] + +Options: + + -h, --help output usage information + -V, --version output the version number + -f, --foo enable some foo + -b, --bar enable some bar + -B, --baz enable some baz + +Examples: + + $ custom-help --help + $ custom-help -h + +``` + +## .prompt(msg, fn) + + Single-line prompt: + +```js +program.prompt('name: ', function(name){ + console.log('hi %s', name); +}); +``` + + Multi-line prompt: + +```js +program.prompt('description:', function(name){ + console.log('hi %s', name); +}); +``` + + Coercion: + +```js +program.prompt('Age: ', Number, function(age){ + console.log('age: %j', age); +}); +``` + +```js +program.prompt('Birthdate: ', Date, function(date){ + console.log('date: %s', date); +}); +``` + +```js +program.prompt('Email: ', /^.+@.+\..+$/, function(email){ + console.log('email: %j', email); +}); +``` + +## .password(msg[, mask], fn) + +Prompt for password without echoing: + +```js +program.password('Password: ', function(pass){ + console.log('got "%s"', pass); + process.stdin.destroy(); +}); +``` + +Prompt for password with mask char "*": + +```js +program.password('Password: ', '*', function(pass){ + console.log('got "%s"', pass); + process.stdin.destroy(); +}); +``` + +## .confirm(msg, fn) + + Confirm with the given `msg`: + +```js +program.confirm('continue? ', function(ok){ + console.log(' got %j', ok); +}); +``` + +## .choose(list, fn) + + Let the user choose from a `list`: + +```js +var list = ['tobi', 'loki', 'jane', 'manny', 'luna']; + +console.log('Choose the coolest pet:'); +program.choose(list, function(i){ + console.log('you chose %d "%s"', i, list[i]); +}); +``` + +## .outputHelp() + + Output help information without exiting. + +## .help() + + Output help information and exit immediately. + +## Links + + - [API documentation](http://visionmedia.github.com/commander.js/) + - [ascii tables](https://github.com/LearnBoost/cli-table) + - [progress bars](https://github.com/visionmedia/node-progress) + - [more progress bars](https://github.com/substack/node-multimeter) + - [examples](https://github.com/visionmedia/commander.js/tree/master/examples) + +## License + +(The MIT License) + +Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/server/node_modules/express/node_modules/commander/index.js b/server/node_modules/express/node_modules/commander/index.js new file mode 100644 index 0000000..d9634e3 --- /dev/null +++ b/server/node_modules/express/node_modules/commander/index.js @@ -0,0 +1,1160 @@ +/*! + * commander + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter + , spawn = require('child_process').spawn + , keypress = require('keypress') + , fs = require('fs') + , exists = fs.existsSync + , path = require('path') + , tty = require('tty') + , dirname = path.dirname + , basename = path.basename; + +/** + * Expose the root command. + */ + +exports = module.exports = new Command; + +/** + * Expose `Command`. + */ + +exports.Command = Command; + +/** + * Expose `Option`. + */ + +exports.Option = Option; + +/** + * Initialize a new `Option` with the given `flags` and `description`. + * + * @param {String} flags + * @param {String} description + * @api public + */ + +function Option(flags, description) { + this.flags = flags; + this.required = ~flags.indexOf('<'); + this.optional = ~flags.indexOf('['); + this.bool = !~flags.indexOf('-no-'); + flags = flags.split(/[ ,|]+/); + if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift(); + this.long = flags.shift(); + this.description = description || ''; +} + +/** + * Return option name. + * + * @return {String} + * @api private + */ + +Option.prototype.name = function(){ + return this.long + .replace('--', '') + .replace('no-', ''); +}; + +/** + * Check if `arg` matches the short or long flag. + * + * @param {String} arg + * @return {Boolean} + * @api private + */ + +Option.prototype.is = function(arg){ + return arg == this.short + || arg == this.long; +}; + +/** + * Initialize a new `Command`. + * + * @param {String} name + * @api public + */ + +function Command(name) { + this.commands = []; + this.options = []; + this._execs = []; + this._args = []; + this._name = name; +} + +/** + * Inherit from `EventEmitter.prototype`. + */ + +Command.prototype.__proto__ = EventEmitter.prototype; + +/** + * Add command `name`. + * + * The `.action()` callback is invoked when the + * command `name` is specified via __ARGV__, + * and the remaining arguments are applied to the + * function for access. + * + * When the `name` is "*" an un-matched command + * will be passed as the first arg, followed by + * the rest of __ARGV__ remaining. + * + * Examples: + * + * program + * .version('0.0.1') + * .option('-C, --chdir ', 'change the working directory') + * .option('-c, --config ', 'set config path. defaults to ./deploy.conf') + * .option('-T, --no-tests', 'ignore test hook') + * + * program + * .command('setup') + * .description('run remote setup commands') + * .action(function(){ + * console.log('setup'); + * }); + * + * program + * .command('exec ') + * .description('run the given remote command') + * .action(function(cmd){ + * console.log('exec "%s"', cmd); + * }); + * + * program + * .command('*') + * .description('deploy the given env') + * .action(function(env){ + * console.log('deploying "%s"', env); + * }); + * + * program.parse(process.argv); + * + * @param {String} name + * @param {String} [desc] + * @return {Command} the new command + * @api public + */ + +Command.prototype.command = function(name, desc){ + var args = name.split(/ +/); + var cmd = new Command(args.shift()); + if (desc) cmd.description(desc); + if (desc) this.executables = true; + if (desc) this._execs[cmd._name] = true; + this.commands.push(cmd); + cmd.parseExpectedArgs(args); + cmd.parent = this; + if (desc) return this; + return cmd; +}; + +/** + * Add an implicit `help [cmd]` subcommand + * which invokes `--help` for the given command. + * + * @api private + */ + +Command.prototype.addImplicitHelpCommand = function() { + this.command('help [cmd]', 'display help for [cmd]'); +}; + +/** + * Parse expected `args`. + * + * For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`. + * + * @param {Array} args + * @return {Command} for chaining + * @api public + */ + +Command.prototype.parseExpectedArgs = function(args){ + if (!args.length) return; + var self = this; + args.forEach(function(arg){ + switch (arg[0]) { + case '<': + self._args.push({ required: true, name: arg.slice(1, -1) }); + break; + case '[': + self._args.push({ required: false, name: arg.slice(1, -1) }); + break; + } + }); + return this; +}; + +/** + * Register callback `fn` for the command. + * + * Examples: + * + * program + * .command('help') + * .description('display verbose help') + * .action(function(){ + * // output help here + * }); + * + * @param {Function} fn + * @return {Command} for chaining + * @api public + */ + +Command.prototype.action = function(fn){ + var self = this; + this.parent.on(this._name, function(args, unknown){ + // Parse any so-far unknown options + unknown = unknown || []; + var parsed = self.parseOptions(unknown); + + // Output help if necessary + outputHelpIfNecessary(self, parsed.unknown); + + // If there are still any unknown options, then we simply + // die, unless someone asked for help, in which case we give it + // to them, and then we die. + if (parsed.unknown.length > 0) { + self.unknownOption(parsed.unknown[0]); + } + + // Leftover arguments need to be pushed back. Fixes issue #56 + if (parsed.args.length) args = parsed.args.concat(args); + + self._args.forEach(function(arg, i){ + if (arg.required && null == args[i]) { + self.missingArgument(arg.name); + } + }); + + // Always append ourselves to the end of the arguments, + // to make sure we match the number of arguments the user + // expects + if (self._args.length) { + args[self._args.length] = self; + } else { + args.push(self); + } + + fn.apply(this, args); + }); + return this; +}; + +/** + * Define option with `flags`, `description` and optional + * coercion `fn`. + * + * The `flags` string should contain both the short and long flags, + * separated by comma, a pipe or space. The following are all valid + * all will output this way when `--help` is used. + * + * "-p, --pepper" + * "-p|--pepper" + * "-p --pepper" + * + * Examples: + * + * // simple boolean defaulting to false + * program.option('-p, --pepper', 'add pepper'); + * + * --pepper + * program.pepper + * // => Boolean + * + * // simple boolean defaulting to false + * program.option('-C, --no-cheese', 'remove cheese'); + * + * program.cheese + * // => true + * + * --no-cheese + * program.cheese + * // => true + * + * // required argument + * program.option('-C, --chdir ', 'change the working directory'); + * + * --chdir /tmp + * program.chdir + * // => "/tmp" + * + * // optional argument + * program.option('-c, --cheese [type]', 'add cheese [marble]'); + * + * @param {String} flags + * @param {String} description + * @param {Function|Mixed} fn or default + * @param {Mixed} defaultValue + * @return {Command} for chaining + * @api public + */ + +Command.prototype.option = function(flags, description, fn, defaultValue){ + var self = this + , option = new Option(flags, description) + , oname = option.name() + , name = camelcase(oname); + + // default as 3rd arg + if ('function' != typeof fn) defaultValue = fn, fn = null; + + // preassign default value only for --no-*, [optional], or + if (false == option.bool || option.optional || option.required) { + // when --no-* we make sure default is true + if (false == option.bool) defaultValue = true; + // preassign only if we have a default + if (undefined !== defaultValue) self[name] = defaultValue; + } + + // register the option + this.options.push(option); + + // when it's passed assign the value + // and conditionally invoke the callback + this.on(oname, function(val){ + // coercion + if (null != val && fn) val = fn(val); + + // unassigned or bool + if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) { + // if no value, bool true, and we have a default, then use it! + if (null == val) { + self[name] = option.bool + ? defaultValue || true + : false; + } else { + self[name] = val; + } + } else if (null !== val) { + // reassign + self[name] = val; + } + }); + + return this; +}; + +/** + * Parse `argv`, settings options and invoking commands when defined. + * + * @param {Array} argv + * @return {Command} for chaining + * @api public + */ + +Command.prototype.parse = function(argv){ + // implicit help + if (this.executables) this.addImplicitHelpCommand(); + + // store raw args + this.rawArgs = argv; + + // guess name + this._name = this._name || basename(argv[1]); + + // process argv + var parsed = this.parseOptions(this.normalize(argv.slice(2))); + var args = this.args = parsed.args; + + var result = this.parseArgs(this.args, parsed.unknown); + + // executable sub-commands + var name = result.args[0]; + if (this._execs[name]) return this.executeSubCommand(argv, args, parsed.unknown); + + return result; +}; + +/** + * Execute a sub-command executable. + * + * @param {Array} argv + * @param {Array} args + * @param {Array} unknown + * @api private + */ + +Command.prototype.executeSubCommand = function(argv, args, unknown) { + args = args.concat(unknown); + + if (!args.length) this.help(); + if ('help' == args[0] && 1 == args.length) this.help(); + + // --help + if ('help' == args[0]) { + args[0] = args[1]; + args[1] = '--help'; + } + + // executable + var dir = dirname(argv[1]); + var bin = basename(argv[1]) + '-' + args[0]; + + // check for ./ first + var local = path.join(dir, bin); + + // run it + args = args.slice(1); + var proc = spawn(local, args, { stdio: 'inherit', customFds: [0, 1, 2] }); + proc.on('error', function(err){ + if (err.code == "ENOENT") { + console.error('\n %s(1) does not exist, try --help\n', bin); + } else if (err.code == "EACCES") { + console.error('\n %s(1) not executable. try chmod or run with root\n', bin); + } + }); + + this.runningCommand = proc; +}; + +/** + * Normalize `args`, splitting joined short flags. For example + * the arg "-abc" is equivalent to "-a -b -c". + * This also normalizes equal sign and splits "--abc=def" into "--abc def". + * + * @param {Array} args + * @return {Array} + * @api private + */ + +Command.prototype.normalize = function(args){ + var ret = [] + , arg + , index; + + for (var i = 0, len = args.length; i < len; ++i) { + arg = args[i]; + if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) { + arg.slice(1).split('').forEach(function(c){ + ret.push('-' + c); + }); + } else if (/^--/.test(arg) && ~(index = arg.indexOf('='))) { + ret.push(arg.slice(0, index), arg.slice(index + 1)); + } else { + ret.push(arg); + } + } + + return ret; +}; + +/** + * Parse command `args`. + * + * When listener(s) are available those + * callbacks are invoked, otherwise the "*" + * event is emitted and those actions are invoked. + * + * @param {Array} args + * @return {Command} for chaining + * @api private + */ + +Command.prototype.parseArgs = function(args, unknown){ + var cmds = this.commands + , len = cmds.length + , name; + + if (args.length) { + name = args[0]; + if (this.listeners(name).length) { + this.emit(args.shift(), args, unknown); + } else { + this.emit('*', args); + } + } else { + outputHelpIfNecessary(this, unknown); + + // If there were no args and we have unknown options, + // then they are extraneous and we need to error. + if (unknown.length > 0) { + this.unknownOption(unknown[0]); + } + } + + return this; +}; + +/** + * Return an option matching `arg` if any. + * + * @param {String} arg + * @return {Option} + * @api private + */ + +Command.prototype.optionFor = function(arg){ + for (var i = 0, len = this.options.length; i < len; ++i) { + if (this.options[i].is(arg)) { + return this.options[i]; + } + } +}; + +/** + * Parse options from `argv` returning `argv` + * void of these options. + * + * @param {Array} argv + * @return {Array} + * @api public + */ + +Command.prototype.parseOptions = function(argv){ + var args = [] + , len = argv.length + , literal + , option + , arg; + + var unknownOptions = []; + + // parse options + for (var i = 0; i < len; ++i) { + arg = argv[i]; + + // literal args after -- + if ('--' == arg) { + literal = true; + continue; + } + + if (literal) { + args.push(arg); + continue; + } + + // find matching Option + option = this.optionFor(arg); + + // option is defined + if (option) { + // requires arg + if (option.required) { + arg = argv[++i]; + if (null == arg) return this.optionMissingArgument(option); + if ('-' == arg[0] && '-' != arg) return this.optionMissingArgument(option, arg); + this.emit(option.name(), arg); + // optional arg + } else if (option.optional) { + arg = argv[i+1]; + if (null == arg || ('-' == arg[0] && '-' != arg)) { + arg = null; + } else { + ++i; + } + this.emit(option.name(), arg); + // bool + } else { + this.emit(option.name()); + } + continue; + } + + // looks like an option + if (arg.length > 1 && '-' == arg[0]) { + unknownOptions.push(arg); + + // If the next argument looks like it might be + // an argument for this option, we pass it on. + // If it isn't, then it'll simply be ignored + if (argv[i+1] && '-' != argv[i+1][0]) { + unknownOptions.push(argv[++i]); + } + continue; + } + + // arg + args.push(arg); + } + + return { args: args, unknown: unknownOptions }; +}; + +/** + * Argument `name` is missing. + * + * @param {String} name + * @api private + */ + +Command.prototype.missingArgument = function(name){ + console.error(); + console.error(" error: missing required argument `%s'", name); + console.error(); + process.exit(1); +}; + +/** + * `Option` is missing an argument, but received `flag` or nothing. + * + * @param {String} option + * @param {String} flag + * @api private + */ + +Command.prototype.optionMissingArgument = function(option, flag){ + console.error(); + if (flag) { + console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag); + } else { + console.error(" error: option `%s' argument missing", option.flags); + } + console.error(); + process.exit(1); +}; + +/** + * Unknown option `flag`. + * + * @param {String} flag + * @api private + */ + +Command.prototype.unknownOption = function(flag){ + console.error(); + console.error(" error: unknown option `%s'", flag); + console.error(); + process.exit(1); +}; + + +/** + * Set the program version to `str`. + * + * This method auto-registers the "-V, --version" flag + * which will print the version number when passed. + * + * @param {String} str + * @param {String} flags + * @return {Command} for chaining + * @api public + */ + +Command.prototype.version = function(str, flags){ + if (0 == arguments.length) return this._version; + this._version = str; + flags = flags || '-V, --version'; + this.option(flags, 'output the version number'); + this.on('version', function(){ + console.log(str); + process.exit(0); + }); + return this; +}; + +/** + * Set the description `str`. + * + * @param {String} str + * @return {String|Command} + * @api public + */ + +Command.prototype.description = function(str){ + if (0 == arguments.length) return this._description; + this._description = str; + return this; +}; + +/** + * Set / get the command usage `str`. + * + * @param {String} str + * @return {String|Command} + * @api public + */ + +Command.prototype.usage = function(str){ + var args = this._args.map(function(arg){ + return arg.required + ? '<' + arg.name + '>' + : '[' + arg.name + ']'; + }); + + var usage = '[options' + + (this.commands.length ? '] [command' : '') + + ']' + + (this._args.length ? ' ' + args : ''); + + if (0 == arguments.length) return this._usage || usage; + this._usage = str; + + return this; +}; + +/** + * Return the largest option length. + * + * @return {Number} + * @api private + */ + +Command.prototype.largestOptionLength = function(){ + return this.options.reduce(function(max, option){ + return Math.max(max, option.flags.length); + }, 0); +}; + +/** + * Return help for options. + * + * @return {String} + * @api private + */ + +Command.prototype.optionHelp = function(){ + var width = this.largestOptionLength(); + + // Prepend the help information + return [pad('-h, --help', width) + ' ' + 'output usage information'] + .concat(this.options.map(function(option){ + return pad(option.flags, width) + + ' ' + option.description; + })) + .join('\n'); +}; + +/** + * Return command help documentation. + * + * @return {String} + * @api private + */ + +Command.prototype.commandHelp = function(){ + if (!this.commands.length) return ''; + return [ + '' + , ' Commands:' + , '' + , this.commands.map(function(cmd){ + var args = cmd._args.map(function(arg){ + return arg.required + ? '<' + arg.name + '>' + : '[' + arg.name + ']'; + }).join(' '); + + return pad(cmd._name + + (cmd.options.length + ? ' [options]' + : '') + ' ' + args, 22) + + (cmd.description() + ? ' ' + cmd.description() + : ''); + }).join('\n').replace(/^/gm, ' ') + , '' + ].join('\n'); +}; + +/** + * Return program help documentation. + * + * @return {String} + * @api private + */ + +Command.prototype.helpInformation = function(){ + return [ + '' + , ' Usage: ' + this._name + ' ' + this.usage() + , '' + this.commandHelp() + , ' Options:' + , '' + , '' + this.optionHelp().replace(/^/gm, ' ') + , '' + , '' + ].join('\n'); +}; + +/** + * Prompt for a `Number`. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptForNumber = function(str, fn){ + var self = this; + this.promptSingleLine(str, function parseNumber(val){ + val = Number(val); + if (isNaN(val)) return self.promptSingleLine(str + '(must be a number) ', parseNumber); + fn(val); + }); +}; + +/** + * Prompt for a `Date`. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptForDate = function(str, fn){ + var self = this; + this.promptSingleLine(str, function parseDate(val){ + val = new Date(val); + if (isNaN(val.getTime())) return self.promptSingleLine(str + '(must be a date) ', parseDate); + fn(val); + }); +}; + + +/** + * Prompt for a `Regular Expression`. + * + * @param {String} str + * @param {Object} pattern regular expression object to test + * @param {Function} fn + * @api private + */ + +Command.prototype.promptForRegexp = function(str, pattern, fn){ + var self = this; + this.promptSingleLine(str, function parseRegexp(val){ + if(!pattern.test(val)) return self.promptSingleLine(str + '(regular expression mismatch) ', parseRegexp); + fn(val); + }); +}; + + +/** + * Single-line prompt. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptSingleLine = function(str, fn){ + // determine if the 2nd argument is a regular expression + if (arguments[1].global !== undefined && arguments[1].multiline !== undefined) { + return this.promptForRegexp(str, arguments[1], arguments[2]); + } else if ('function' == typeof arguments[2]) { + return this['promptFor' + (fn.name || fn)](str, arguments[2]); + } + + process.stdout.write(str); + process.stdin.setEncoding('utf8'); + process.stdin.once('data', function(val){ + fn(val.trim()); + }).resume(); +}; + +/** + * Multi-line prompt. + * + * @param {String} str + * @param {Function} fn + * @api private + */ + +Command.prototype.promptMultiLine = function(str, fn){ + var buf = []; + console.log(str); + process.stdin.setEncoding('utf8'); + process.stdin.on('data', function(val){ + if ('\n' == val || '\r\n' == val) { + process.stdin.removeAllListeners('data'); + fn(buf.join('\n')); + } else { + buf.push(val.trimRight()); + } + }).resume(); +}; + +/** + * Prompt `str` and callback `fn(val)` + * + * Commander supports single-line and multi-line prompts. + * To issue a single-line prompt simply add white-space + * to the end of `str`, something like "name: ", whereas + * for a multi-line prompt omit this "description:". + * + * + * Examples: + * + * program.prompt('Username: ', function(name){ + * console.log('hi %s', name); + * }); + * + * program.prompt('Description:', function(desc){ + * console.log('description was "%s"', desc.trim()); + * }); + * + * @param {String|Object} str + * @param {Function} fn + * @api public + */ + +Command.prototype.prompt = function(str, fn){ + var self = this; + if ('string' == typeof str) { + if (/ $/.test(str)) return this.promptSingleLine.apply(this, arguments); + this.promptMultiLine(str, fn); + } else { + var keys = Object.keys(str) + , obj = {}; + + function next() { + var key = keys.shift() + , label = str[key]; + + if (!key) return fn(obj); + self.prompt(label, function(val){ + obj[key] = val; + next(); + }); + } + + next(); + } +}; + +/** + * Prompt for password with `str`, `mask` char and callback `fn(val)`. + * + * The mask string defaults to '', aka no output is + * written while typing, you may want to use "*" etc. + * + * Examples: + * + * program.password('Password: ', function(pass){ + * console.log('got "%s"', pass); + * process.stdin.destroy(); + * }); + * + * program.password('Password: ', '*', function(pass){ + * console.log('got "%s"', pass); + * process.stdin.destroy(); + * }); + * + * @param {String} str + * @param {String} mask + * @param {Function} fn + * @api public + */ + +Command.prototype.password = function(str, mask, fn){ + var self = this + , buf = ''; + + // default mask + if ('function' == typeof mask) { + fn = mask; + mask = ''; + } + + keypress(process.stdin); + + function setRawMode(mode) { + if (process.stdin.setRawMode) { + process.stdin.setRawMode(mode); + } else { + tty.setRawMode(mode); + } + }; + setRawMode(true); + process.stdout.write(str); + + // keypress + process.stdin.on('keypress', function(c, key){ + if (key && 'enter' == key.name) { + console.log(); + process.stdin.pause(); + process.stdin.removeAllListeners('keypress'); + setRawMode(false); + if (!buf.trim().length) return self.password(str, mask, fn); + fn(buf); + return; + } + + if (key && key.ctrl && 'c' == key.name) { + console.log('%s', buf); + process.exit(); + } + + process.stdout.write(mask); + buf += c; + }).resume(); +}; + +/** + * Confirmation prompt with `str` and callback `fn(bool)` + * + * Examples: + * + * program.confirm('continue? ', function(ok){ + * console.log(' got %j', ok); + * process.stdin.destroy(); + * }); + * + * @param {String} str + * @param {Function} fn + * @api public + */ + + +Command.prototype.confirm = function(str, fn, verbose){ + var self = this; + this.prompt(str, function(ok){ + if (!ok.trim()) { + if (!verbose) str += '(yes or no) '; + return self.confirm(str, fn, true); + } + fn(parseBool(ok)); + }); +}; + +/** + * Choice prompt with `list` of items and callback `fn(index, item)` + * + * Examples: + * + * var list = ['tobi', 'loki', 'jane', 'manny', 'luna']; + * + * console.log('Choose the coolest pet:'); + * program.choose(list, function(i){ + * console.log('you chose %d "%s"', i, list[i]); + * process.stdin.destroy(); + * }); + * + * @param {Array} list + * @param {Number|Function} index or fn + * @param {Function} fn + * @api public + */ + +Command.prototype.choose = function(list, index, fn){ + var self = this + , hasDefault = 'number' == typeof index; + + if (!hasDefault) { + fn = index; + index = null; + } + + list.forEach(function(item, i){ + if (hasDefault && i == index) { + console.log('* %d) %s', i + 1, item); + } else { + console.log(' %d) %s', i + 1, item); + } + }); + + function again() { + self.prompt(' : ', function(val){ + val = parseInt(val, 10) - 1; + if (hasDefault && isNaN(val)) val = index; + + if (null == list[val]) { + again(); + } else { + fn(val, list[val]); + } + }); + } + + again(); +}; + + +/** + * Output help information for this command + * + * @api public + */ + +Command.prototype.outputHelp = function(){ + process.stdout.write(this.helpInformation()); + this.emit('--help'); +}; + +/** + * Output help information and exit. + * + * @api public + */ + +Command.prototype.help = function(){ + this.outputHelp(); + process.exit(); +}; + +/** + * Camel-case the given `flag` + * + * @param {String} flag + * @return {String} + * @api private + */ + +function camelcase(flag) { + return flag.split('-').reduce(function(str, word){ + return str + word[0].toUpperCase() + word.slice(1); + }); +} + +/** + * Parse a boolean `str`. + * + * @param {String} str + * @return {Boolean} + * @api private + */ + +function parseBool(str) { + return /^y|yes|ok|true$/i.test(str); +} + +/** + * Pad `str` to `width`. + * + * @param {String} str + * @param {Number} width + * @return {String} + * @api private + */ + +function pad(str, width) { + var len = Math.max(0, width - str.length); + return str + Array(len + 1).join(' '); +} + +/** + * Output help information if necessary + * + * @param {Command} command to output help for + * @param {Array} array of options to search for -h or --help + * @api private + */ + +function outputHelpIfNecessary(cmd, options) { + options = options || []; + for (var i = 0; i < options.length; i++) { + if (options[i] == '--help' || options[i] == '-h') { + cmd.outputHelp(); + process.exit(0); + } + } +} diff --git a/server/node_modules/express/node_modules/commander/node_modules/keypress/README.md b/server/node_modules/express/node_modules/commander/node_modules/keypress/README.md new file mode 100644 index 0000000..a768e8f --- /dev/null +++ b/server/node_modules/express/node_modules/commander/node_modules/keypress/README.md @@ -0,0 +1,101 @@ +keypress +======== +### Make any Node ReadableStream emit "keypress" events + + +Previous to Node `v0.8.x`, there was an undocumented `"keypress"` event that +`process.stdin` would emit when it was a TTY. Some people discovered this hidden +gem, and started using it in their own code. + +Now in Node `v0.8.x`, this `"keypress"` event does not get emitted by default, +but rather only when it is being used in conjuction with the `readline` (or by +extension, the `repl`) module. + +This module is the exact logic from the node `v0.8.x` releases ripped out into its +own module. + +__Bonus:__ Now with mouse support! + +Installation +------------ + +Install with `npm`: + +``` bash +$ npm install keypress +``` + +Or add it to the `"dependencies"` section of your _package.json_ file. + + +Example +------- + +#### Listening for "keypress" events + +``` js +var keypress = require('keypress'); + +// make `process.stdin` begin emitting "keypress" events +keypress(process.stdin); + +// listen for the "keypress" event +process.stdin.on('keypress', function (ch, key) { + console.log('got "keypress"', key); + if (key && key.ctrl && key.name == 'c') { + process.stdin.pause(); + } +}); + +process.stdin.setRawMode(true); +process.stdin.resume(); +``` + +#### Listening for "mousepress" events + +``` js +var keypress = require('keypress'); + +// make `process.stdin` begin emitting "mousepress" (and "keypress") events +keypress(process.stdin); + +// you must enable the mouse events before they will begin firing +keypress.enableMouse(process.stdout); + +process.stdin.on('mousepress', function (info) { + console.log('got "mousepress" event at %d x %d', info.x, info.y); +}); + +process.on('exit', function () { + // disable mouse on exit, so that the state + // is back to normal for the terminal + keypress.disableMouse(process.stdout); +}); +``` + + +License +------- + +(The MIT License) + +Copyright (c) 2012 Nathan Rajlich <nathan@tootallnate.net> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/server/node_modules/express/node_modules/commander/node_modules/keypress/index.js b/server/node_modules/express/node_modules/commander/node_modules/keypress/index.js new file mode 100644 index 0000000..c2ba488 --- /dev/null +++ b/server/node_modules/express/node_modules/commander/node_modules/keypress/index.js @@ -0,0 +1,346 @@ + +/** + * This module offers the internal "keypress" functionality from node-core's + * `readline` module, for your own programs and modules to use. + * + * Usage: + * + * require('keypress')(process.stdin); + * + * process.stdin.on('keypress', function (ch, key) { + * console.log(ch, key); + * if (key.ctrl && key.name == 'c') { + * process.stdin.pause(); + * } + * }); + * proces.stdin.resume(); + */ +var exports = module.exports = keypress; + +exports.enableMouse = function (stream) { + stream.write('\x1b' +'[?1000h') +} + +exports.disableMouse = function (stream) { + stream.write('\x1b' +'[?1000l') +} + + +/** + * accepts a readable Stream instance and makes it emit "keypress" events + */ + +function keypress(stream) { + if (isEmittingKeypress(stream)) return; + stream._emitKeypress = true; + + function onData(b) { + if (stream.listeners('keypress').length > 0) { + emitKey(stream, b); + } else { + // Nobody's watching anyway + stream.removeListener('data', onData); + stream.on('newListener', onNewListener); + } + } + + function onNewListener(event) { + if (event == 'keypress') { + stream.on('data', onData); + stream.removeListener('newListener', onNewListener); + } + } + + if (stream.listeners('keypress').length > 0) { + stream.on('data', onData); + } else { + stream.on('newListener', onNewListener); + } +} + +/** + * Returns `true` if the stream is already emitting "keypress" events. + * `false` otherwise. + */ + +function isEmittingKeypress(stream) { + var rtn = stream._emitKeypress; + if (!rtn) { + // hack: check for the v0.6.x "data" event + stream.listeners('data').forEach(function (l) { + if (l.name == 'onData' && /emitKey/.test(l.toString())) { + rtn = true; + stream._emitKeypress = true; + } + }); + } + if (!rtn) { + // hack: check for the v0.6.x "newListener" event + stream.listeners('newListener').forEach(function (l) { + if (l.name == 'onNewListener' && /keypress/.test(l.toString())) { + rtn = true; + stream._emitKeypress = true; + } + }); + } + return rtn; +} + + +/* + Some patterns seen in terminal key escape codes, derived from combos seen + at http://www.midnight-commander.org/browser/lib/tty/key.c + + ESC letter + ESC [ letter + ESC [ modifier letter + ESC [ 1 ; modifier letter + ESC [ num char + ESC [ num ; modifier char + ESC O letter + ESC O modifier letter + ESC O 1 ; modifier letter + ESC N letter + ESC [ [ num ; modifier char + ESC [ [ 1 ; modifier letter + ESC ESC [ num char + ESC ESC O letter + + - char is usually ~ but $ and ^ also happen with rxvt + - modifier is 1 + + (shift * 1) + + (left_alt * 2) + + (ctrl * 4) + + (right_alt * 8) + - two leading ESCs apparently mean the same as one leading ESC +*/ + +// Regexes used for ansi escape code splitting +var metaKeyCodeRe = /^(?:\x1b)([a-zA-Z0-9])$/; +var functionKeyCodeRe = + /^(?:\x1b+)(O|N|\[|\[\[)(?:(\d+)(?:;(\d+))?([~^$])|(?:1;)?(\d+)?([a-zA-Z]))/; + +function emitKey(stream, s) { + var ch, + key = { + name: undefined, + ctrl: false, + meta: false, + shift: false + }, + parts; + + if (Buffer.isBuffer(s)) { + if (s[0] > 127 && s[1] === undefined) { + s[0] -= 128; + s = '\x1b' + s.toString(stream.encoding || 'utf-8'); + } else { + s = s.toString(stream.encoding || 'utf-8'); + } + } + + key.sequence = s; + + if (s === '\r' || s === '\n') { + // enter + key.name = 'enter'; + + } else if (s === '\t') { + // tab + key.name = 'tab'; + + } else if (s === '\b' || s === '\x7f' || + s === '\x1b\x7f' || s === '\x1b\b') { + // backspace or ctrl+h + key.name = 'backspace'; + key.meta = (s.charAt(0) === '\x1b'); + + } else if (s === '\x1b' || s === '\x1b\x1b') { + // escape key + key.name = 'escape'; + key.meta = (s.length === 2); + + } else if (s === ' ' || s === '\x1b ') { + key.name = 'space'; + key.meta = (s.length === 2); + + } else if (s <= '\x1a') { + // ctrl+letter + key.name = String.fromCharCode(s.charCodeAt(0) + 'a'.charCodeAt(0) - 1); + key.ctrl = true; + + } else if (s.length === 1 && s >= 'a' && s <= 'z') { + // lowercase letter + key.name = s; + + } else if (s.length === 1 && s >= 'A' && s <= 'Z') { + // shift+letter + key.name = s.toLowerCase(); + key.shift = true; + + } else if (parts = metaKeyCodeRe.exec(s)) { + // meta+character key + key.name = parts[1].toLowerCase(); + key.meta = true; + key.shift = /^[A-Z]$/.test(parts[1]); + + } else if (parts = functionKeyCodeRe.exec(s)) { + // ansi escape sequence + + // reassemble the key code leaving out leading \x1b's, + // the modifier key bitflag and any meaningless "1;" sequence + var code = (parts[1] || '') + (parts[2] || '') + + (parts[4] || '') + (parts[6] || ''), + modifier = (parts[3] || parts[5] || 1) - 1; + + // Parse the key modifier + key.ctrl = !!(modifier & 4); + key.meta = !!(modifier & 10); + key.shift = !!(modifier & 1); + key.code = code; + + // Parse the key itself + switch (code) { + /* xterm/gnome ESC O letter */ + case 'OP': key.name = 'f1'; break; + case 'OQ': key.name = 'f2'; break; + case 'OR': key.name = 'f3'; break; + case 'OS': key.name = 'f4'; break; + + /* xterm/rxvt ESC [ number ~ */ + case '[11~': key.name = 'f1'; break; + case '[12~': key.name = 'f2'; break; + case '[13~': key.name = 'f3'; break; + case '[14~': key.name = 'f4'; break; + + /* from Cygwin and used in libuv */ + case '[[A': key.name = 'f1'; break; + case '[[B': key.name = 'f2'; break; + case '[[C': key.name = 'f3'; break; + case '[[D': key.name = 'f4'; break; + case '[[E': key.name = 'f5'; break; + + /* common */ + case '[15~': key.name = 'f5'; break; + case '[17~': key.name = 'f6'; break; + case '[18~': key.name = 'f7'; break; + case '[19~': key.name = 'f8'; break; + case '[20~': key.name = 'f9'; break; + case '[21~': key.name = 'f10'; break; + case '[23~': key.name = 'f11'; break; + case '[24~': key.name = 'f12'; break; + + /* xterm ESC [ letter */ + case '[A': key.name = 'up'; break; + case '[B': key.name = 'down'; break; + case '[C': key.name = 'right'; break; + case '[D': key.name = 'left'; break; + case '[E': key.name = 'clear'; break; + case '[F': key.name = 'end'; break; + case '[H': key.name = 'home'; break; + + /* xterm/gnome ESC O letter */ + case 'OA': key.name = 'up'; break; + case 'OB': key.name = 'down'; break; + case 'OC': key.name = 'right'; break; + case 'OD': key.name = 'left'; break; + case 'OE': key.name = 'clear'; break; + case 'OF': key.name = 'end'; break; + case 'OH': key.name = 'home'; break; + + /* xterm/rxvt ESC [ number ~ */ + case '[1~': key.name = 'home'; break; + case '[2~': key.name = 'insert'; break; + case '[3~': key.name = 'delete'; break; + case '[4~': key.name = 'end'; break; + case '[5~': key.name = 'pageup'; break; + case '[6~': key.name = 'pagedown'; break; + + /* putty */ + case '[[5~': key.name = 'pageup'; break; + case '[[6~': key.name = 'pagedown'; break; + + /* rxvt */ + case '[7~': key.name = 'home'; break; + case '[8~': key.name = 'end'; break; + + /* rxvt keys with modifiers */ + case '[a': key.name = 'up'; key.shift = true; break; + case '[b': key.name = 'down'; key.shift = true; break; + case '[c': key.name = 'right'; key.shift = true; break; + case '[d': key.name = 'left'; key.shift = true; break; + case '[e': key.name = 'clear'; key.shift = true; break; + + case '[2$': key.name = 'insert'; key.shift = true; break; + case '[3$': key.name = 'delete'; key.shift = true; break; + case '[5$': key.name = 'pageup'; key.shift = true; break; + case '[6$': key.name = 'pagedown'; key.shift = true; break; + case '[7$': key.name = 'home'; key.shift = true; break; + case '[8$': key.name = 'end'; key.shift = true; break; + + case 'Oa': key.name = 'up'; key.ctrl = true; break; + case 'Ob': key.name = 'down'; key.ctrl = true; break; + case 'Oc': key.name = 'right'; key.ctrl = true; break; + case 'Od': key.name = 'left'; key.ctrl = true; break; + case 'Oe': key.name = 'clear'; key.ctrl = true; break; + + case '[2^': key.name = 'insert'; key.ctrl = true; break; + case '[3^': key.name = 'delete'; key.ctrl = true; break; + case '[5^': key.name = 'pageup'; key.ctrl = true; break; + case '[6^': key.name = 'pagedown'; key.ctrl = true; break; + case '[7^': key.name = 'home'; key.ctrl = true; break; + case '[8^': key.name = 'end'; key.ctrl = true; break; + + /* misc. */ + case '[Z': key.name = 'tab'; key.shift = true; break; + default: key.name = 'undefined'; break; + + } + } else if (s.length > 1 && s[0] !== '\x1b') { + // Got a longer-than-one string of characters. + // Probably a paste, since it wasn't a control sequence. + Array.prototype.forEach.call(s, function(c) { + emitKey(stream, c); + }); + return; + } + + if (key.code == '[M') { + key.name = 'mouse'; + var s = key.sequence; + var b = s.charCodeAt(3); + key.x = s.charCodeAt(4) - 040; + key.y = s.charCodeAt(5) - 040; + + key.scroll = 0; + + key.ctrl = !!(1<<4 & b); + key.meta = !!(1<<3 & b); + key.shift = !!(1<<2 & b); + + key.release = (3 & b) === 3; + + if (1<<6 & b) { //scroll + key.scroll = 1 & b ? 1 : -1; + } + + if (!key.release && !key.scroll) { + key.button = b & 3; + } + } + + // Don't emit a key if no name was found + if (key.name === undefined) { + key = undefined; + } + + if (s.length === 1) { + ch = s; + } + + if (key && key.name == 'mouse') { + stream.emit('mousepress', key) + } else if (key || ch) { + stream.emit('keypress', ch, key); + } +} diff --git a/server/node_modules/express/node_modules/commander/node_modules/keypress/package.json b/server/node_modules/express/node_modules/commander/node_modules/keypress/package.json new file mode 100644 index 0000000..c546205 --- /dev/null +++ b/server/node_modules/express/node_modules/commander/node_modules/keypress/package.json @@ -0,0 +1,32 @@ +{ + "name": "keypress", + "version": "0.1.0", + "description": "Make any Node ReadableStream emit \"keypress\" events", + "author": { + "name": "Nathan Rajlich", + "email": "nathan@tootallnate.net", + "url": "http://tootallnate.net" + }, + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git://github.com/TooTallNate/keypress.git" + }, + "keywords": [ + "keypress", + "readline", + "core" + ], + "license": "MIT", + "readme": "keypress\n========\n### Make any Node ReadableStream emit \"keypress\" events\n\n\nPrevious to Node `v0.8.x`, there was an undocumented `\"keypress\"` event that\n`process.stdin` would emit when it was a TTY. Some people discovered this hidden\ngem, and started using it in their own code.\n\nNow in Node `v0.8.x`, this `\"keypress\"` event does not get emitted by default,\nbut rather only when it is being used in conjuction with the `readline` (or by\nextension, the `repl`) module.\n\nThis module is the exact logic from the node `v0.8.x` releases ripped out into its\nown module.\n\n__Bonus:__ Now with mouse support!\n\nInstallation\n------------\n\nInstall with `npm`:\n\n``` bash\n$ npm install keypress\n```\n\nOr add it to the `\"dependencies\"` section of your _package.json_ file.\n\n\nExample\n-------\n\n#### Listening for \"keypress\" events\n\n``` js\nvar keypress = require('keypress');\n\n// make `process.stdin` begin emitting \"keypress\" events\nkeypress(process.stdin);\n\n// listen for the \"keypress\" event\nprocess.stdin.on('keypress', function (ch, key) {\n console.log('got \"keypress\"', key);\n if (key && key.ctrl && key.name == 'c') {\n process.stdin.pause();\n }\n});\n\nprocess.stdin.setRawMode(true);\nprocess.stdin.resume();\n```\n\n#### Listening for \"mousepress\" events\n\n``` js\nvar keypress = require('keypress');\n\n// make `process.stdin` begin emitting \"mousepress\" (and \"keypress\") events\nkeypress(process.stdin);\n\n// you must enable the mouse events before they will begin firing\nkeypress.enableMouse(process.stdout);\n\nprocess.stdin.on('mousepress', function (info) {\n console.log('got \"mousepress\" event at %d x %d', info.x, info.y);\n});\n\nprocess.on('exit', function () {\n // disable mouse on exit, so that the state\n // is back to normal for the terminal\n keypress.disableMouse(process.stdout);\n});\n```\n\n\nLicense\n-------\n\n(The MIT License)\n\nCopyright (c) 2012 Nathan Rajlich <nathan@tootallnate.net>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/TooTallNate/keypress/issues" + }, + "homepage": "https://github.com/TooTallNate/keypress", + "_id": "keypress@0.1.0", + "_from": "keypress@0.1.x" +} diff --git a/server/node_modules/express/node_modules/commander/node_modules/keypress/test.js b/server/node_modules/express/node_modules/commander/node_modules/keypress/test.js new file mode 100644 index 0000000..c3f61d7 --- /dev/null +++ b/server/node_modules/express/node_modules/commander/node_modules/keypress/test.js @@ -0,0 +1,28 @@ + +var keypress = require('./') +keypress(process.stdin) + +if (process.stdin.setRawMode) + process.stdin.setRawMode(true) +else + require('tty').setRawMode(true) + +process.stdin.on('keypress', function (c, key) { + console.log(0, c, key) + if (key && key.ctrl && key.name == 'c') { + process.stdin.pause() + } +}) +process.stdin.on('mousepress', function (mouse) { + console.log(mouse) +}) + +keypress.enableMouse(process.stdout) +process.on('exit', function () { + //disable mouse on exit, so that the state is back to normal + //for the terminal. + keypress.disableMouse(process.stdout) +}) + +process.stdin.resume() + diff --git a/server/node_modules/express/node_modules/commander/package.json b/server/node_modules/express/node_modules/commander/package.json new file mode 100644 index 0000000..08cdeec --- /dev/null +++ b/server/node_modules/express/node_modules/commander/package.json @@ -0,0 +1,45 @@ +{ + "name": "commander", + "version": "1.3.2", + "description": "the complete solution for node.js command-line programs", + "keywords": [ + "command", + "option", + "parser", + "prompt", + "stdin" + ], + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "repository": { + "type": "git", + "url": "https://github.com/visionmedia/commander.js.git" + }, + "dependencies": { + "keypress": "0.1.x" + }, + "devDependencies": { + "should": ">= 0.0.1" + }, + "scripts": { + "test": "make test" + }, + "main": "index", + "engines": { + "node": ">= 0.6.x" + }, + "readme": "# Commander.js\n\n The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/visionmedia/commander).\n\n [![Build Status](https://secure.travis-ci.org/visionmedia/commander.js.png)](http://travis-ci.org/visionmedia/commander.js)\n\n## Installation\n\n $ npm install commander\n\n## Option parsing\n\n Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options.\n\n```js\n#!/usr/bin/env node\n\n/**\n * Module dependencies.\n */\n\nvar program = require('commander');\n\nprogram\n .version('0.0.1')\n .option('-p, --peppers', 'Add peppers')\n .option('-P, --pineapple', 'Add pineapple')\n .option('-b, --bbq', 'Add bbq sauce')\n .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble')\n .parse(process.argv);\n\nconsole.log('you ordered a pizza with:');\nif (program.peppers) console.log(' - peppers');\nif (program.pineapple) console.log(' - pineapple');\nif (program.bbq) console.log(' - bbq');\nconsole.log(' - %s cheese', program.cheese);\n```\n\n Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as \"--template-engine\" are camel-cased, becoming `program.templateEngine` etc.\n\n## Automated --help\n\n The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free:\n\n``` \n $ ./examples/pizza --help\n\n Usage: pizza [options]\n\n Options:\n\n -V, --version output the version number\n -p, --peppers Add peppers\n -P, --pineapple Add pineapple\n -b, --bbq Add bbq sauce\n -c, --cheese Add the specified type of cheese [marble]\n -h, --help output usage information\n\n```\n\n## Coercion\n\n```js\nfunction range(val) {\n return val.split('..').map(Number);\n}\n\nfunction list(val) {\n return val.split(',');\n}\n\nprogram\n .version('0.0.1')\n .usage('[options] ')\n .option('-i, --integer ', 'An integer argument', parseInt)\n .option('-f, --float ', 'A float argument', parseFloat)\n .option('-r, --range ..', 'A range', range)\n .option('-l, --list ', 'A list', list)\n .option('-o, --optional [value]', 'An optional value')\n .parse(process.argv);\n\nconsole.log(' int: %j', program.integer);\nconsole.log(' float: %j', program.float);\nconsole.log(' optional: %j', program.optional);\nprogram.range = program.range || [];\nconsole.log(' range: %j..%j', program.range[0], program.range[1]);\nconsole.log(' list: %j', program.list);\nconsole.log(' args: %j', program.args);\n```\n\n## Custom help\n\n You can display arbitrary `-h, --help` information\n by listening for \"--help\". Commander will automatically\n exit once you are done so that the remainder of your program\n does not execute causing undesired behaviours, for example\n in the following executable \"stuff\" will not output when\n `--help` is used.\n\n```js\n#!/usr/bin/env node\n\n/**\n * Module dependencies.\n */\n\nvar program = require('../');\n\nfunction list(val) {\n return val.split(',').map(Number);\n}\n\nprogram\n .version('0.0.1')\n .option('-f, --foo', 'enable some foo')\n .option('-b, --bar', 'enable some bar')\n .option('-B, --baz', 'enable some baz');\n\n// must be before .parse() since\n// node's emit() is immediate\n\nprogram.on('--help', function(){\n console.log(' Examples:');\n console.log('');\n console.log(' $ custom-help --help');\n console.log(' $ custom-help -h');\n console.log('');\n});\n\nprogram.parse(process.argv);\n\nconsole.log('stuff');\n```\n\nyielding the following help output:\n\n```\n\nUsage: custom-help [options]\n\nOptions:\n\n -h, --help output usage information\n -V, --version output the version number\n -f, --foo enable some foo\n -b, --bar enable some bar\n -B, --baz enable some baz\n\nExamples:\n\n $ custom-help --help\n $ custom-help -h\n\n```\n\n## .prompt(msg, fn)\n\n Single-line prompt:\n\n```js\nprogram.prompt('name: ', function(name){\n console.log('hi %s', name);\n});\n```\n\n Multi-line prompt:\n\n```js\nprogram.prompt('description:', function(name){\n console.log('hi %s', name);\n});\n```\n\n Coercion:\n\n```js\nprogram.prompt('Age: ', Number, function(age){\n console.log('age: %j', age);\n});\n```\n\n```js\nprogram.prompt('Birthdate: ', Date, function(date){\n console.log('date: %s', date);\n});\n```\n\n```js\nprogram.prompt('Email: ', /^.+@.+\\..+$/, function(email){\n console.log('email: %j', email);\n});\n```\n\n## .password(msg[, mask], fn)\n\nPrompt for password without echoing:\n\n```js\nprogram.password('Password: ', function(pass){\n console.log('got \"%s\"', pass);\n process.stdin.destroy();\n});\n```\n\nPrompt for password with mask char \"*\":\n\n```js\nprogram.password('Password: ', '*', function(pass){\n console.log('got \"%s\"', pass);\n process.stdin.destroy();\n});\n```\n\n## .confirm(msg, fn)\n\n Confirm with the given `msg`:\n\n```js\nprogram.confirm('continue? ', function(ok){\n console.log(' got %j', ok);\n});\n```\n\n## .choose(list, fn)\n\n Let the user choose from a `list`:\n\n```js\nvar list = ['tobi', 'loki', 'jane', 'manny', 'luna'];\n\nconsole.log('Choose the coolest pet:');\nprogram.choose(list, function(i){\n console.log('you chose %d \"%s\"', i, list[i]);\n});\n```\n\n## .outputHelp()\n\n Output help information without exiting.\n\n## .help()\n\n Output help information and exit immediately.\n\n## Links\n\n - [API documentation](http://visionmedia.github.com/commander.js/)\n - [ascii tables](https://github.com/LearnBoost/cli-table)\n - [progress bars](https://github.com/visionmedia/node-progress)\n - [more progress bars](https://github.com/substack/node-multimeter)\n - [examples](https://github.com/visionmedia/commander.js/tree/master/examples)\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", + "readmeFilename": "Readme.md", + "bugs": { + "url": "https://github.com/visionmedia/commander.js/issues" + }, + "homepage": "https://github.com/visionmedia/commander.js", + "_id": "commander@1.3.2", + "dist": { + "shasum": "71f3734fdb2cf83f8502701b993e3bbf8ed3bb2e" + }, + "_from": "commander@1.3.2", + "_resolved": "https://registry.npmjs.org/commander/-/commander-1.3.2.tgz" +} diff --git a/server/node_modules/express/node_modules/connect/.npmignore b/server/node_modules/express/node_modules/connect/.npmignore new file mode 100644 index 0000000..9046dde --- /dev/null +++ b/server/node_modules/express/node_modules/connect/.npmignore @@ -0,0 +1,12 @@ +*.markdown +*.md +.git* +Makefile +benchmarks/ +docs/ +examples/ +install.sh +support/ +test/ +.DS_Store +coverage.html diff --git a/server/node_modules/express/node_modules/connect/.travis.yml b/server/node_modules/express/node_modules/connect/.travis.yml new file mode 100644 index 0000000..a12e3f0 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - "0.8" + - "0.10" \ No newline at end of file diff --git a/server/node_modules/express/node_modules/connect/LICENSE b/server/node_modules/express/node_modules/connect/LICENSE new file mode 100644 index 0000000..0c5d22d --- /dev/null +++ b/server/node_modules/express/node_modules/connect/LICENSE @@ -0,0 +1,24 @@ +(The MIT License) + +Copyright (c) 2010 Sencha Inc. +Copyright (c) 2011 LearnBoost +Copyright (c) 2011 TJ Holowaychuk + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/server/node_modules/express/node_modules/connect/Readme.md b/server/node_modules/express/node_modules/connect/Readme.md new file mode 100644 index 0000000..e38a326 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/Readme.md @@ -0,0 +1,84 @@ +# Connect [![build status](https://secure.travis-ci.org/senchalabs/connect.png)](http://travis-ci.org/senchalabs/connect) + + Connect is an extensible HTTP server framework for [node](http://nodejs.org), providing high performance "plugins" known as _middleware_. + + Connect is bundled with over _20_ commonly used middleware, including + a logger, session support, cookie parser, and [more](http://senchalabs.github.com/connect). Be sure to view the 2.x [documentation](http://senchalabs.github.com/connect/). + +```js +var connect = require('connect') + , http = require('http'); + +var app = connect() + .use(connect.favicon()) + .use(connect.logger('dev')) + .use(connect.static('public')) + .use(connect.directory('public')) + .use(connect.cookieParser()) + .use(connect.session({ secret: 'my secret here' })) + .use(function(req, res){ + res.end('Hello from Connect!\n'); + }); + +http.createServer(app).listen(3000); +``` + +## Middleware + + - [basicAuth](http://www.senchalabs.org/connect/basicAuth.html) + - [bodyParser](http://www.senchalabs.org/connect/bodyParser.html) + - [compress](http://www.senchalabs.org/connect/compress.html) + - [cookieParser](http://www.senchalabs.org/connect/cookieParser.html) + - [cookieSession](http://www.senchalabs.org/connect/cookieSession.html) + - [csrf](http://www.senchalabs.org/connect/csrf.html) + - [directory](http://www.senchalabs.org/connect/directory.html) + - [errorHandler](http://www.senchalabs.org/connect/errorHandler.html) + - [favicon](http://www.senchalabs.org/connect/favicon.html) + - [json](http://www.senchalabs.org/connect/json.html) + - [limit](http://www.senchalabs.org/connect/limit.html) + - [logger](http://www.senchalabs.org/connect/logger.html) + - [methodOverride](http://www.senchalabs.org/connect/methodOverride.html) + - [multipart](http://www.senchalabs.org/connect/multipart.html) + - [urlencoded](http://www.senchalabs.org/connect/urlencoded.html) + - [query](http://www.senchalabs.org/connect/query.html) + - [responseTime](http://www.senchalabs.org/connect/responseTime.html) + - [session](http://www.senchalabs.org/connect/session.html) + - [static](http://www.senchalabs.org/connect/static.html) + - [staticCache](http://www.senchalabs.org/connect/staticCache.html) + - [subdomains](http://www.senchalabs.org/connect/subdomains.html) + - [vhost](http://www.senchalabs.org/connect/vhost.html) + +## Running Tests + +first: + + $ npm install -d + +then: + + $ make test + +## Contributors + + https://github.com/senchalabs/connect/graphs/contributors + +## Node Compatibility + + Connect `< 1.x` is compatible with node 0.2.x + + + Connect `1.x` is compatible with node 0.4.x + + + Connect `2.x` is compatible with node 0.6.x + + + Connect (_master_) is compatible with node 0.8.x + +## CLA + + [http://sencha.com/cla](http://sencha.com/cla) + +## License + +View the [LICENSE](https://github.com/senchalabs/connect/blob/master/LICENSE) file. The [Silk](http://www.famfamfam.com/lab/icons/silk/) icons used by the `directory` middleware created by/copyright of [FAMFAMFAM](http://www.famfamfam.com/). diff --git a/server/node_modules/express/node_modules/connect/index.js b/server/node_modules/express/node_modules/connect/index.js new file mode 100644 index 0000000..23240ee --- /dev/null +++ b/server/node_modules/express/node_modules/connect/index.js @@ -0,0 +1,4 @@ + +module.exports = process.env.CONNECT_COV + ? require('./lib-cov/connect') + : require('./lib/connect'); \ No newline at end of file diff --git a/server/node_modules/express/node_modules/connect/lib/cache.js b/server/node_modules/express/node_modules/connect/lib/cache.js new file mode 100644 index 0000000..052fcdb --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/cache.js @@ -0,0 +1,81 @@ + +/*! + * Connect - Cache + * Copyright(c) 2011 Sencha Inc. + * MIT Licensed + */ + +/** + * Expose `Cache`. + */ + +module.exports = Cache; + +/** + * LRU cache store. + * + * @param {Number} limit + * @api private + */ + +function Cache(limit) { + this.store = {}; + this.keys = []; + this.limit = limit; +} + +/** + * Touch `key`, promoting the object. + * + * @param {String} key + * @param {Number} i + * @api private + */ + +Cache.prototype.touch = function(key, i){ + this.keys.splice(i,1); + this.keys.push(key); +}; + +/** + * Remove `key`. + * + * @param {String} key + * @api private + */ + +Cache.prototype.remove = function(key){ + delete this.store[key]; +}; + +/** + * Get the object stored for `key`. + * + * @param {String} key + * @return {Array} + * @api private + */ + +Cache.prototype.get = function(key){ + return this.store[key]; +}; + +/** + * Add a cache `key`. + * + * @param {String} key + * @return {Array} + * @api private + */ + +Cache.prototype.add = function(key){ + // initialize store + var len = this.keys.push(key); + + // limit reached, invalidate LRU + if (len > this.limit) this.remove(this.keys.shift()); + + var arr = this.store[key] = []; + arr.createdAt = new Date; + return arr; +}; diff --git a/server/node_modules/express/node_modules/connect/lib/connect.js b/server/node_modules/express/node_modules/connect/lib/connect.js new file mode 100644 index 0000000..72961dc --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/connect.js @@ -0,0 +1,92 @@ +/*! + * Connect + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter + , proto = require('./proto') + , utils = require('./utils') + , path = require('path') + , basename = path.basename + , fs = require('fs'); + +// node patches + +require('./patch'); + +// expose createServer() as the module + +exports = module.exports = createServer; + +/** + * Framework version. + */ + +exports.version = '2.7.11'; + +/** + * Expose mime module. + */ + +exports.mime = require('./middleware/static').mime; + +/** + * Expose the prototype. + */ + +exports.proto = proto; + +/** + * Auto-load middleware getters. + */ + +exports.middleware = {}; + +/** + * Expose utilities. + */ + +exports.utils = utils; + +/** + * Create a new connect server. + * + * @return {Function} + * @api public + */ + +function createServer() { + function app(req, res, next){ app.handle(req, res, next); } + utils.merge(app, proto); + utils.merge(app, EventEmitter.prototype); + app.route = '/'; + app.stack = []; + for (var i = 0; i < arguments.length; ++i) { + app.use(arguments[i]); + } + return app; +}; + +/** + * Support old `.createServer()` method. + */ + +createServer.createServer = createServer; + +/** + * Auto-load bundled middleware with getters. + */ + +fs.readdirSync(__dirname + '/middleware').forEach(function(filename){ + if (!/\.js$/.test(filename)) return; + var name = basename(filename, '.js'); + function load(){ return require('./middleware/' + name); } + exports.middleware.__defineGetter__(name, load); + exports.__defineGetter__(name, load); +}); diff --git a/server/node_modules/express/node_modules/connect/lib/index.js b/server/node_modules/express/node_modules/connect/lib/index.js new file mode 100644 index 0000000..2618ddc --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/index.js @@ -0,0 +1,50 @@ + +/** + * Connect is a middleware framework for node, + * shipping with over 18 bundled middleware and a rich selection of + * 3rd-party middleware. + * + * var app = connect() + * .use(connect.logger('dev')) + * .use(connect.static('public')) + * .use(function(req, res){ + * res.end('hello world\n'); + * }) + * .listen(3000); + * + * Installation: + * + * $ npm install connect + * + * Middleware: + * + * - [logger](logger.html) request logger with custom format support + * - [csrf](csrf.html) Cross-site request forgery protection + * - [compress](compress.html) Gzip compression middleware + * - [basicAuth](basicAuth.html) basic http authentication + * - [bodyParser](bodyParser.html) extensible request body parser + * - [json](json.html) application/json parser + * - [urlencoded](urlencoded.html) application/x-www-form-urlencoded parser + * - [multipart](multipart.html) multipart/form-data parser + * - [timeout](timeout.html) request timeouts + * - [cookieParser](cookieParser.html) cookie parser + * - [session](session.html) session management support with bundled MemoryStore + * - [cookieSession](cookieSession.html) cookie-based session support + * - [methodOverride](methodOverride.html) faux HTTP method support + * - [responseTime](responseTime.html) calculates response-time and exposes via X-Response-Time + * - [staticCache](staticCache.html) memory cache layer for the static() middleware + * - [static](static.html) streaming static file server supporting `Range` and more + * - [directory](directory.html) directory listing middleware + * - [vhost](vhost.html) virtual host sub-domain mapping middleware + * - [favicon](favicon.html) efficient favicon server (with default icon) + * - [limit](limit.html) limit the bytesize of request bodies + * - [query](query.html) automatic querystring parser, populating `req.query` + * - [errorHandler](errorHandler.html) flexible error handler + * + * Links: + * + * - list of [3rd-party](https://github.com/senchalabs/connect/wiki) middleware + * - GitHub [repository](http://github.com/senchalabs/connect) + * - [test documentation](https://github.com/senchalabs/connect/blob/gh-pages/tests.md) + * + */ \ No newline at end of file diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/basicAuth.js b/server/node_modules/express/node_modules/connect/lib/middleware/basicAuth.js new file mode 100644 index 0000000..59527e6 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/basicAuth.js @@ -0,0 +1,102 @@ +/*! + * Connect - basicAuth + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../utils') + , unauthorized = utils.unauthorized; + +/** + * Basic Auth: + * + * Enfore basic authentication by providing a `callback(user, pass)`, + * which must return `true` in order to gain access. Alternatively an async + * method is provided as well, invoking `callback(user, pass, callback)`. Populates + * `req.user`. The final alternative is simply passing username / password + * strings. + * + * Simple username and password + * + * connect(connect.basicAuth('username', 'password')); + * + * Callback verification + * + * connect() + * .use(connect.basicAuth(function(user, pass){ + * return 'tj' == user && 'wahoo' == pass; + * })) + * + * Async callback verification, accepting `fn(err, user)`. + * + * connect() + * .use(connect.basicAuth(function(user, pass, fn){ + * User.authenticate({ user: user, pass: pass }, fn); + * })) + * + * @param {Function|String} callback or username + * @param {String} realm + * @api public + */ + +module.exports = function basicAuth(callback, realm) { + var username, password; + + // user / pass strings + if ('string' == typeof callback) { + username = callback; + password = realm; + if ('string' != typeof password) throw new Error('password argument required'); + realm = arguments[2]; + callback = function(user, pass){ + return user == username && pass == password; + } + } + + realm = realm || 'Authorization Required'; + + return function(req, res, next) { + var authorization = req.headers.authorization; + + if (req.user) return next(); + if (!authorization) return unauthorized(res, realm); + + var parts = authorization.split(' '); + + if (parts.length !== 2) return next(utils.error(400)); + + var scheme = parts[0] + , credentials = new Buffer(parts[1], 'base64').toString() + , index = credentials.indexOf(':'); + + if ('Basic' != scheme || index < 0) return next(utils.error(400)); + + var user = credentials.slice(0, index) + , pass = credentials.slice(index + 1); + + // async + if (callback.length >= 3) { + var pause = utils.pause(req); + callback(user, pass, function(err, user){ + if (err || !user) return unauthorized(res, realm); + req.user = req.remoteUser = user; + next(); + pause.resume(); + }); + // sync + } else { + if (callback(user, pass)) { + req.user = req.remoteUser = user; + next(); + } else { + unauthorized(res, realm); + } + } + } +}; + diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/bodyParser.js b/server/node_modules/express/node_modules/connect/lib/middleware/bodyParser.js new file mode 100644 index 0000000..9f692cd --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/bodyParser.js @@ -0,0 +1,61 @@ + +/*! + * Connect - bodyParser + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var multipart = require('./multipart') + , urlencoded = require('./urlencoded') + , json = require('./json'); + +/** + * Body parser: + * + * Parse request bodies, supports _application/json_, + * _application/x-www-form-urlencoded_, and _multipart/form-data_. + * + * This is equivalent to: + * + * app.use(connect.json()); + * app.use(connect.urlencoded()); + * app.use(connect.multipart()); + * + * Examples: + * + * connect() + * .use(connect.bodyParser()) + * .use(function(req, res) { + * res.end('viewing user ' + req.body.user.name); + * }); + * + * $ curl -d 'user[name]=tj' http://local/ + * $ curl -d '{"user":{"name":"tj"}}' -H "Content-Type: application/json" http://local/ + * + * View [json](json.html), [urlencoded](urlencoded.html), and [multipart](multipart.html) for more info. + * + * @param {Object} options + * @return {Function} + * @api public + */ + +exports = module.exports = function bodyParser(options){ + var _urlencoded = urlencoded(options) + , _multipart = multipart(options) + , _json = json(options); + + return function bodyParser(req, res, next) { + _json(req, res, function(err){ + if (err) return next(err); + _urlencoded(req, res, function(err){ + if (err) return next(err); + _multipart(req, res, next); + }); + }); + } +}; \ No newline at end of file diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/compress.js b/server/node_modules/express/node_modules/connect/lib/middleware/compress.js new file mode 100644 index 0000000..a300e53 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/compress.js @@ -0,0 +1,192 @@ +/*! + * Connect - compress + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var zlib = require('zlib'); +var utils = require('../utils'); +var Negotiator = require('negotiator'); + +/** + * Supported content-encoding methods. + */ + +exports.methods = { + gzip: zlib.createGzip + , deflate: zlib.createDeflate +}; + +/** + * Default filter function. + */ + +exports.filter = function(req, res){ + return /json|text|javascript|dart|image\/svg\+xml|application\/x-font-ttf|application\/vnd\.ms-opentype|application\/vnd\.ms-fontobject/.test(res.getHeader('Content-Type')); +}; + +/** + * Compress: + * + * Compress response data with gzip/deflate. + * + * Filter: + * + * A `filter` callback function may be passed to + * replace the default logic of: + * + * exports.filter = function(req, res){ + * return /json|text|javascript/.test(res.getHeader('Content-Type')); + * }; + * + * Threshold: + * + * Only compress the response if the byte size is at or above a threshold. + * Always compress while streaming. + * + * - `threshold` - string representation of size or bytes as an integer. + * + * Options: + * + * All remaining options are passed to the gzip/deflate + * creation functions. Consult node's docs for additional details. + * + * - `chunkSize` (default: 16*1024) + * - `windowBits` + * - `level`: 0-9 where 0 is no compression, and 9 is slow but best compression + * - `memLevel`: 1-9 low is slower but uses less memory, high is fast but uses more + * - `strategy`: compression strategy + * + * @param {Object} options + * @return {Function} + * @api public + */ + +module.exports = function compress(options) { + options = options || {}; + var filter = options.filter || exports.filter; + var threshold; + + if (false === options.threshold || 0 === options.threshold) { + threshold = 0 + } else if ('string' === typeof options.threshold) { + threshold = utils.parseBytes(options.threshold) + } else { + threshold = options.threshold || 1024 + } + + return function compress(req, res, next){ + var accept = req.headers['accept-encoding'] + , write = res.write + , end = res.end + , compress = true + , stream; + + // see #724 + req.on('close', function(){ + res.write = res.end = function(){}; + }); + + // flush is noop by default + res.flush = noop; + + // proxy + + res.write = function(chunk, encoding){ + if (!this.headerSent) { + // if content-length is set and is lower + // than the threshold, don't compress + var length = res.getHeader('content-length'); + if (!isNaN(length) && length < threshold) compress = false; + this._implicitHeader(); + } + return stream + ? stream.write(new Buffer(chunk, encoding)) + : write.call(res, chunk, encoding); + }; + + res.end = function(chunk, encoding){ + if (chunk) { + if (!this.headerSent && getSize(chunk) < threshold) compress = false; + this.write(chunk, encoding); + } else if (!this.headerSent) { + // response size === 0 + compress = false; + } + return stream + ? stream.end() + : end.call(res); + }; + + res.on('header', function(){ + // default request filter + if (!filter(req, res)) return; + + // vary + var vary = res.getHeader('Vary'); + if (!vary) { + res.setHeader('Vary', 'Accept-Encoding'); + } else if (!~vary.indexOf('Accept-Encoding')) { + res.setHeader('Vary', vary + ', Accept-Encoding'); + } + + if (!compress) return; + + var encoding = res.getHeader('Content-Encoding') || 'identity'; + + // already encoded + if ('identity' != encoding) return; + + // SHOULD use identity + if (!accept) return; + + // head + if ('HEAD' == req.method) return; + + // compression method + var method = new Negotiator(req).preferredEncoding(['gzip', 'deflate', 'identity']); + // negotiation failed + if (method === 'identity') return; + + // compression stream + stream = exports.methods[method](options); + + // overwrite the flush method + res.flush = function(){ + stream.flush(); + } + + // header fields + res.setHeader('Content-Encoding', method); + res.removeHeader('Content-Length'); + + // compression + stream.on('data', function(chunk){ + write.call(res, chunk); + }); + + stream.on('end', function(){ + end.call(res); + }); + + stream.on('drain', function() { + res.emit('drain'); + }); + }); + + next(); + }; +}; + +function getSize(chunk) { + return Buffer.isBuffer(chunk) + ? chunk.length + : Buffer.byteLength(chunk); +} + +function noop(){} diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/cookieParser.js b/server/node_modules/express/node_modules/connect/lib/middleware/cookieParser.js new file mode 100644 index 0000000..1dfa25f --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/cookieParser.js @@ -0,0 +1,62 @@ + +/*! + * Connect - cookieParser + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('./../utils') + , cookie = require('cookie'); + +/** + * Cookie parser: + * + * Parse _Cookie_ header and populate `req.cookies` + * with an object keyed by the cookie names. Optionally + * you may enabled signed cookie support by passing + * a `secret` string, which assigns `req.secret` so + * it may be used by other middleware. + * + * Examples: + * + * connect() + * .use(connect.cookieParser('optional secret string')) + * .use(function(req, res, next){ + * res.end(JSON.stringify(req.cookies)); + * }) + * + * @param {String} secret + * @return {Function} + * @api public + */ + +module.exports = function cookieParser(secret, opt){ + return function cookieParser(req, res, next) { + if (req.cookies) return next(); + var cookies = req.headers.cookie; + + req.secret = secret; + req.cookies = {}; + req.signedCookies = {}; + + if (cookies) { + try { + req.cookies = cookie.parse(cookies, opt); + if (secret) { + req.signedCookies = utils.parseSignedCookies(req.cookies, secret); + req.signedCookies = utils.parseJSONCookies(req.signedCookies); + } + req.cookies = utils.parseJSONCookies(req.cookies); + } catch (err) { + err.status = 400; + return next(err); + } + } + next(); + }; +}; diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/cookieSession.js b/server/node_modules/express/node_modules/connect/lib/middleware/cookieSession.js new file mode 100644 index 0000000..bcf5aed --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/cookieSession.js @@ -0,0 +1,117 @@ +/*! + * Connect - cookieSession + * Copyright(c) 2011 Sencha Inc. + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('./../utils') + , Cookie = require('./session/cookie') + , debug = require('debug')('connect:cookieSession') + , signature = require('cookie-signature') + , crc32 = require('buffer-crc32') + , url = require('url'); + +/** + * Cookie Session: + * + * Cookie session middleware. + * + * var app = connect(); + * app.use(connect.cookieParser()); + * app.use(connect.cookieSession({ secret: 'tobo!', cookie: { maxAge: 60 * 60 * 1000 }})); + * + * Options: + * + * - `key` cookie name defaulting to `connect.sess` + * - `secret` prevents cookie tampering + * - `cookie` session cookie settings, defaulting to `{ path: '/', httpOnly: true, maxAge: null }` + * - `proxy` trust the reverse proxy when setting secure cookies (via "x-forwarded-proto") + * + * Clearing sessions: + * + * To clear the session simply set its value to `null`, + * `cookieSession()` will then respond with a 1970 Set-Cookie. + * + * req.session = null; + * + * @param {Object} options + * @return {Function} + * @api public + */ + +module.exports = function cookieSession(options){ + // TODO: utilize Session/Cookie to unify API + options = options || {}; + var key = options.key || 'connect.sess' + , trustProxy = options.proxy; + + return function cookieSession(req, res, next) { + + // req.secret is for backwards compatibility + var secret = options.secret || req.secret; + if (!secret) throw new Error('`secret` option required for cookie sessions'); + + // default session + req.session = {}; + var cookie = req.session.cookie = new Cookie(options.cookie); + + // pathname mismatch + var originalPath = url.parse(req.originalUrl).pathname; + if (0 != originalPath.indexOf(cookie.path)) return next(); + + // cookieParser secret + if (!options.secret && req.secret) { + req.session = req.signedCookies[key] || {}; + req.session.cookie = cookie; + } else { + // TODO: refactor + var rawCookie = req.cookies[key]; + if (rawCookie) { + var unsigned = utils.parseSignedCookie(rawCookie, secret); + if (unsigned) { + var originalHash = crc32.signed(unsigned); + req.session = utils.parseJSONCookie(unsigned) || {}; + req.session.cookie = cookie; + } + } + } + + res.on('header', function(){ + // removed + if (!req.session) { + debug('clear session'); + cookie.expires = new Date(0); + res.setHeader('Set-Cookie', cookie.serialize(key, '')); + return; + } + + delete req.session.cookie; + + // check security + var proto = (req.headers['x-forwarded-proto'] || '').toLowerCase() + , tls = req.connection.encrypted || (trustProxy && 'https' == proto.split(/\s*,\s*/)[0]); + + // only send secure cookies via https + if (cookie.secure && !tls) return debug('not secured'); + + // serialize + debug('serializing %j', req.session); + var val = 'j:' + JSON.stringify(req.session); + + // compare hashes, no need to set-cookie if unchanged + if (originalHash == crc32.signed(val)) return debug('unmodified session'); + + // set-cookie + val = 's:' + signature.sign(val, secret); + val = cookie.serialize(key, val); + debug('set-cookie %j', cookie); + res.setHeader('Set-Cookie', val); + }); + + next(); + }; +}; diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/csrf.js b/server/node_modules/express/node_modules/connect/lib/middleware/csrf.js new file mode 100644 index 0000000..1c3854b --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/csrf.js @@ -0,0 +1,163 @@ +/*! + * Connect - csrf + * Copyright(c) 2011 Sencha Inc. + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../utils'); +var uid = require('uid2'); +var crypto = require('crypto'); + +/** + * Anti CSRF: + * + * CSRF protection middleware. + * + * This middleware adds a `req.csrfToken()` function to make a token + * which should be added to requests which mutate + * state, within a hidden form field, query-string etc. This + * token is validated against the visitor's session. + * + * The default `value` function checks `req.body` generated + * by the `bodyParser()` middleware, `req.query` generated + * by `query()`, and the "X-CSRF-Token" header field. + * + * This middleware requires session support, thus should be added + * somewhere _below_ `session()` and `cookieParser()`. + * + * Options: + * + * - `value` a function accepting the request, returning the token + * + * @param {Object} options + * @api public + */ + +module.exports = function csrf(options) { + options = options || {}; + var value = options.value || defaultValue; + + return function(req, res, next){ + + // already have one + var secret = req.session._csrfSecret; + if (secret) return createToken(secret); + + // generate secret + uid(24, function(err, secret){ + if (err) return next(err); + req.session._csrfSecret = secret; + createToken(secret); + }); + + // generate the token + function createToken(secret) { + var token; + + // lazy-load token + req.csrfToken = function csrfToken() { + return token || (token = saltedToken(secret)); + }; + + // compatibility with old middleware + Object.defineProperty(req.session, '_csrf', { + configurable: true, + get: function() { + console.warn('req.session._csrf is deprecated, use req.csrfToken() instead'); + return req.csrfToken(); + } + }); + + // ignore these methods + if ('GET' == req.method || 'HEAD' == req.method || 'OPTIONS' == req.method) return next(); + + // determine user-submitted value + var val = value(req); + + // check + if (!checkToken(val, secret)) return next(utils.error(403)); + + next(); + } + } +}; + +/** + * Default value function, checking the `req.body` + * and `req.query` for the CSRF token. + * + * @param {IncomingMessage} req + * @return {String} + * @api private + */ + +function defaultValue(req) { + return (req.body && req.body._csrf) + || (req.query && req.query._csrf) + || (req.headers['x-csrf-token']) + || (req.headers['x-xsrf-token']); +} + +/** + * Return salted token. + * + * @param {String} secret + * @return {String} + * @api private + */ + +function saltedToken(secret) { + return createToken(generateSalt(10), secret); +} + +/** + * Creates a CSRF token from a given salt and secret. + * + * @param {String} salt (should be 10 characters) + * @param {String} secret + * @return {String} + * @api private + */ + +function createToken(salt, secret) { + return salt + crypto + .createHash('sha1') + .update(salt + secret) + .digest('base64'); +} + +/** + * Checks if a given CSRF token matches the given secret. + * + * @param {String} token + * @param {String} secret + * @return {Boolean} + * @api private + */ + +function checkToken(token, secret) { + if ('string' != typeof token) return false; + return token === createToken(token.slice(0, 10), secret); +} + +/** + * Generates a random salt, using a fast non-blocking PRNG (Math.random()). + * + * @param {Number} length + * @return {String} + * @api private + */ + +function generateSalt(length) { + var i, r = []; + for (i = 0; i < length; ++i) { + r.push(SALTCHARS[Math.floor(Math.random() * SALTCHARS.length)]); + } + return r.join(''); +} + +var SALTCHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/directory.js b/server/node_modules/express/node_modules/connect/lib/middleware/directory.js new file mode 100644 index 0000000..1d253dc --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/directory.js @@ -0,0 +1,245 @@ + +/*! + * Connect - directory + * Copyright(c) 2011 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +// TODO: icon / style for directories +// TODO: arrow key navigation +// TODO: make icons extensible + +/** + * Module dependencies. + */ + +var fs = require('fs') + , parse = require('url').parse + , utils = require('../utils') + , path = require('path') + , normalize = path.normalize + , sep = path.sep + , extname = path.extname + , join = path.join; +var Negotiator = require('negotiator'); + +/*! + * Icon cache. + */ + +var cache = {}; + +/** + * Media types and the map for content negotiation. + */ + +var mediaTypes = [ + 'text/html', + 'text/plain', + 'application/json' +]; + +var mediaType = { + 'text/html': 'html', + 'text/plain': 'plain', + 'application/json': 'json' +}; + +/** + * Directory: + * + * Serve directory listings with the given `root` path. + * + * Options: + * + * - `hidden` display hidden (dot) files. Defaults to false. + * - `icons` display icons. Defaults to false. + * - `filter` Apply this filter function to files. Defaults to false. + * + * @param {String} root + * @param {Object} options + * @return {Function} + * @api public + */ + +exports = module.exports = function directory(root, options){ + options = options || {}; + + // root required + if (!root) throw new Error('directory() root path required'); + var hidden = options.hidden + , icons = options.icons + , filter = options.filter + , root = normalize(root + sep); + + return function directory(req, res, next) { + if ('GET' != req.method && 'HEAD' != req.method) return next(); + + var url = parse(req.url) + , dir = decodeURIComponent(url.pathname) + , path = normalize(join(root, dir)) + , originalUrl = parse(req.originalUrl) + , originalDir = decodeURIComponent(originalUrl.pathname) + , showUp = path != root; + + // null byte(s), bad request + if (~path.indexOf('\0')) return next(utils.error(400)); + + // malicious path, forbidden + if (0 != path.indexOf(root)) return next(utils.error(403)); + + // check if we have a directory + fs.stat(path, function(err, stat){ + if (err) return 'ENOENT' == err.code + ? next() + : next(err); + + if (!stat.isDirectory()) return next(); + + // fetch files + fs.readdir(path, function(err, files){ + if (err) return next(err); + if (!hidden) files = removeHidden(files); + if (filter) files = files.filter(filter); + files.sort(); + + // content-negotiation + var type = new Negotiator(req).preferredMediaType(mediaTypes); + + // not acceptable + if (!type) return next(utils.error(406)); + exports[mediaType[type]](req, res, files, next, originalDir, showUp, icons); + }); + }); + }; +}; + +/** + * Respond with text/html. + */ + +exports.html = function(req, res, files, next, dir, showUp, icons){ + fs.readFile(__dirname + '/../public/directory.html', 'utf8', function(err, str){ + if (err) return next(err); + fs.readFile(__dirname + '/../public/style.css', 'utf8', function(err, style){ + if (err) return next(err); + if (showUp) files.unshift('..'); + str = str + .replace('{style}', style) + .replace('{files}', html(files, dir, icons)) + .replace('{directory}', dir) + .replace('{linked-path}', htmlPath(dir)); + res.setHeader('Content-Type', 'text/html'); + res.setHeader('Content-Length', str.length); + res.end(str); + }); + }); +}; + +/** + * Respond with application/json. + */ + +exports.json = function(req, res, files){ + files = JSON.stringify(files); + res.setHeader('Content-Type', 'application/json'); + res.setHeader('Content-Length', files.length); + res.end(files); +}; + +/** + * Respond with text/plain. + */ + +exports.plain = function(req, res, files){ + files = files.join('\n') + '\n'; + res.setHeader('Content-Type', 'text/plain'); + res.setHeader('Content-Length', files.length); + res.end(files); +}; + +/** + * Map html `dir`, returning a linked path. + */ + +function htmlPath(dir) { + var curr = []; + return dir.split('/').map(function(part){ + curr.push(encodeURIComponent(part)); + return part ? '' + part + '' : ''; + }).join(' / '); +} + +/** + * Map html `files`, returning an html unordered list. + */ + +function html(files, dir, useIcons) { + return '
    ' + files.map(function(file){ + var icon = '' + , classes = [] + , path = dir.split('/').map(function (c) { return encodeURIComponent(c); }); + + if (useIcons && '..' != file) { + icon = icons[extname(file)] || icons.default; + icon = ''; + classes.push('icon'); + } + + path.push(encodeURIComponent(file)); + + return '
  • ' + + icon + file + '
  • '; + + }).join('\n') + '
'; +} + +/** + * Load and cache the given `icon`. + * + * @param {String} icon + * @return {String} + * @api private + */ + +function load(icon) { + if (cache[icon]) return cache[icon]; + return cache[icon] = fs.readFileSync(__dirname + '/../public/icons/' + icon, 'base64'); +} + +/** + * Filter "hidden" `files`, aka files + * beginning with a `.`. + * + * @param {Array} files + * @return {Array} + * @api private + */ + +function removeHidden(files) { + return files.filter(function(file){ + return '.' != file[0]; + }); +} + +/** + * Icon map. + */ + +var icons = { + '.js': 'page_white_code_red.png' + , '.c': 'page_white_c.png' + , '.h': 'page_white_h.png' + , '.cc': 'page_white_cplusplus.png' + , '.php': 'page_white_php.png' + , '.rb': 'page_white_ruby.png' + , '.cpp': 'page_white_cplusplus.png' + , '.swf': 'page_white_flash.png' + , '.pdf': 'page_white_acrobat.png' + , 'default': 'page_white.png' +}; diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/errorHandler.js b/server/node_modules/express/node_modules/connect/lib/middleware/errorHandler.js new file mode 100644 index 0000000..07ec9e4 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/errorHandler.js @@ -0,0 +1,86 @@ +/*! + * Connect - errorHandler + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../utils') + , fs = require('fs'); + +// environment + +var env = process.env.NODE_ENV || 'development'; + +/** + * Error handler: + * + * Development error handler, providing stack traces + * and error message responses for requests accepting text, html, + * or json. + * + * Text: + * + * By default, and when _text/plain_ is accepted a simple stack trace + * or error message will be returned. + * + * JSON: + * + * When _application/json_ is accepted, connect will respond with + * an object in the form of `{ "error": error }`. + * + * HTML: + * + * When accepted connect will output a nice html stack trace. + * + * @return {Function} + * @api public + */ + +exports = module.exports = function errorHandler(){ + return function errorHandler(err, req, res, next){ + if (err.status) res.statusCode = err.status; + if (res.statusCode < 400) res.statusCode = 500; + if ('test' != env) console.error(err.stack); + var accept = req.headers.accept || ''; + // html + if (~accept.indexOf('html')) { + fs.readFile(__dirname + '/../public/style.css', 'utf8', function(e, style){ + fs.readFile(__dirname + '/../public/error.html', 'utf8', function(e, html){ + var stack = (err.stack || '') + .split('\n').slice(1) + .map(function(v){ return '
  • ' + v + '
  • '; }).join(''); + html = html + .replace('{style}', style) + .replace('{stack}', stack) + .replace('{title}', exports.title) + .replace('{statusCode}', res.statusCode) + .replace(/\{error\}/g, utils.escape(err.toString().replace(/\n/g, '
    '))); + res.setHeader('Content-Type', 'text/html; charset=utf-8'); + res.end(html); + }); + }); + // json + } else if (~accept.indexOf('json')) { + var error = { message: err.message, stack: err.stack }; + for (var prop in err) error[prop] = err[prop]; + var json = JSON.stringify({ error: error }); + res.setHeader('Content-Type', 'application/json'); + res.end(json); + // plain text + } else { + res.writeHead(res.statusCode, { 'Content-Type': 'text/plain' }); + res.end(err.stack); + } + }; +}; + +/** + * Template title, framework authors may override this value. + */ + +exports.title = 'Connect'; diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/favicon.js b/server/node_modules/express/node_modules/connect/lib/middleware/favicon.js new file mode 100644 index 0000000..ef54354 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/favicon.js @@ -0,0 +1,80 @@ +/*! + * Connect - favicon + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var fs = require('fs') + , utils = require('../utils'); + +/** + * Favicon: + * + * By default serves the connect favicon, or the favicon + * located by the given `path`. + * + * Options: + * + * - `maxAge` cache-control max-age directive, defaulting to 1 day + * + * Examples: + * + * Serve default favicon: + * + * connect() + * .use(connect.favicon()) + * + * Serve favicon before logging for brevity: + * + * connect() + * .use(connect.favicon()) + * .use(connect.logger('dev')) + * + * Serve custom favicon: + * + * connect() + * .use(connect.favicon('public/favicon.ico')) + * + * @param {String} path + * @param {Object} options + * @return {Function} + * @api public + */ + +module.exports = function favicon(path, options){ + var options = options || {} + , path = path || __dirname + '/../public/favicon.ico' + , maxAge = options.maxAge || 86400000 + , icon; // favicon cache + + return function favicon(req, res, next){ + if ('/favicon.ico' == req.url) { + if (icon) { + res.writeHead(200, icon.headers); + res.end(icon.body); + } else { + fs.readFile(path, function(err, buf){ + if (err) return next(err); + icon = { + headers: { + 'Content-Type': 'image/x-icon' + , 'Content-Length': buf.length + , 'ETag': '"' + utils.md5(buf) + '"' + , 'Cache-Control': 'public, max-age=' + (maxAge / 1000) + }, + body: buf + }; + res.writeHead(200, icon.headers); + res.end(icon.body); + }); + } + } else { + next(); + } + }; +}; diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/json.js b/server/node_modules/express/node_modules/connect/lib/middleware/json.js new file mode 100644 index 0000000..d3e948e --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/json.js @@ -0,0 +1,97 @@ + +/*! + * Connect - json + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../utils'); +var getBody = require('raw-body'); +var bytes = require('bytes'); + +/** + * JSON: + * + * Parse JSON request bodies, providing the + * parsed object as `req.body`. + * + * Options: + * + * - `strict` when `false` anything `JSON.parse()` accepts will be parsed + * - `reviver` used as the second "reviver" argument for JSON.parse + * - `limit` byte limit [1mb] + * - `verify` synchronous verification function. + * should have signature (req, res, buffer). + * should throw an error on failure. + * + * @param {Object} options + * @return {Function} + * @api public + */ + +exports = module.exports = function(options){ + options = options || {}; + var strict = options.strict !== false; + var verify = typeof options.verify === 'function' && options.verify; + + var limit = !options.limit ? bytes('1mb') + : typeof options.limit === 'number' ? options.limit + : typeof options.limit === 'string' ? bytes(options.limit) + : null; + + return function json(req, res, next) { + if (req._body) return next(); + req.body = req.body || {}; + + if (!utils.hasBody(req)) return next(); + + // check Content-Type + if (!exports.regexp.test(utils.mime(req))) return next(); + + // flag as parsed + req._body = true; + + // parse + getBody(req, { + limit: limit, + expected: req.headers['content-length'] + }, function (err, buf) { + if (err) return next(err); + + if (verify) { + try { + verify(req, res, buf) + } catch (err) { + if (!err.status) err.status = 403; + return next(err); + } + } + + buf = buf.toString('utf8').trim(); + + var first = buf[0]; + + if (0 == buf.length) { + return next(utils.error(400, 'invalid json, empty body')); + } + + if (strict && '{' != first && '[' != first) return next(utils.error(400, 'invalid json')); + try { + req.body = JSON.parse(buf, options.reviver); + } catch (err){ + err.body = buf; + err.status = 400; + return next(err); + } + next(); + }) + }; +}; + +exports.regexp = /^application\/([\w!#\$%&\*`\-\.\^~]*\+)?json$/i; + diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/limit.js b/server/node_modules/express/node_modules/connect/lib/middleware/limit.js new file mode 100644 index 0000000..f16439c --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/limit.js @@ -0,0 +1,83 @@ + +/*! + * Connect - limit + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../utils'), + brokenPause = utils.brokenPause; + +/** + * Limit: + * + * Limit request bodies to the given size in `bytes`. + * + * A string representation of the bytesize may also be passed, + * for example "5mb", "200kb", "1gb", etc. + * + * connect() + * .use(connect.limit('5.5mb')) + * .use(handleImageUpload) + * + * @param {Number|String} bytes + * @return {Function} + * @api public + */ + +module.exports = function limit(bytes){ + if ('string' == typeof bytes) bytes = utils.parseBytes(bytes); + if ('number' != typeof bytes) throw new Error('limit() bytes required'); + + if (process.env.NODE_ENV !== 'test') { + console.warn('connect.limit() will be removed in connect 3.0'); + } + + return function limit(req, res, next){ + var received = 0 + , len = req.headers['content-length'] + ? parseInt(req.headers['content-length'], 10) + : null; + + // self-awareness + if (req._limit) return next(); + req._limit = true; + + // limit by content-length + if (len && len > bytes) return next(utils.error(413)); + + // limit + if (brokenPause) { + listen(); + } else { + req.on('newListener', function handler(event) { + if (event !== 'data') return; + + req.removeListener('newListener', handler); + // Start listening at the end of the current loop + // otherwise the request will be consumed too early. + // Sideaffect is `limit` will miss the first chunk, + // but that's not a big deal. + // Unfortunately, the tests don't have large enough + // request bodies to test this. + process.nextTick(listen); + }); + }; + + next(); + + function listen() { + req.on('data', function(chunk) { + received += Buffer.isBuffer(chunk) + ? chunk.length : + Buffer.byteLength(chunk); + + if (received > bytes) req.destroy(); + }); + }; + }; +}; \ No newline at end of file diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/logger.js b/server/node_modules/express/node_modules/connect/lib/middleware/logger.js new file mode 100644 index 0000000..fba43e4 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/logger.js @@ -0,0 +1,342 @@ +/*! + * Connect - logger + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var bytes = require('bytes'); + +/*! + * Log buffer. + */ + +var buf = []; + +/*! + * Default log buffer duration. + */ + +var defaultBufferDuration = 1000; + +/** + * Logger: + * + * Log requests with the given `options` or a `format` string. + * + * Options: + * + * - `format` Format string, see below for tokens + * - `stream` Output stream, defaults to _stdout_ + * - `buffer` Buffer duration, defaults to 1000ms when _true_ + * - `immediate` Write log line on request instead of response (for response times) + * + * Tokens: + * + * - `:req[header]` ex: `:req[Accept]` + * - `:res[header]` ex: `:res[Content-Length]` + * - `:http-version` + * - `:response-time` + * - `:remote-addr` + * - `:date` + * - `:method` + * - `:url` + * - `:referrer` + * - `:user-agent` + * - `:status` + * + * Formats: + * + * Pre-defined formats that ship with connect: + * + * - `default` ':remote-addr - - [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"' + * - `short` ':remote-addr - :method :url HTTP/:http-version :status :res[content-length] - :response-time ms' + * - `tiny` ':method :url :status :res[content-length] - :response-time ms' + * - `dev` concise output colored by response status for development use + * + * Examples: + * + * connect.logger() // default + * connect.logger('short') + * connect.logger('tiny') + * connect.logger({ immediate: true, format: 'dev' }) + * connect.logger(':method :url - :referrer') + * connect.logger(':req[content-type] -> :res[content-type]') + * connect.logger(function(tokens, req, res){ return 'some format string' }) + * + * Defining Tokens: + * + * To define a token, simply invoke `connect.logger.token()` with the + * name and a callback function. The value returned is then available + * as ":type" in this case. + * + * connect.logger.token('type', function(req, res){ return req.headers['content-type']; }) + * + * Defining Formats: + * + * All default formats are defined this way, however it's public API as well: + * + * connect.logger.format('name', 'string or function') + * + * @param {String|Function|Object} format or options + * @return {Function} + * @api public + */ + +exports = module.exports = function logger(options) { + if ('object' == typeof options) { + options = options || {}; + } else if (options) { + options = { format: options }; + } else { + options = {}; + } + + // output on request instead of response + var immediate = options.immediate; + + // format name + var fmt = exports[options.format] || options.format || exports.default; + + // compile format + if ('function' != typeof fmt) fmt = compile(fmt); + + // options + var stream = options.stream || process.stdout + , buffer = options.buffer; + + // buffering support + if (buffer) { + var realStream = stream + , interval = 'number' == typeof buffer + ? buffer + : defaultBufferDuration; + + // flush interval + setInterval(function(){ + if (buf.length) { + realStream.write(buf.join('')); + buf.length = 0; + } + }, interval); + + // swap the stream + stream = { + write: function(str){ + buf.push(str); + } + }; + } + + return function logger(req, res, next) { + var sock = req.socket; + req._startTime = new Date; + req._remoteAddress = sock.socket ? sock.socket.remoteAddress : sock.remoteAddress; + + function logRequest(){ + res.removeListener('finish', logRequest); + res.removeListener('close', logRequest); + var line = fmt(exports, req, res); + if (null == line) return; + stream.write(line + '\n'); + }; + + // immediate + if (immediate) { + logRequest(); + // proxy end to output logging + } else { + res.on('finish', logRequest); + res.on('close', logRequest); + } + + + next(); + }; +}; + +/** + * Compile `fmt` into a function. + * + * @param {String} fmt + * @return {Function} + * @api private + */ + +function compile(fmt) { + fmt = fmt.replace(/"/g, '\\"'); + var js = ' return "' + fmt.replace(/:([-\w]{2,})(?:\[([^\]]+)\])?/g, function(_, name, arg){ + return '"\n + (tokens["' + name + '"](req, res, "' + arg + '") || "-") + "'; + }) + '";' + return new Function('tokens, req, res', js); +}; + +/** + * Define a token function with the given `name`, + * and callback `fn(req, res)`. + * + * @param {String} name + * @param {Function} fn + * @return {Object} exports for chaining + * @api public + */ + +exports.token = function(name, fn) { + exports[name] = fn; + return this; +}; + +/** + * Define a `fmt` with the given `name`. + * + * @param {String} name + * @param {String|Function} fmt + * @return {Object} exports for chaining + * @api public + */ + +exports.format = function(name, str){ + exports[name] = str; + return this; +}; + +/** + * Default format. + */ + +exports.format('default', ':remote-addr - - [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"'); + +/** + * Short format. + */ + +exports.format('short', ':remote-addr - :method :url HTTP/:http-version :status :res[content-length] - :response-time ms'); + +/** + * Tiny format. + */ + +exports.format('tiny', ':method :url :status :res[content-length] - :response-time ms'); + +/** + * dev (colored) + */ + +exports.format('dev', function(tokens, req, res){ + var status = res.statusCode + , len = parseInt(res.getHeader('Content-Length'), 10) + , color = 32; + + if (status >= 500) color = 31 + else if (status >= 400) color = 33 + else if (status >= 300) color = 36; + + len = isNaN(len) + ? '' + : len = ' - ' + bytes(len); + + return '\x1b[90m' + req.method + + ' ' + req.originalUrl + ' ' + + '\x1b[' + color + 'm' + res.statusCode + + ' \x1b[90m' + + (new Date - req._startTime) + + 'ms' + len + + '\x1b[0m'; +}); + +/** + * request url + */ + +exports.token('url', function(req){ + return req.originalUrl || req.url; +}); + +/** + * request method + */ + +exports.token('method', function(req){ + return req.method; +}); + +/** + * response time in milliseconds + */ + +exports.token('response-time', function(req){ + return String(Date.now() - req._startTime); +}); + +/** + * UTC date + */ + +exports.token('date', function(){ + return new Date().toUTCString(); +}); + +/** + * response status code + */ + +exports.token('status', function(req, res){ + return res.headerSent ? res.statusCode : null; +}); + +/** + * normalized referrer + */ + +exports.token('referrer', function(req){ + return req.headers['referer'] || req.headers['referrer']; +}); + +/** + * remote address + */ + +exports.token('remote-addr', function(req){ + if (req.ip) return req.ip; + if (req._remoteAddress) return req._remoteAddress; + var sock = req.socket; + if (sock.socket) return sock.socket.remoteAddress; + return sock.remoteAddress; +}); + +/** + * HTTP version + */ + +exports.token('http-version', function(req){ + return req.httpVersionMajor + '.' + req.httpVersionMinor; +}); + +/** + * UA string + */ + +exports.token('user-agent', function(req){ + return req.headers['user-agent']; +}); + +/** + * request header + */ + +exports.token('req', function(req, res, field){ + return req.headers[field.toLowerCase()]; +}); + +/** + * response header + */ + +exports.token('res', function(req, res, field){ + return (res._headers || {})[field.toLowerCase()]; +}); + diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/methodOverride.js b/server/node_modules/express/node_modules/connect/lib/middleware/methodOverride.js new file mode 100644 index 0000000..a729d13 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/methodOverride.js @@ -0,0 +1,58 @@ +/*! + * Connect - methodOverride + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var methods = require('methods'); + +/** + * Method Override: + * + * Provides faux HTTP method support. + * + * Pass an optional `key` to use when checking for + * a method override, otherwise defaults to _\_method_. + * The original method is available via `req.originalMethod`. + * + * @param {String} key + * @return {Function} + * @api public + */ + +module.exports = function methodOverride(key){ + key = key || "_method"; + return function methodOverride(req, res, next) { + var method; + req.originalMethod = req.originalMethod || req.method; + + // req.body + if (req.body && typeof req.body === 'object' && key in req.body) { + method = req.body[key].toLowerCase(); + delete req.body[key]; + } + + // check X-HTTP-Method-Override + if (req.headers['x-http-method-override']) { + method = req.headers['x-http-method-override'].toLowerCase(); + } + + // replace + if (supports(method)) req.method = method.toUpperCase(); + + next(); + }; +}; + +/** + * Check if node supports `method`. + */ + +function supports(method) { + return ~methods.indexOf(method); +} diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/multipart.js b/server/node_modules/express/node_modules/connect/lib/middleware/multipart.js new file mode 100644 index 0000000..b985d2c --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/multipart.js @@ -0,0 +1,164 @@ +/*! + * Connect - multipart + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var multiparty = require('multiparty') + , _limit = require('./limit') + , utils = require('../utils') + , qs = require('qs'); + +/** + * Multipart: + * + * Parse multipart/form-data request bodies, + * providing the parsed object as `req.body` + * and `req.files`. + * + * Configuration: + * + * The options passed are merged with [multiparty](https://github.com/superjoe30/node-multiparty)'s + * `Form` object, allowing you to configure the upload directory, + * size limits, etc. For example if you wish to change the upload dir do the following. + * + * app.use(connect.multipart({ uploadDir: path })); + * + * Options: + * + * - `limit` byte limit defaulting to [100mb] + * - `defer` defers processing and exposes the multiparty form object as `req.form`. + * `next()` is called without waiting for the form's "end" event. + * This option is useful if you need to bind to the "progress" or "part" events, for example. + * + * Temporary Files: + * + * By default temporary files are used, stored in `os.tmpDir()`. These + * are not automatically garbage collected, you are in charge of moving them + * or deleting them. When `defer` is not used and these files are created you + * may refernce them via the `req.files` object. + * + * req.files.images.forEach(function(file){ + * console.log(' uploaded : %s %skb : %s', file.originalFilename, file.size / 1024 | 0, file.path); + * }); + * + * It is highly recommended to monitor and clean up tempfiles in any production + * environment, you may use tools like [reap](https://github.com/visionmedia/reap) + * to do so. + * + * Streaming: + * + * When `defer` is used files are _not_ streamed to tmpfiles, you may + * access them via the "part" events and stream them accordingly: + * + * req.form.on('part', function(part){ + * // transfer to s3 etc + * console.log('upload %s %s', part.name, part.filename); + * var out = fs.createWriteStream('/tmp/' + part.filename); + * part.pipe(out); + * }); + * + * req.form.on('close', function(){ + * res.end('uploaded!'); + * }); + * + * @param {Object} options + * @return {Function} + * @api public + */ + +exports = module.exports = function(options){ + options = options || {}; + + if (process.env.NODE_ENV !== 'test') { + console.warn('connect.multipart() will be removed in connect 3.0'); + console.warn('visit https://github.com/senchalabs/connect/wiki/Connect-3.0 for alternatives'); + } + + var limit = _limit(options.limit || '100mb'); + + return function multipart(req, res, next) { + if (req._body) return next(); + req.body = req.body || {}; + req.files = req.files || {}; + + if (!utils.hasBody(req)) return next(); + + // ignore GET + if ('GET' == req.method || 'HEAD' == req.method) return next(); + + // check Content-Type + if ('multipart/form-data' != utils.mime(req)) return next(); + + // flag as parsed + req._body = true; + + // parse + limit(req, res, function(err){ + if (err) return next(err); + + var form = new multiparty.Form(options) + , data = {} + , files = {} + , done; + + Object.keys(options).forEach(function(key){ + form[key] = options[key]; + }); + + function ondata(name, val, data){ + if (Array.isArray(data[name])) { + data[name].push(val); + } else if (data[name]) { + data[name] = [data[name], val]; + } else { + data[name] = val; + } + } + + form.on('field', function(name, val){ + ondata(name, val, data); + }); + + if (!options.defer) { + form.on('file', function(name, val){ + val.name = val.originalFilename; + val.type = val.headers['content-type'] || null; + ondata(name, val, files); + }); + } + + form.on('error', function(err){ + if (!options.defer) { + err.status = 400; + next(err); + } + done = true; + }); + + form.on('close', function(){ + if (done) return; + try { + req.body = qs.parse(data); + req.files = qs.parse(files); + } catch (err) { + form.emit('error', err); + return; + } + if (!options.defer) next(); + }); + + form.parse(req); + + if (options.defer) { + req.form = form; + next(); + } + }); + } +}; diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/query.js b/server/node_modules/express/node_modules/connect/lib/middleware/query.js new file mode 100644 index 0000000..93fc5d3 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/query.js @@ -0,0 +1,46 @@ +/*! + * Connect - query + * Copyright(c) 2011 TJ Holowaychuk + * Copyright(c) 2011 Sencha Inc. + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var qs = require('qs') + , parse = require('../utils').parseUrl; + +/** + * Query: + * + * Automatically parse the query-string when available, + * populating the `req.query` object. + * + * Examples: + * + * connect() + * .use(connect.query()) + * .use(function(req, res){ + * res.end(JSON.stringify(req.query)); + * }); + * + * The `options` passed are provided to qs.parse function. + * + * @param {Object} options + * @return {Function} + * @api public + */ + +module.exports = function query(options){ + return function query(req, res, next){ + if (!req.query) { + req.query = ~req.url.indexOf('?') + ? qs.parse(parse(req).query, options) + : {}; + } + + next(); + }; +}; diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/responseTime.js b/server/node_modules/express/node_modules/connect/lib/middleware/responseTime.js new file mode 100644 index 0000000..62abc04 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/responseTime.js @@ -0,0 +1,32 @@ + +/*! + * Connect - responseTime + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Reponse time: + * + * Adds the `X-Response-Time` header displaying the response + * duration in milliseconds. + * + * @return {Function} + * @api public + */ + +module.exports = function responseTime(){ + return function(req, res, next){ + var start = new Date; + + if (res._responseTime) return next(); + res._responseTime = true; + + res.on('header', function(){ + var duration = new Date - start; + res.setHeader('X-Response-Time', duration + 'ms'); + }); + + next(); + }; +}; diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/session.js b/server/node_modules/express/node_modules/connect/lib/middleware/session.js new file mode 100644 index 0000000..d918d30 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/session.js @@ -0,0 +1,361 @@ +/*! + * Connect - session + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Session = require('./session/session') + , debug = require('debug')('connect:session') + , MemoryStore = require('./session/memory') + , signature = require('cookie-signature') + , Cookie = require('./session/cookie') + , Store = require('./session/store') + , utils = require('./../utils') + , uid = require('uid2') + , crc32 = require('buffer-crc32') + , parse = require('url').parse; + +// environment + +var env = process.env.NODE_ENV; + +/** + * Expose the middleware. + */ + +exports = module.exports = session; + +/** + * Expose constructors. + */ + +exports.Store = Store; +exports.Cookie = Cookie; +exports.Session = Session; +exports.MemoryStore = MemoryStore; + +/** + * Warning message for `MemoryStore` usage in production. + */ + +var warning = 'Warning: connection.session() MemoryStore is not\n' + + 'designed for a production environment, as it will leak\n' + + 'memory, and will not scale past a single process.'; + +/** + * Session: + * + * Setup session store with the given `options`. + * + * Session data is _not_ saved in the cookie itself, however + * cookies are used, so we must use the [cookieParser()](cookieParser.html) + * middleware _before_ `session()`. + * + * Examples: + * + * connect() + * .use(connect.cookieParser()) + * .use(connect.session({ secret: 'keyboard cat', key: 'sid', cookie: { secure: true }})) + * + * Options: + * + * - `key` cookie name defaulting to `connect.sid` + * - `store` session store instance + * - `secret` session cookie is signed with this secret to prevent tampering + * - `cookie` session cookie settings, defaulting to `{ path: '/', httpOnly: true, maxAge: null }` + * - `proxy` trust the reverse proxy when setting secure cookies (via "x-forwarded-proto") + * + * Cookie option: + * + * By default `cookie.maxAge` is `null`, meaning no "expires" parameter is set + * so the cookie becomes a browser-session cookie. When the user closes the + * browser the cookie (and session) will be removed. + * + * ## req.session + * + * To store or access session data, simply use the request property `req.session`, + * which is (generally) serialized as JSON by the store, so nested objects + * are typically fine. For example below is a user-specific view counter: + * + * connect() + * .use(connect.favicon()) + * .use(connect.cookieParser()) + * .use(connect.session({ secret: 'keyboard cat', cookie: { maxAge: 60000 }})) + * .use(function(req, res, next){ + * var sess = req.session; + * if (sess.views) { + * res.setHeader('Content-Type', 'text/html'); + * res.write('

    views: ' + sess.views + '

    '); + * res.write('

    expires in: ' + (sess.cookie.maxAge / 1000) + 's

    '); + * res.end(); + * sess.views++; + * } else { + * sess.views = 1; + * res.end('welcome to the session demo. refresh!'); + * } + * } + * )).listen(3000); + * + * ## Session#regenerate() + * + * To regenerate the session simply invoke the method, once complete + * a new SID and `Session` instance will be initialized at `req.session`. + * + * req.session.regenerate(function(err){ + * // will have a new session here + * }); + * + * ## Session#destroy() + * + * Destroys the session, removing `req.session`, will be re-generated next request. + * + * req.session.destroy(function(err){ + * // cannot access session here + * }); + * + * ## Session#reload() + * + * Reloads the session data. + * + * req.session.reload(function(err){ + * // session updated + * }); + * + * ## Session#save() + * + * Save the session. + * + * req.session.save(function(err){ + * // session saved + * }); + * + * ## Session#touch() + * + * Updates the `.maxAge` property. Typically this is + * not necessary to call, as the session middleware does this for you. + * + * ## Session#cookie + * + * Each session has a unique cookie object accompany it. This allows + * you to alter the session cookie per visitor. For example we can + * set `req.session.cookie.expires` to `false` to enable the cookie + * to remain for only the duration of the user-agent. + * + * ## Session#maxAge + * + * Alternatively `req.session.cookie.maxAge` will return the time + * remaining in milliseconds, which we may also re-assign a new value + * to adjust the `.expires` property appropriately. The following + * are essentially equivalent + * + * var hour = 3600000; + * req.session.cookie.expires = new Date(Date.now() + hour); + * req.session.cookie.maxAge = hour; + * + * For example when `maxAge` is set to `60000` (one minute), and 30 seconds + * has elapsed it will return `30000` until the current request has completed, + * at which time `req.session.touch()` is called to reset `req.session.maxAge` + * to its original value. + * + * req.session.cookie.maxAge; + * // => 30000 + * + * Session Store Implementation: + * + * Every session store _must_ implement the following methods + * + * - `.get(sid, callback)` + * - `.set(sid, session, callback)` + * - `.destroy(sid, callback)` + * + * Recommended methods include, but are not limited to: + * + * - `.length(callback)` + * - `.clear(callback)` + * + * For an example implementation view the [connect-redis](http://github.com/visionmedia/connect-redis) repo. + * + * @param {Object} options + * @return {Function} + * @api public + */ + +function session(options){ + var options = options || {} + , key = options.key || 'connect.sid' + , store = options.store || new MemoryStore + , cookie = options.cookie || {} + , trustProxy = options.proxy + , storeReady = true + , rollingSessions = options.rolling || false; + + // notify user that this store is not + // meant for a production environment + if ('production' == env && store instanceof MemoryStore) { + console.warn(warning); + } + + // generates the new session + store.generate = function(req){ + req.sessionID = uid(24); + req.session = new Session(req); + req.session.cookie = new Cookie(cookie); + }; + + store.on('disconnect', function(){ storeReady = false; }); + store.on('connect', function(){ storeReady = true; }); + + return function session(req, res, next) { + // self-awareness + if (req.session) return next(); + + // Handle connection as if there is no session if + // the store has temporarily disconnected etc + if (!storeReady) return debug('store is disconnected'), next(); + + // pathname mismatch + var originalPath = parse(req.originalUrl).pathname; + if (0 != originalPath.indexOf(cookie.path || '/')) return next(); + + // backwards compatibility for signed cookies + // req.secret is passed from the cookie parser middleware + var secret = options.secret || req.secret; + + // ensure secret is available or bail + if (!secret) throw new Error('`secret` option required for sessions'); + + var originalHash + , originalId; + + // expose store + req.sessionStore = store; + + // grab the session cookie value and check the signature + var rawCookie = req.cookies[key]; + + // get signedCookies for backwards compat with signed cookies + var unsignedCookie = req.signedCookies[key]; + + if (!unsignedCookie && rawCookie) { + unsignedCookie = utils.parseSignedCookie(rawCookie, secret); + } + + // set-cookie + res.on('header', function(){ + if (!req.session) return; + var cookie = req.session.cookie + , proto = (req.headers['x-forwarded-proto'] || '').split(',')[0].toLowerCase().trim() + , tls = req.connection.encrypted || (trustProxy && 'https' == proto) + , isNew = unsignedCookie != req.sessionID; + + // only send secure cookies via https + if (cookie.secure && !tls) return debug('not secured'); + + // in case of rolling session, always reset the cookie + if (!rollingSessions) { + + // long expires, handle expiry server-side + if (!isNew && cookie.hasLongExpires) return debug('already set cookie'); + + // browser-session length cookie + if (null == cookie.expires) { + if (!isNew) return debug('already set browser-session cookie'); + // compare hashes and ids + } else if (originalHash == hash(req.session) && originalId == req.session.id) { + return debug('unmodified session'); + } + + } + + var val = 's:' + signature.sign(req.sessionID, secret); + val = cookie.serialize(key, val); + debug('set-cookie %s', val); + res.setHeader('Set-Cookie', val); + }); + + // proxy end() to commit the session + var end = res.end; + res.end = function(data, encoding){ + res.end = end; + if (!req.session) return res.end(data, encoding); + debug('saving'); + req.session.resetMaxAge(); + req.session.save(function(err){ + if (err) console.error(err.stack); + debug('saved'); + res.end(data, encoding); + }); + }; + + // generate the session + function generate() { + store.generate(req); + } + + // get the sessionID from the cookie + req.sessionID = unsignedCookie; + + // generate a session if the browser doesn't send a sessionID + if (!req.sessionID) { + debug('no SID sent, generating session'); + generate(); + next(); + return; + } + + // generate the session object + var pause = utils.pause(req); + debug('fetching %s', req.sessionID); + store.get(req.sessionID, function(err, sess){ + // proxy to resume() events + var _next = next; + next = function(err){ + _next(err); + pause.resume(); + }; + + // error handling + if (err) { + debug('error %j', err); + if ('ENOENT' == err.code) { + generate(); + next(); + } else { + next(err); + } + // no session + } else if (!sess) { + debug('no session found'); + generate(); + next(); + // populate req.session + } else { + debug('session found'); + store.createSession(req, sess); + originalId = req.sessionID; + originalHash = hash(sess); + next(); + } + }); + }; +}; + +/** + * Hash the given `sess` object omitting changes + * to `.cookie`. + * + * @param {Object} sess + * @return {String} + * @api private + */ + +function hash(sess) { + return crc32.signed(JSON.stringify(sess, function(key, val){ + if ('cookie' != key) return val; + })); +} diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/session/cookie.js b/server/node_modules/express/node_modules/connect/lib/middleware/session/cookie.js new file mode 100644 index 0000000..cdce2a5 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/session/cookie.js @@ -0,0 +1,140 @@ + +/*! + * Connect - session - Cookie + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../../utils') + , cookie = require('cookie'); + +/** + * Initialize a new `Cookie` with the given `options`. + * + * @param {IncomingMessage} req + * @param {Object} options + * @api private + */ + +var Cookie = module.exports = function Cookie(options) { + this.path = '/'; + this.maxAge = null; + this.httpOnly = true; + if (options) utils.merge(this, options); + this.originalMaxAge = undefined == this.originalMaxAge + ? this.maxAge + : this.originalMaxAge; +}; + +/*! + * Prototype. + */ + +Cookie.prototype = { + + /** + * Set expires `date`. + * + * @param {Date} date + * @api public + */ + + set expires(date) { + this._expires = date; + this.originalMaxAge = this.maxAge; + }, + + /** + * Get expires `date`. + * + * @return {Date} + * @api public + */ + + get expires() { + return this._expires; + }, + + /** + * Set expires via max-age in `ms`. + * + * @param {Number} ms + * @api public + */ + + set maxAge(ms) { + this.expires = 'number' == typeof ms + ? new Date(Date.now() + ms) + : ms; + }, + + /** + * Get expires max-age in `ms`. + * + * @return {Number} + * @api public + */ + + get maxAge() { + return this.expires instanceof Date + ? this.expires.valueOf() - Date.now() + : this.expires; + }, + + /** + * Return cookie data object. + * + * @return {Object} + * @api private + */ + + get data() { + return { + originalMaxAge: this.originalMaxAge + , expires: this._expires + , secure: this.secure + , httpOnly: this.httpOnly + , domain: this.domain + , path: this.path + } + }, + + /** + * Check if the cookie has a reasonably large max-age. + * + * @return {Boolean} + * @api private + */ + + get hasLongExpires() { + var week = 604800000; + return this.maxAge > (4 * week); + }, + + /** + * Return a serialized cookie string. + * + * @return {String} + * @api public + */ + + serialize: function(name, val){ + return cookie.serialize(name, val, this.data); + }, + + /** + * Return JSON representation of this cookie. + * + * @return {Object} + * @api private + */ + + toJSON: function(){ + return this.data; + } +}; diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/session/memory.js b/server/node_modules/express/node_modules/connect/lib/middleware/session/memory.js new file mode 100644 index 0000000..fb93939 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/session/memory.js @@ -0,0 +1,129 @@ + +/*! + * Connect - session - MemoryStore + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var Store = require('./store'); + +/** + * Initialize a new `MemoryStore`. + * + * @api public + */ + +var MemoryStore = module.exports = function MemoryStore() { + this.sessions = {}; +}; + +/** + * Inherit from `Store.prototype`. + */ + +MemoryStore.prototype.__proto__ = Store.prototype; + +/** + * Attempt to fetch session by the given `sid`. + * + * @param {String} sid + * @param {Function} fn + * @api public + */ + +MemoryStore.prototype.get = function(sid, fn){ + var self = this; + process.nextTick(function(){ + var expires + , sess = self.sessions[sid]; + if (sess) { + sess = JSON.parse(sess); + expires = 'string' == typeof sess.cookie.expires + ? new Date(sess.cookie.expires) + : sess.cookie.expires; + if (!expires || new Date < expires) { + fn(null, sess); + } else { + self.destroy(sid, fn); + } + } else { + fn(); + } + }); +}; + +/** + * Commit the given `sess` object associated with the given `sid`. + * + * @param {String} sid + * @param {Session} sess + * @param {Function} fn + * @api public + */ + +MemoryStore.prototype.set = function(sid, sess, fn){ + var self = this; + process.nextTick(function(){ + self.sessions[sid] = JSON.stringify(sess); + fn && fn(); + }); +}; + +/** + * Destroy the session associated with the given `sid`. + * + * @param {String} sid + * @api public + */ + +MemoryStore.prototype.destroy = function(sid, fn){ + var self = this; + process.nextTick(function(){ + delete self.sessions[sid]; + fn && fn(); + }); +}; + +/** + * Invoke the given callback `fn` with all active sessions. + * + * @param {Function} fn + * @api public + */ + +MemoryStore.prototype.all = function(fn){ + var arr = [] + , keys = Object.keys(this.sessions); + for (var i = 0, len = keys.length; i < len; ++i) { + arr.push(this.sessions[keys[i]]); + } + fn(null, arr); +}; + +/** + * Clear all sessions. + * + * @param {Function} fn + * @api public + */ + +MemoryStore.prototype.clear = function(fn){ + this.sessions = {}; + fn && fn(); +}; + +/** + * Fetch number of sessions. + * + * @param {Function} fn + * @api public + */ + +MemoryStore.prototype.length = function(fn){ + fn(null, Object.keys(this.sessions).length); +}; diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/session/session.js b/server/node_modules/express/node_modules/connect/lib/middleware/session/session.js new file mode 100644 index 0000000..0dd4b40 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/session/session.js @@ -0,0 +1,116 @@ + +/*! + * Connect - session - Session + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../../utils'); + +/** + * Create a new `Session` with the given request and `data`. + * + * @param {IncomingRequest} req + * @param {Object} data + * @api private + */ + +var Session = module.exports = function Session(req, data) { + Object.defineProperty(this, 'req', { value: req }); + Object.defineProperty(this, 'id', { value: req.sessionID }); + if ('object' == typeof data) utils.merge(this, data); +}; + +/** + * Update reset `.cookie.maxAge` to prevent + * the cookie from expiring when the + * session is still active. + * + * @return {Session} for chaining + * @api public + */ + +Session.prototype.touch = function(){ + return this.resetMaxAge(); +}; + +/** + * Reset `.maxAge` to `.originalMaxAge`. + * + * @return {Session} for chaining + * @api public + */ + +Session.prototype.resetMaxAge = function(){ + this.cookie.maxAge = this.cookie.originalMaxAge; + return this; +}; + +/** + * Save the session data with optional callback `fn(err)`. + * + * @param {Function} fn + * @return {Session} for chaining + * @api public + */ + +Session.prototype.save = function(fn){ + this.req.sessionStore.set(this.id, this, fn || function(){}); + return this; +}; + +/** + * Re-loads the session data _without_ altering + * the maxAge properties. Invokes the callback `fn(err)`, + * after which time if no exception has occurred the + * `req.session` property will be a new `Session` object, + * although representing the same session. + * + * @param {Function} fn + * @return {Session} for chaining + * @api public + */ + +Session.prototype.reload = function(fn){ + var req = this.req + , store = this.req.sessionStore; + store.get(this.id, function(err, sess){ + if (err) return fn(err); + if (!sess) return fn(new Error('failed to load session')); + store.createSession(req, sess); + fn(); + }); + return this; +}; + +/** + * Destroy `this` session. + * + * @param {Function} fn + * @return {Session} for chaining + * @api public + */ + +Session.prototype.destroy = function(fn){ + delete this.req.session; + this.req.sessionStore.destroy(this.id, fn); + return this; +}; + +/** + * Regenerate this request's session. + * + * @param {Function} fn + * @return {Session} for chaining + * @api public + */ + +Session.prototype.regenerate = function(fn){ + this.req.sessionStore.regenerate(this.req, fn); + return this; +}; diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/session/store.js b/server/node_modules/express/node_modules/connect/lib/middleware/session/store.js new file mode 100644 index 0000000..54294cb --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/session/store.js @@ -0,0 +1,84 @@ + +/*! + * Connect - session - Store + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var EventEmitter = require('events').EventEmitter + , Session = require('./session') + , Cookie = require('./cookie'); + +/** + * Initialize abstract `Store`. + * + * @api private + */ + +var Store = module.exports = function Store(options){}; + +/** + * Inherit from `EventEmitter.prototype`. + */ + +Store.prototype.__proto__ = EventEmitter.prototype; + +/** + * Re-generate the given requests's session. + * + * @param {IncomingRequest} req + * @return {Function} fn + * @api public + */ + +Store.prototype.regenerate = function(req, fn){ + var self = this; + this.destroy(req.sessionID, function(err){ + self.generate(req); + fn(err); + }); +}; + +/** + * Load a `Session` instance via the given `sid` + * and invoke the callback `fn(err, sess)`. + * + * @param {String} sid + * @param {Function} fn + * @api public + */ + +Store.prototype.load = function(sid, fn){ + var self = this; + this.get(sid, function(err, sess){ + if (err) return fn(err); + if (!sess) return fn(); + var req = { sessionID: sid, sessionStore: self }; + sess = self.createSession(req, sess); + fn(null, sess); + }); +}; + +/** + * Create session from JSON `sess` data. + * + * @param {IncomingRequest} req + * @param {Object} sess + * @return {Session} + * @api private + */ + +Store.prototype.createSession = function(req, sess){ + var expires = sess.cookie.expires + , orig = sess.cookie.originalMaxAge; + sess.cookie = new Cookie(sess.cookie); + if ('string' == typeof expires) sess.cookie.expires = new Date(expires); + sess.cookie.originalMaxAge = orig; + req.session = new Session(req, sess); + return req.session; +}; diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/static.js b/server/node_modules/express/node_modules/connect/lib/middleware/static.js new file mode 100644 index 0000000..67970da --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/static.js @@ -0,0 +1,102 @@ +/*! + * Connect - static + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var send = require('send') + , utils = require('../utils') + , parse = utils.parseUrl + , url = require('url'); + +/** + * Static: + * + * Static file server with the given `root` path. + * + * Examples: + * + * var oneDay = 86400000; + * + * connect() + * .use(connect.static(__dirname + '/public')) + * + * connect() + * .use(connect.static(__dirname + '/public', { maxAge: oneDay })) + * + * Options: + * + * - `maxAge` Browser cache maxAge in milliseconds. defaults to 0 + * - `hidden` Allow transfer of hidden files. defaults to false + * - `redirect` Redirect to trailing "/" when the pathname is a dir. defaults to true + * - `index` Default file name, defaults to 'index.html' + * + * @param {String} root + * @param {Object} options + * @return {Function} + * @api public + */ + +exports = module.exports = function(root, options){ + options = options || {}; + + // root required + if (!root) throw new Error('static() root path required'); + + // default redirect + var redirect = false !== options.redirect; + + return function staticMiddleware(req, res, next) { + if ('GET' != req.method && 'HEAD' != req.method) return next(); + var originalUrl = url.parse(req.originalUrl); + var path = parse(req).pathname; + var pause = utils.pause(req); + + if (path == '/' && originalUrl.pathname[originalUrl.pathname.length - 1] != '/') { + return directory(); + } + + function resume() { + next(); + pause.resume(); + } + + function directory() { + if (!redirect) return resume(); + var target; + originalUrl.pathname += '/'; + target = url.format(originalUrl); + res.statusCode = 303; + res.setHeader('Location', target); + res.end('Redirecting to ' + utils.escape(target)); + } + + function error(err) { + if (404 == err.status) return resume(); + next(err); + } + + send(req, path) + .maxage(options.maxAge || 0) + .root(root) + .index(options.index || 'index.html') + .hidden(options.hidden) + .on('error', error) + .on('directory', directory) + .pipe(res); + }; +}; + +/** + * Expose mime module. + * + * If you wish to extend the mime table use this + * reference to the "mime" module in the npm registry. + */ + +exports.mime = send.mime; diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/staticCache.js b/server/node_modules/express/node_modules/connect/lib/middleware/staticCache.js new file mode 100644 index 0000000..cc4c3a2 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/staticCache.js @@ -0,0 +1,233 @@ + +/*! + * Connect - staticCache + * Copyright(c) 2011 Sencha Inc. + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../utils') + , Cache = require('../cache') + , fresh = require('fresh'); + +/** + * Static cache: + * + * Enables a memory cache layer on top of + * the `static()` middleware, serving popular + * static files. + * + * By default a maximum of 128 objects are + * held in cache, with a max of 256k each, + * totalling ~32mb. + * + * A Least-Recently-Used (LRU) cache algo + * is implemented through the `Cache` object, + * simply rotating cache objects as they are + * hit. This means that increasingly popular + * objects maintain their positions while + * others get shoved out of the stack and + * garbage collected. + * + * Benchmarks: + * + * static(): 2700 rps + * node-static: 5300 rps + * static() + staticCache(): 7500 rps + * + * Options: + * + * - `maxObjects` max cache objects [128] + * - `maxLength` max cache object length 256kb + * + * @param {Object} options + * @return {Function} + * @api public + */ + +module.exports = function staticCache(options){ + var options = options || {} + , cache = new Cache(options.maxObjects || 128) + , maxlen = options.maxLength || 1024 * 256; + + if (process.env.NODE_ENV !== 'test') { + console.warn('connect.staticCache() is deprecated and will be removed in 3.0'); + console.warn('use varnish or similar reverse proxy caches.'); + } + + return function staticCache(req, res, next){ + var key = cacheKey(req) + , ranges = req.headers.range + , hasCookies = req.headers.cookie + , hit = cache.get(key); + + // cache static + // TODO: change from staticCache() -> cache() + // and make this work for any request + req.on('static', function(stream){ + var headers = res._headers + , cc = utils.parseCacheControl(headers['cache-control'] || '') + , contentLength = headers['content-length'] + , hit; + + // dont cache set-cookie responses + if (headers['set-cookie']) return hasCookies = true; + + // dont cache when cookies are present + if (hasCookies) return; + + // ignore larger files + if (!contentLength || contentLength > maxlen) return; + + // don't cache partial files + if (headers['content-range']) return; + + // dont cache items we shouldn't be + // TODO: real support for must-revalidate / no-cache + if ( cc['no-cache'] + || cc['no-store'] + || cc['private'] + || cc['must-revalidate']) return; + + // if already in cache then validate + if (hit = cache.get(key)){ + if (headers.etag == hit[0].etag) { + hit[0].date = new Date; + return; + } else { + cache.remove(key); + } + } + + // validation notifiactions don't contain a steam + if (null == stream) return; + + // add the cache object + var arr = []; + + // store the chunks + stream.on('data', function(chunk){ + arr.push(chunk); + }); + + // flag it as complete + stream.on('end', function(){ + var cacheEntry = cache.add(key); + delete headers['x-cache']; // Clean up (TODO: others) + cacheEntry.push(200); + cacheEntry.push(headers); + cacheEntry.push.apply(cacheEntry, arr); + }); + }); + + if (req.method == 'GET' || req.method == 'HEAD') { + if (ranges) { + next(); + } else if (!hasCookies && hit && !mustRevalidate(req, hit)) { + res.setHeader('X-Cache', 'HIT'); + respondFromCache(req, res, hit); + } else { + res.setHeader('X-Cache', 'MISS'); + next(); + } + } else { + next(); + } + } +}; + +/** + * Respond with the provided cached value. + * TODO: Assume 200 code, that's iffy. + * + * @param {Object} req + * @param {Object} res + * @param {Object} cacheEntry + * @return {String} + * @api private + */ + +function respondFromCache(req, res, cacheEntry) { + var status = cacheEntry[0] + , headers = utils.merge({}, cacheEntry[1]) + , content = cacheEntry.slice(2); + + headers.age = (new Date - new Date(headers.date)) / 1000 || 0; + + switch (req.method) { + case 'HEAD': + res.writeHead(status, headers); + res.end(); + break; + case 'GET': + if (utils.conditionalGET(req) && fresh(req.headers, headers)) { + headers['content-length'] = 0; + res.writeHead(304, headers); + res.end(); + } else { + res.writeHead(status, headers); + + function write() { + while (content.length) { + if (false === res.write(content.shift())) { + res.once('drain', write); + return; + } + } + res.end(); + } + + write(); + } + break; + default: + // This should never happen. + res.writeHead(500, ''); + res.end(); + } +} + +/** + * Determine whether or not a cached value must be revalidated. + * + * @param {Object} req + * @param {Object} cacheEntry + * @return {String} + * @api private + */ + +function mustRevalidate(req, cacheEntry) { + var cacheHeaders = cacheEntry[1] + , reqCC = utils.parseCacheControl(req.headers['cache-control'] || '') + , cacheCC = utils.parseCacheControl(cacheHeaders['cache-control'] || '') + , cacheAge = (new Date - new Date(cacheHeaders.date)) / 1000 || 0; + + if ( cacheCC['no-cache'] + || cacheCC['must-revalidate'] + || cacheCC['proxy-revalidate']) return true; + + if (reqCC['no-cache']) return true; + + if (null != reqCC['max-age']) return reqCC['max-age'] < cacheAge; + + if (null != cacheCC['max-age']) return cacheCC['max-age'] < cacheAge; + + return false; +} + +/** + * The key to use in the cache. For now, this is the URL path and query. + * + * 'http://example.com?key=value' -> '/?key=value' + * + * @param {Object} req + * @return {String} + * @api private + */ + +function cacheKey(req) { + return utils.parseUrl(req).path; +} diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/timeout.js b/server/node_modules/express/node_modules/connect/lib/middleware/timeout.js new file mode 100644 index 0000000..5496c02 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/timeout.js @@ -0,0 +1,55 @@ +/*! + * Connect - timeout + * Ported from https://github.com/LearnBoost/connect-timeout + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var debug = require('debug')('connect:timeout'); + +/** + * Timeout: + * + * Times out the request in `ms`, defaulting to `5000`. The + * method `req.clearTimeout()` is added to revert this behaviour + * programmatically within your application's middleware, routes, etc. + * + * The timeout error is passed to `next()` so that you may customize + * the response behaviour. This error has the `.timeout` property as + * well as `.status == 503`. + * + * @param {Number} ms + * @return {Function} + * @api public + */ + +module.exports = function timeout(ms) { + ms = ms || 5000; + + return function(req, res, next) { + var id = setTimeout(function(){ + req.emit('timeout', ms); + }, ms); + + req.on('timeout', function(){ + if (res.headerSent) return debug('response started, cannot timeout'); + var err = new Error('Response timeout'); + err.timeout = ms; + err.status = 503; + next(err); + }); + + req.clearTimeout = function(){ + clearTimeout(id); + }; + + res.on('header', function(){ + clearTimeout(id); + }); + + next(); + }; +}; diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/urlencoded.js b/server/node_modules/express/node_modules/connect/lib/middleware/urlencoded.js new file mode 100644 index 0000000..30fd088 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/urlencoded.js @@ -0,0 +1,86 @@ + +/*! + * Connect - urlencoded + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var utils = require('../utils'); +var getBody = require('raw-body'); +var bytes = require('bytes'); +var qs = require('qs'); + +/** + * Urlencoded: + * + * Parse x-ww-form-urlencoded request bodies, + * providing the parsed object as `req.body`. + * + * Options: + * + * - `limit` byte limit [1mb] + * - `verify` synchronous verification function. + * should have signature (req, res, buffer). + * should throw an error on failure. + * + * @param {Object} options + * @return {Function} + * @api public + */ + +exports = module.exports = function(options){ + options = options || {}; + var verify = typeof options.verify === 'function' && options.verify; + + var limit = !options.limit ? bytes('1mb') + : typeof options.limit === 'number' ? options.limit + : typeof options.limit === 'string' ? bytes(options.limit) + : null + + return function urlencoded(req, res, next) { + if (req._body) return next(); + req.body = req.body || {}; + + if (!utils.hasBody(req)) return next(); + + // check Content-Type + if ('application/x-www-form-urlencoded' != utils.mime(req)) return next(); + + // flag as parsed + req._body = true; + + // parse + getBody(req, { + limit: limit, + expected: req.headers['content-length'] + }, function (err, buf) { + if (err) return next(err); + + if (verify) { + try { + verify(req, res, buf) + } catch (err) { + if (!err.status) err.status = 403; + return next(err); + } + } + + buf = buf.toString('utf8').trim(); + + try { + req.body = buf.length + ? qs.parse(buf, options) + : {}; + } catch (err){ + err.body = buf; + return next(err); + } + next(); + }) + } +}; diff --git a/server/node_modules/express/node_modules/connect/lib/middleware/vhost.js b/server/node_modules/express/node_modules/connect/lib/middleware/vhost.js new file mode 100644 index 0000000..abbb050 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/middleware/vhost.js @@ -0,0 +1,40 @@ + +/*! + * Connect - vhost + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Vhost: + * + * Setup vhost for the given `hostname` and `server`. + * + * connect() + * .use(connect.vhost('foo.com', fooApp)) + * .use(connect.vhost('bar.com', barApp)) + * .use(connect.vhost('*.com', mainApp)) + * + * The `server` may be a Connect server or + * a regular Node `http.Server`. + * + * @param {String} hostname + * @param {Server} server + * @return {Function} + * @api public + */ + +module.exports = function vhost(hostname, server){ + if (!hostname) throw new Error('vhost hostname required'); + if (!server) throw new Error('vhost server required'); + var regexp = new RegExp('^' + hostname.replace(/[^*\w]/g, '\\$&').replace(/[*]/g, '(?:.*?)') + '$', 'i'); + if (server.onvhost) server.onvhost(hostname); + return function vhost(req, res, next){ + if (!req.headers.host) return next(); + var host = req.headers.host.split(':')[0]; + if (!regexp.test(host)) return next(); + if ('function' == typeof server) return server(req, res, next); + server.emit('request', req, res); + }; +}; diff --git a/server/node_modules/express/node_modules/connect/lib/patch.js b/server/node_modules/express/node_modules/connect/lib/patch.js new file mode 100644 index 0000000..22bcbc6 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/patch.js @@ -0,0 +1,89 @@ + +/*! + * Connect + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var http = require('http') + , res = http.ServerResponse.prototype + , setHeader = res.setHeader + , _renderHeaders = res._renderHeaders + , writeHead = res.writeHead; + +// apply only once + +if (!res._hasConnectPatch) { + + /** + * Provide a public "header sent" flag + * until node does. + * + * @return {Boolean} + * @api public + */ + + res.__defineGetter__('headerSent', function(){ + return this._header; + }); + + /** + * Set header `field` to `val`, special-casing + * the `Set-Cookie` field for multiple support. + * + * @param {String} field + * @param {String} val + * @api public + */ + + res.setHeader = function(field, val){ + var key = field.toLowerCase() + , prev; + + // special-case Set-Cookie + if (this._headers && 'set-cookie' == key) { + if (prev = this.getHeader(field)) { + if (Array.isArray(prev)) { + val = prev.concat(val); + } else if (Array.isArray(val)) { + val = val.concat(prev); + } else { + val = [prev, val]; + } + } + // charset + } else if ('content-type' == key && this.charset) { + val += '; charset=' + this.charset; + } + + return setHeader.call(this, field, val); + }; + + /** + * Proxy to emit "header" event. + */ + + res._renderHeaders = function(){ + if (!this._emittedHeader) this.emit('header'); + this._emittedHeader = true; + return _renderHeaders.call(this); + }; + + res.writeHead = function(statusCode, reasonPhrase, headers){ + if (typeof reasonPhrase === 'object') headers = reasonPhrase; + if (typeof headers === 'object') { + Object.keys(headers).forEach(function(key){ + this.setHeader(key, headers[key]); + }, this); + } + if (!this._emittedHeader) this.emit('header'); + this._emittedHeader = true; + return writeHead.call(this, statusCode, reasonPhrase); + }; + + res._hasConnectPatch = true; +} diff --git a/server/node_modules/express/node_modules/connect/lib/proto.js b/server/node_modules/express/node_modules/connect/lib/proto.js new file mode 100644 index 0000000..44337b3 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/proto.js @@ -0,0 +1,233 @@ +/*! + * Connect - HTTPServer + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var http = require('http') + , utils = require('./utils') + , debug = require('debug')('connect:dispatcher'); + +// prototype + +var app = module.exports = {}; + +// environment + +var env = process.env.NODE_ENV || 'development'; + +/** + * Utilize the given middleware `handle` to the given `route`, + * defaulting to _/_. This "route" is the mount-point for the + * middleware, when given a value other than _/_ the middleware + * is only effective when that segment is present in the request's + * pathname. + * + * For example if we were to mount a function at _/admin_, it would + * be invoked on _/admin_, and _/admin/settings_, however it would + * not be invoked for _/_, or _/posts_. + * + * Examples: + * + * var app = connect(); + * app.use(connect.favicon()); + * app.use(connect.logger()); + * app.use(connect.static(__dirname + '/public')); + * + * If we wanted to prefix static files with _/public_, we could + * "mount" the `static()` middleware: + * + * app.use('/public', connect.static(__dirname + '/public')); + * + * This api is chainable, so the following is valid: + * + * connect() + * .use(connect.favicon()) + * .use(connect.logger()) + * .use(connect.static(__dirname + '/public')) + * .listen(3000); + * + * @param {String|Function|Server} route, callback or server + * @param {Function|Server} callback or server + * @return {Server} for chaining + * @api public + */ + +app.use = function(route, fn){ + // default route to '/' + if ('string' != typeof route) { + fn = route; + route = '/'; + } + + // wrap sub-apps + if ('function' == typeof fn.handle) { + var server = fn; + fn.route = route; + fn = function(req, res, next){ + server.handle(req, res, next); + }; + } + + // wrap vanilla http.Servers + if (fn instanceof http.Server) { + fn = fn.listeners('request')[0]; + } + + // strip trailing slash + if ('/' == route[route.length - 1]) { + route = route.slice(0, -1); + } + + // add the middleware + debug('use %s %s', route || '/', fn.name || 'anonymous'); + this.stack.push({ route: route, handle: fn }); + + return this; +}; + +/** + * Handle server requests, punting them down + * the middleware stack. + * + * @api private + */ + +app.handle = function(req, res, out) { + var stack = this.stack + , search = 1 + req.url.indexOf('?') + , pathlength = search ? search - 1 : req.url.length + , fqdn = 1 + req.url.substr(0, pathlength).indexOf('://') + , protohost = fqdn ? req.url.substr(0, req.url.indexOf('/', 2 + fqdn)) : '' + , removed = '' + , slashAdded = false + , index = 0; + + function next(err) { + var layer, path, c; + + if (slashAdded) { + req.url = req.url.substr(1); + slashAdded = false; + } + + req.url = protohost + removed + req.url.substr(protohost.length); + req.originalUrl = req.originalUrl || req.url; + removed = ''; + + // next callback + layer = stack[index++]; + + // all done + if (!layer || res.headerSent) { + // delegate to parent + if (out) return out(err); + + // unhandled error + if (err) { + // default to 500 + if (res.statusCode < 400) res.statusCode = 500; + debug('default %s', res.statusCode); + + // respect err.status + if (err.status) res.statusCode = err.status; + + // production gets a basic error message + var msg = 'production' == env + ? http.STATUS_CODES[res.statusCode] + : err.stack || err.toString(); + msg = utils.escape(msg); + + // log to stderr in a non-test env + if ('test' != env) console.error(err.stack || err.toString()); + if (res.headerSent) return req.socket.destroy(); + res.setHeader('Content-Type', 'text/html'); + res.setHeader('Content-Length', Buffer.byteLength(msg)); + if ('HEAD' == req.method) return res.end(); + res.end(msg); + } else { + debug('default 404'); + res.statusCode = 404; + res.setHeader('Content-Type', 'text/html'); + if ('HEAD' == req.method) return res.end(); + res.end('Cannot ' + utils.escape(req.method) + ' ' + utils.escape(req.originalUrl)); + } + return; + } + + try { + path = utils.parseUrl(req).pathname; + if (undefined == path) path = '/'; + + // skip this layer if the route doesn't match. + if (0 != path.toLowerCase().indexOf(layer.route.toLowerCase())) return next(err); + + c = path[layer.route.length]; + if (c && '/' != c && '.' != c) return next(err); + + // Call the layer handler + // Trim off the part of the url that matches the route + removed = layer.route; + req.url = protohost + req.url.substr(protohost.length + removed.length); + + // Ensure leading slash + if (!fqdn && '/' != req.url[0]) { + req.url = '/' + req.url; + slashAdded = true; + } + + debug('%s %s : %s', layer.handle.name || 'anonymous', layer.route, req.originalUrl); + var arity = layer.handle.length; + if (err) { + if (arity === 4) { + layer.handle(err, req, res, next); + } else { + next(err); + } + } else if (arity < 4) { + layer.handle(req, res, next); + } else { + next(); + } + } catch (e) { + next(e); + } + } + next(); +}; + +/** + * Listen for connections. + * + * This method takes the same arguments + * as node's `http.Server#listen()`. + * + * HTTP and HTTPS: + * + * If you run your application both as HTTP + * and HTTPS you may wrap them individually, + * since your Connect "server" is really just + * a JavaScript `Function`. + * + * var connect = require('connect') + * , http = require('http') + * , https = require('https'); + * + * var app = connect(); + * + * http.createServer(app).listen(80); + * https.createServer(options, app).listen(443); + * + * @return {http.Server} + * @api public + */ + +app.listen = function(){ + var server = http.createServer(this); + return server.listen.apply(server, arguments); +}; diff --git a/server/node_modules/express/node_modules/connect/lib/public/directory.html b/server/node_modules/express/node_modules/connect/lib/public/directory.html new file mode 100644 index 0000000..2d63704 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/public/directory.html @@ -0,0 +1,81 @@ + + + + + listing directory {directory} + + + + + +
    +

    {linked-path}

    + {files} +
    + + \ No newline at end of file diff --git a/server/node_modules/express/node_modules/connect/lib/public/error.html b/server/node_modules/express/node_modules/connect/lib/public/error.html new file mode 100644 index 0000000..a6d3faf --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/public/error.html @@ -0,0 +1,14 @@ + + + + {error} + + + +
    +

    {title}

    +

    {statusCode} {error}

    +
      {stack}
    +
    + + diff --git a/server/node_modules/express/node_modules/connect/lib/public/favicon.ico b/server/node_modules/express/node_modules/connect/lib/public/favicon.ico new file mode 100644 index 0000000..895fc96 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/favicon.ico differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page.png new file mode 100644 index 0000000..03ddd79 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_add.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_add.png new file mode 100644 index 0000000..d5bfa07 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_add.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_attach.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_attach.png new file mode 100644 index 0000000..89ee2da Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_attach.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_code.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_code.png new file mode 100644 index 0000000..f7ea904 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_code.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_copy.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_copy.png new file mode 100644 index 0000000..195dc6d Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_copy.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_delete.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_delete.png new file mode 100644 index 0000000..3141467 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_delete.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_edit.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_edit.png new file mode 100644 index 0000000..046811e Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_edit.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_error.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_error.png new file mode 100644 index 0000000..f07f449 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_error.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_excel.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_excel.png new file mode 100644 index 0000000..eb6158e Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_excel.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_find.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_find.png new file mode 100644 index 0000000..2f19388 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_find.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_gear.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_gear.png new file mode 100644 index 0000000..8e83281 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_gear.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_go.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_go.png new file mode 100644 index 0000000..80fe1ed Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_go.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_green.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_green.png new file mode 100644 index 0000000..de8e003 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_green.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_key.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_key.png new file mode 100644 index 0000000..d6626cb Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_key.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_lightning.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_lightning.png new file mode 100644 index 0000000..7e56870 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_lightning.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_link.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_link.png new file mode 100644 index 0000000..312eab0 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_link.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_paintbrush.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_paintbrush.png new file mode 100644 index 0000000..246a2f0 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_paintbrush.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_paste.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_paste.png new file mode 100644 index 0000000..968f073 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_paste.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_red.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_red.png new file mode 100644 index 0000000..0b18247 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_red.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_refresh.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_refresh.png new file mode 100644 index 0000000..cf347c7 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_refresh.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_save.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_save.png new file mode 100644 index 0000000..caea546 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_save.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white.png new file mode 100644 index 0000000..8b8b1ca Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_acrobat.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_acrobat.png new file mode 100644 index 0000000..8f8095e Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_acrobat.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_actionscript.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_actionscript.png new file mode 100644 index 0000000..159b240 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_actionscript.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_add.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_add.png new file mode 100644 index 0000000..aa23dde Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_add.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_c.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_c.png new file mode 100644 index 0000000..34a05cc Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_c.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_camera.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_camera.png new file mode 100644 index 0000000..f501a59 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_camera.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_cd.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_cd.png new file mode 100644 index 0000000..848bdaf Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_cd.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_code.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_code.png new file mode 100644 index 0000000..0c76bd1 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_code.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_code_red.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_code_red.png new file mode 100644 index 0000000..87a6914 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_code_red.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_coldfusion.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_coldfusion.png new file mode 100644 index 0000000..c66011f Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_coldfusion.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_compressed.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_compressed.png new file mode 100644 index 0000000..2b6b100 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_compressed.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_copy.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_copy.png new file mode 100644 index 0000000..a9f31a2 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_copy.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_cplusplus.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_cplusplus.png new file mode 100644 index 0000000..a87cf84 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_cplusplus.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_csharp.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_csharp.png new file mode 100644 index 0000000..ffb8fc9 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_csharp.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_cup.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_cup.png new file mode 100644 index 0000000..0a7d6f4 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_cup.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_database.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_database.png new file mode 100644 index 0000000..bddba1f Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_database.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_delete.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_delete.png new file mode 100644 index 0000000..af1ecaf Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_delete.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_dvd.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_dvd.png new file mode 100644 index 0000000..4cc537a Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_dvd.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_edit.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_edit.png new file mode 100644 index 0000000..b93e776 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_edit.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_error.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_error.png new file mode 100644 index 0000000..9fc5a0a Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_error.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_excel.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_excel.png new file mode 100644 index 0000000..b977d7e Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_excel.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_find.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_find.png new file mode 100644 index 0000000..5818436 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_find.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_flash.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_flash.png new file mode 100644 index 0000000..5769120 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_flash.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_freehand.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_freehand.png new file mode 100644 index 0000000..8d719df Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_freehand.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_gear.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_gear.png new file mode 100644 index 0000000..106f5aa Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_gear.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_get.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_get.png new file mode 100644 index 0000000..e4a1ecb Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_get.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_go.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_go.png new file mode 100644 index 0000000..7e62a92 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_go.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_h.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_h.png new file mode 100644 index 0000000..e902abb Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_h.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_horizontal.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_horizontal.png new file mode 100644 index 0000000..1d2d0a4 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_horizontal.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_key.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_key.png new file mode 100644 index 0000000..d616484 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_key.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_lightning.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_lightning.png new file mode 100644 index 0000000..7215d1e Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_lightning.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_link.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_link.png new file mode 100644 index 0000000..bf7bd1c Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_link.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_magnify.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_magnify.png new file mode 100644 index 0000000..f6b74cc Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_magnify.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_medal.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_medal.png new file mode 100644 index 0000000..d3fffb6 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_medal.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_office.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_office.png new file mode 100644 index 0000000..a65bcb3 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_office.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_paint.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_paint.png new file mode 100644 index 0000000..23a37b8 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_paint.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_paintbrush.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_paintbrush.png new file mode 100644 index 0000000..f907e44 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_paintbrush.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_paste.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_paste.png new file mode 100644 index 0000000..5b2cbb3 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_paste.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_php.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_php.png new file mode 100644 index 0000000..7868a25 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_php.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_picture.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_picture.png new file mode 100644 index 0000000..134b669 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_picture.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_powerpoint.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_powerpoint.png new file mode 100644 index 0000000..c4eff03 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_powerpoint.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_put.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_put.png new file mode 100644 index 0000000..884ffd6 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_put.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_ruby.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_ruby.png new file mode 100644 index 0000000..f59b7c4 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_ruby.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_stack.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_stack.png new file mode 100644 index 0000000..44084ad Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_stack.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_star.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_star.png new file mode 100644 index 0000000..3a1441c Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_star.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_swoosh.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_swoosh.png new file mode 100644 index 0000000..e770829 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_swoosh.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_text.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_text.png new file mode 100644 index 0000000..813f712 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_text.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_text_width.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_text_width.png new file mode 100644 index 0000000..d9cf132 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_text_width.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_tux.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_tux.png new file mode 100644 index 0000000..52699bf Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_tux.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_vector.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_vector.png new file mode 100644 index 0000000..4a05955 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_vector.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_visualstudio.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_visualstudio.png new file mode 100644 index 0000000..a0a433d Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_visualstudio.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_width.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_width.png new file mode 100644 index 0000000..1eb8809 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_width.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_word.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_word.png new file mode 100644 index 0000000..ae8ecbf Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_word.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_world.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_world.png new file mode 100644 index 0000000..6ed2490 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_world.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_wrench.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_wrench.png new file mode 100644 index 0000000..fecadd0 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_wrench.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_zip.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_zip.png new file mode 100644 index 0000000..fd4bbcc Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_white_zip.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_word.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_word.png new file mode 100644 index 0000000..834cdfa Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_word.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/icons/page_world.png b/server/node_modules/express/node_modules/connect/lib/public/icons/page_world.png new file mode 100644 index 0000000..b8895dd Binary files /dev/null and b/server/node_modules/express/node_modules/connect/lib/public/icons/page_world.png differ diff --git a/server/node_modules/express/node_modules/connect/lib/public/style.css b/server/node_modules/express/node_modules/connect/lib/public/style.css new file mode 100644 index 0000000..32b6507 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/public/style.css @@ -0,0 +1,141 @@ +body { + margin: 0; + padding: 80px 100px; + font: 13px "Helvetica Neue", "Lucida Grande", "Arial"; + background: #ECE9E9 -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ECE9E9)); + background: #ECE9E9 -moz-linear-gradient(top, #fff, #ECE9E9); + background-repeat: no-repeat; + color: #555; + -webkit-font-smoothing: antialiased; +} +h1, h2, h3 { + margin: 0; + font-size: 22px; + color: #343434; +} +h1 em, h2 em { + padding: 0 5px; + font-weight: normal; +} +h1 { + font-size: 60px; +} +h2 { + margin-top: 10px; +} +h3 { + margin: 5px 0 10px 0; + padding-bottom: 5px; + border-bottom: 1px solid #eee; + font-size: 18px; +} +ul { + margin: 0; + padding: 0; +} +ul li { + margin: 5px 0; + padding: 3px 8px; + list-style: none; +} +ul li:hover { + cursor: pointer; + color: #2e2e2e; +} +ul li .path { + padding-left: 5px; + font-weight: bold; +} +ul li .line { + padding-right: 5px; + font-style: italic; +} +ul li:first-child .path { + padding-left: 0; +} +p { + line-height: 1.5; +} +a { + color: #555; + text-decoration: none; +} +a:hover { + color: #303030; +} +#stacktrace { + margin-top: 15px; +} +.directory h1 { + margin-bottom: 15px; + font-size: 18px; +} +ul#files { + width: 100%; + height: 500px; +} +ul#files li { + padding: 0; +} +ul#files li img { + position: absolute; + top: 5px; + left: 5px; +} +ul#files li a { + position: relative; + display: block; + margin: 1px; + width: 30%; + height: 25px; + line-height: 25px; + text-indent: 8px; + float: left; + border: 1px solid transparent; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + overflow: hidden; + text-overflow: ellipsis; +} +ul#files li a.icon { + text-indent: 25px; +} +ul#files li a:focus, +ul#files li a:hover { + outline: none; + background: rgba(255,255,255,0.65); + border: 1px solid #ececec; +} +ul#files li a.highlight { + -webkit-transition: background .4s ease-in-out; + background: #ffff4f; + border-color: #E9DC51; +} +#search { + display: block; + position: fixed; + top: 20px; + right: 20px; + width: 90px; + -webkit-transition: width ease 0.2s, opacity ease 0.4s; + -moz-transition: width ease 0.2s, opacity ease 0.4s; + -webkit-border-radius: 32px; + -moz-border-radius: 32px; + -webkit-box-shadow: inset 0px 0px 3px rgba(0, 0, 0, 0.25), inset 0px 1px 3px rgba(0, 0, 0, 0.7), 0px 1px 0px rgba(255, 255, 255, 0.03); + -moz-box-shadow: inset 0px 0px 3px rgba(0, 0, 0, 0.25), inset 0px 1px 3px rgba(0, 0, 0, 0.7), 0px 1px 0px rgba(255, 255, 255, 0.03); + -webkit-font-smoothing: antialiased; + text-align: left; + font: 13px "Helvetica Neue", Arial, sans-serif; + padding: 4px 10px; + border: none; + background: transparent; + margin-bottom: 0; + outline: none; + opacity: 0.7; + color: #888; +} +#search:focus { + width: 120px; + opacity: 1.0; +} diff --git a/server/node_modules/express/node_modules/connect/lib/utils.js b/server/node_modules/express/node_modules/connect/lib/utils.js new file mode 100644 index 0000000..9839bc3 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/lib/utils.js @@ -0,0 +1,408 @@ + +/*! + * Connect - utils + * Copyright(c) 2010 Sencha Inc. + * Copyright(c) 2011 TJ Holowaychuk + * MIT Licensed + */ + +/** + * Module dependencies. + */ + +var http = require('http') + , crypto = require('crypto') + , parse = require('url').parse + , sep = require('path').sep + , signature = require('cookie-signature') + , nodeVersion = process.versions.node.split('.'); + +// pause is broken in node < 0.10 +exports.brokenPause = parseInt(nodeVersion[0], 10) === 0 + && parseInt(nodeVersion[1], 10) < 10; + +/** + * Return `true` if the request has a body, otherwise return `false`. + * + * @param {IncomingMessage} req + * @return {Boolean} + * @api private + */ + +exports.hasBody = function(req) { + var encoding = 'transfer-encoding' in req.headers; + var length = 'content-length' in req.headers && req.headers['content-length'] !== '0'; + return encoding || length; +}; + +/** + * Extract the mime type from the given request's + * _Content-Type_ header. + * + * @param {IncomingMessage} req + * @return {String} + * @api private + */ + +exports.mime = function(req) { + var str = req.headers['content-type'] || ''; + return str.split(';')[0]; +}; + +/** + * Generate an `Error` from the given status `code` + * and optional `msg`. + * + * @param {Number} code + * @param {String} msg + * @return {Error} + * @api private + */ + +exports.error = function(code, msg){ + var err = new Error(msg || http.STATUS_CODES[code]); + err.status = code; + return err; +}; + +/** + * Return md5 hash of the given string and optional encoding, + * defaulting to hex. + * + * utils.md5('wahoo'); + * // => "e493298061761236c96b02ea6aa8a2ad" + * + * @param {String} str + * @param {String} encoding + * @return {String} + * @api private + */ + +exports.md5 = function(str, encoding){ + return crypto + .createHash('md5') + .update(str, 'utf8') + .digest(encoding || 'hex'); +}; + +/** + * Merge object b with object a. + * + * var a = { foo: 'bar' } + * , b = { bar: 'baz' }; + * + * utils.merge(a, b); + * // => { foo: 'bar', bar: 'baz' } + * + * @param {Object} a + * @param {Object} b + * @return {Object} + * @api private + */ + +exports.merge = function(a, b){ + if (a && b) { + for (var key in b) { + a[key] = b[key]; + } + } + return a; +}; + +/** + * Escape the given string of `html`. + * + * @param {String} html + * @return {String} + * @api private + */ + +exports.escape = function(html){ + return String(html) + .replace(/&(?!\w+;)/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +}; + +/** + * Sign the given `val` with `secret`. + * + * @param {String} val + * @param {String} secret + * @return {String} + * @api private + */ + +exports.sign = function(val, secret){ + console.warn('do not use utils.sign(), use https://github.com/visionmedia/node-cookie-signature') + return val + '.' + crypto + .createHmac('sha256', secret) + .update(val) + .digest('base64') + .replace(/=+$/, ''); +}; + +/** + * Unsign and decode the given `val` with `secret`, + * returning `false` if the signature is invalid. + * + * @param {String} val + * @param {String} secret + * @return {String|Boolean} + * @api private + */ + +exports.unsign = function(val, secret){ + console.warn('do not use utils.unsign(), use https://github.com/visionmedia/node-cookie-signature') + var str = val.slice(0, val.lastIndexOf('.')); + return exports.sign(str, secret) == val + ? str + : false; +}; + +/** + * Parse signed cookies, returning an object + * containing the decoded key/value pairs, + * while removing the signed key from `obj`. + * + * @param {Object} obj + * @return {Object} + * @api private + */ + +exports.parseSignedCookies = function(obj, secret){ + var ret = {}; + Object.keys(obj).forEach(function(key){ + var val = obj[key]; + if (0 == val.indexOf('s:')) { + val = signature.unsign(val.slice(2), secret); + if (val) { + ret[key] = val; + delete obj[key]; + } + } + }); + return ret; +}; + +/** + * Parse a signed cookie string, return the decoded value + * + * @param {String} str signed cookie string + * @param {String} secret + * @return {String} decoded value + * @api private + */ + +exports.parseSignedCookie = function(str, secret){ + return 0 == str.indexOf('s:') + ? signature.unsign(str.slice(2), secret) + : str; +}; + +/** + * Parse JSON cookies. + * + * @param {Object} obj + * @return {Object} + * @api private + */ + +exports.parseJSONCookies = function(obj){ + Object.keys(obj).forEach(function(key){ + var val = obj[key]; + var res = exports.parseJSONCookie(val); + if (res) obj[key] = res; + }); + return obj; +}; + +/** + * Parse JSON cookie string + * + * @param {String} str + * @return {Object} Parsed object or null if not json cookie + * @api private + */ + +exports.parseJSONCookie = function(str) { + if (0 == str.indexOf('j:')) { + try { + return JSON.parse(str.slice(2)); + } catch (err) { + // no op + } + } +}; + +/** + * Pause `data` and `end` events on the given `obj`. + * Middleware performing async tasks _should_ utilize + * this utility (or similar), to re-emit data once + * the async operation has completed, otherwise these + * events may be lost. Pause is only required for + * node versions less than 10, and is replaced with + * noop's otherwise. + * + * var pause = utils.pause(req); + * fs.readFile(path, function(){ + * next(); + * pause.resume(); + * }); + * + * @param {Object} obj + * @return {Object} + * @api private + */ + +exports.pause = exports.brokenPause + ? require('pause') + : function () { + return { + end: noop, + resume: noop + } + } + +/** + * Strip `Content-*` headers from `res`. + * + * @param {ServerResponse} res + * @api private + */ + +exports.removeContentHeaders = function(res){ + if (!res._headers) return; + Object.keys(res._headers).forEach(function(field){ + if (0 == field.indexOf('content')) { + res.removeHeader(field); + } + }); +}; + +/** + * Check if `req` is a conditional GET request. + * + * @param {IncomingMessage} req + * @return {Boolean} + * @api private + */ + +exports.conditionalGET = function(req) { + return req.headers['if-modified-since'] + || req.headers['if-none-match']; +}; + +/** + * Respond with 401 "Unauthorized". + * + * @param {ServerResponse} res + * @param {String} realm + * @api private + */ + +exports.unauthorized = function(res, realm) { + res.statusCode = 401; + res.setHeader('WWW-Authenticate', 'Basic realm="' + realm + '"'); + res.end('Unauthorized'); +}; + +/** + * Respond with 304 "Not Modified". + * + * @param {ServerResponse} res + * @param {Object} headers + * @api private + */ + +exports.notModified = function(res) { + exports.removeContentHeaders(res); + res.statusCode = 304; + res.end(); +}; + +/** + * Return an ETag in the form of `"-"` + * from the given `stat`. + * + * @param {Object} stat + * @return {String} + * @api private + */ + +exports.etag = function(stat) { + return '"' + stat.size + '-' + Number(stat.mtime) + '"'; +}; + +/** + * Parse the given Cache-Control `str`. + * + * @param {String} str + * @return {Object} + * @api private + */ + +exports.parseCacheControl = function(str){ + var directives = str.split(',') + , obj = {}; + + for(var i = 0, len = directives.length; i < len; i++) { + var parts = directives[i].split('=') + , key = parts.shift().trim() + , val = parseInt(parts.shift(), 10); + + obj[key] = isNaN(val) ? true : val; + } + + return obj; +}; + +/** + * Parse the `req` url with memoization. + * + * @param {ServerRequest} req + * @return {Object} + * @api private + */ + +exports.parseUrl = function(req){ + var parsed = req._parsedUrl; + if (parsed && parsed.href == req.url) { + return parsed; + } else { + parsed = parse(req.url); + + if (parsed.auth && !parsed.protocol && ~parsed.href.indexOf('//')) { + // This parses pathnames, and a strange pathname like //r@e should work + parsed = parse(req.url.replace(/@/g, '%40')); + } + + return req._parsedUrl = parsed; + } +}; + +/** + * Parse byte `size` string. + * + * @param {String} size + * @return {Number} + * @api private + */ + +exports.parseBytes = require('bytes'); + +/** + * Normalizes the path separator from system separator + * to URL separator, aka `/`. + * + * @param {String} path + * @return {String} + * @api private + */ + +exports.normalizeSlashes = function normalizeSlashes(path) { + return path.split(sep).join('/'); +}; + +function noop() {} diff --git a/server/node_modules/express/node_modules/connect/node_modules/bytes/.npmignore b/server/node_modules/express/node_modules/connect/node_modules/bytes/.npmignore new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/bytes/.npmignore @@ -0,0 +1 @@ +test diff --git a/server/node_modules/express/node_modules/connect/node_modules/bytes/History.md b/server/node_modules/express/node_modules/connect/node_modules/bytes/History.md new file mode 100644 index 0000000..f233ed1 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/bytes/History.md @@ -0,0 +1,15 @@ + +0.2.1 / 2013-04-01 +================== + + * add .component + +0.2.0 / 2012-10-28 +================== + + * bytes(200).should.eql('200b') + +0.1.0 / 2012-07-04 +================== + + * add bytes to string conversion [yields] diff --git a/server/node_modules/express/node_modules/connect/node_modules/bytes/Makefile b/server/node_modules/express/node_modules/connect/node_modules/bytes/Makefile new file mode 100644 index 0000000..8e8640f --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/bytes/Makefile @@ -0,0 +1,7 @@ + +test: + @./node_modules/.bin/mocha \ + --reporter spec \ + --require should + +.PHONY: test \ No newline at end of file diff --git a/server/node_modules/express/node_modules/connect/node_modules/bytes/Readme.md b/server/node_modules/express/node_modules/connect/node_modules/bytes/Readme.md new file mode 100644 index 0000000..9325d5b --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/bytes/Readme.md @@ -0,0 +1,51 @@ +# node-bytes + + Byte string parser / formatter. + +## Example: + +```js +bytes('1kb') +// => 1024 + +bytes('2mb') +// => 2097152 + +bytes('1gb') +// => 1073741824 + +bytes(1073741824) +// => 1gb +``` + +## Installation + +``` +$ npm install bytes +$ component install visionmedia/bytes.js +``` + +## License + +(The MIT License) + +Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/server/node_modules/express/node_modules/connect/node_modules/bytes/component.json b/server/node_modules/express/node_modules/connect/node_modules/bytes/component.json new file mode 100644 index 0000000..2929c25 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/bytes/component.json @@ -0,0 +1,7 @@ +{ + "name": "bytes", + "description": "byte size string parser / serializer", + "keywords": ["bytes", "utility"], + "version": "0.2.1", + "scripts": ["index.js"] +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/bytes/index.js b/server/node_modules/express/node_modules/connect/node_modules/bytes/index.js new file mode 100644 index 0000000..70b2e01 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/bytes/index.js @@ -0,0 +1,39 @@ + +/** + * Parse byte `size` string. + * + * @param {String} size + * @return {Number} + * @api public + */ + +module.exports = function(size) { + if ('number' == typeof size) return convert(size); + var parts = size.match(/^(\d+(?:\.\d+)?) *(kb|mb|gb)$/) + , n = parseFloat(parts[1]) + , type = parts[2]; + + var map = { + kb: 1 << 10 + , mb: 1 << 20 + , gb: 1 << 30 + }; + + return map[type] * n; +}; + +/** + * convert bytes into string. + * + * @param {Number} b - bytes to convert + * @return {String} + * @api public + */ + +function convert (b) { + var gb = 1 << 30, mb = 1 << 20, kb = 1 << 10; + if (b >= gb) return (Math.round(b / gb * 100) / 100) + 'gb'; + if (b >= mb) return (Math.round(b / mb * 100) / 100) + 'mb'; + if (b >= kb) return (Math.round(b / kb * 100) / 100) + 'kb'; + return b + 'b'; +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/bytes/package.json b/server/node_modules/express/node_modules/connect/node_modules/bytes/package.json new file mode 100644 index 0000000..078c602 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/bytes/package.json @@ -0,0 +1,29 @@ +{ + "name": "bytes", + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca", + "url": "http://tjholowaychuk.com" + }, + "description": "byte size string parser / serializer", + "version": "0.2.1", + "main": "index.js", + "dependencies": {}, + "devDependencies": { + "mocha": "*", + "should": "*" + }, + "component": { + "scripts": { + "bytes/index.js": "index.js" + } + }, + "readme": "# node-bytes\n\n Byte string parser / formatter.\n\n## Example:\n\n```js\nbytes('1kb')\n// => 1024\n\nbytes('2mb')\n// => 2097152\n\nbytes('1gb')\n// => 1073741824\n\nbytes(1073741824)\n// => 1gb\n```\n\n## Installation\n\n```\n$ npm install bytes\n$ component install visionmedia/bytes.js\n```\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", + "readmeFilename": "Readme.md", + "_id": "bytes@0.2.1", + "dist": { + "shasum": "43d9b02cfa27c15e8a55e51bc41b3150a3dd7811" + }, + "_from": "bytes@0.2.1", + "_resolved": "https://registry.npmjs.org/bytes/-/bytes-0.2.1.tgz" +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/methods/index.js b/server/node_modules/express/node_modules/connect/node_modules/methods/index.js new file mode 100644 index 0000000..297d022 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/methods/index.js @@ -0,0 +1,26 @@ + +module.exports = [ + 'get' + , 'post' + , 'put' + , 'head' + , 'delete' + , 'options' + , 'trace' + , 'copy' + , 'lock' + , 'mkcol' + , 'move' + , 'propfind' + , 'proppatch' + , 'unlock' + , 'report' + , 'mkactivity' + , 'checkout' + , 'merge' + , 'm-search' + , 'notify' + , 'subscribe' + , 'unsubscribe' + , 'patch' +]; \ No newline at end of file diff --git a/server/node_modules/express/node_modules/connect/node_modules/methods/package.json b/server/node_modules/express/node_modules/connect/node_modules/methods/package.json new file mode 100644 index 0000000..f867a8b --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/methods/package.json @@ -0,0 +1,20 @@ +{ + "name": "methods", + "version": "0.0.1", + "description": "HTTP methods that node supports", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "http", + "methods" + ], + "author": { + "name": "TJ Holowaychuk" + }, + "license": "MIT", + "readme": "ERROR: No README data found!", + "_id": "methods@0.0.1", + "_from": "methods@0.0.1" +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/.jshintrc b/server/node_modules/express/node_modules/connect/node_modules/multiparty/.jshintrc new file mode 100644 index 0000000..a93b50c --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/.jshintrc @@ -0,0 +1,70 @@ +{ + // Settings + "passfail" : false, // Stop on first error. + "maxerr" : 100, // Maximum errors before stopping. + + + // Predefined globals whom JSHint will ignore. + "browser" : false, // Standard browser globals e.g. `window`, `document`. + + "node" : true, + "rhino" : false, + "couch" : false, + "wsh" : false, // Windows Scripting Host. + + "jquery" : false, + "prototypejs" : false, + "mootools" : false, + "dojo" : false, + + + "predef" : [ + "describe", "it", "before", "after" + ], + + // Development. + "debug" : true, // Allow debugger statements e.g. browser breakpoints. + "devel" : true, // Allow development statements e.g. `console.log();`. + + + // EcmaScript 5. + "es5" : true, // Allow EcmaScript 5 syntax. + "strict" : false, // Require `use strict` pragma in every file. + "globalstrict" : true, // Allow global "use strict" (also enables 'strict'). + + + // The Good Parts. + "asi" : true, // Tolerate Automatic Semicolon Insertion (no semicolons). + "laxbreak" : false, // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons. + "laxcomma" : true, + "bitwise" : false, // Prohibit bitwise operators (&, |, ^, etc.). + "boss" : true, // Tolerate assignments inside if, for & while. Usually conditions & loops are for comparison, not assignments. + "curly" : false, // Require {} for every new block or scope. + "eqeqeq" : true, // Require triple equals i.e. `===`. + "eqnull" : true, // Tolerate use of `== null`. + "evil" : false, // Tolerate use of `eval`. + "expr" : false, // Tolerate `ExpressionStatement` as Programs. + "forin" : false, // Prohibt `for in` loops without `hasOwnProperty`. + "immed" : true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );` + "latedef" : false, // Prohibit variable use before definition. + "loopfunc" : false, // Allow functions to be defined within loops. + "noarg" : true, // Prohibit use of `arguments.caller` and `arguments.callee`. + "regexp" : false, // Prohibit `.` and `[^...]` in regular expressions. + "regexdash" : false, // Tolerate unescaped last dash i.e. `[-...]`. + "scripturl" : false, // Tolerate script-targeted URLs. + "shadow" : false, // Allows re-define variables later in code e.g. `var x=1; x=2;`. + "supernew" : false, // Tolerate `new function () { ... };` and `new Object;`. + "undef" : true, // Require all non-global variables be declared before they are used. + + + // Persone styling prefrences. + "newcap" : true, // Require capitalization of all constructor functions e.g. `new F()`. + "noempty" : true, // Prohibit use of empty blocks. + "nonew" : true, // Prohibit use of constructors for side-effects. + "nomen" : false, // Prohibit use of initial or trailing underbars in names. + "onevar" : false, // Allow only one `var` statement per function. + "plusplus" : false, // Prohibit use of `++` & `--`. + "sub" : false, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`. + "trailing" : true, // Prohibit trailing whitespaces. + "white" : false // Check against strict whitespace and indentation rules. +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/.npmignore b/server/node_modules/express/node_modules/connect/node_modules/multiparty/.npmignore new file mode 100644 index 0000000..07e6e47 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/.npmignore @@ -0,0 +1 @@ +/node_modules diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/.travis.yml b/server/node_modules/express/node_modules/connect/node_modules/multiparty/.travis.yml new file mode 100644 index 0000000..b1fc4b0 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/.travis.yml @@ -0,0 +1,6 @@ +language: node_js +node_js: + - "0.8" + - "0.10" +before_script: + - ulimit -n 500 diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/CHANGELOG.md b/server/node_modules/express/node_modules/connect/node_modules/multiparty/CHANGELOG.md new file mode 100644 index 0000000..ea54d9a --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/CHANGELOG.md @@ -0,0 +1,191 @@ +### 2.2.0 + + * additional callback API to support multiple files with same field name + * fix assertion crash when max field count is exceeded + * fix assertion crash when client aborts an invalid request + * (>=v0.10 only) unpipe the request when an error occurs to save resources. + * update readable-stream to ~1.1.9 + * fix assertion crash when EMFILE occurrs + * (no more assertions - only 'error' events) + +### 2.1.9 + + * relax content-type detection regex. (thanks amitaibu) + +### 2.1.8 + + * replace deprecated Buffer.write(). (thanks hueniverse) + +### 2.1.7 + + * add repository field to package.json + +### 2.1.6 + + * expose `hash` as an option to `Form`. (thanks wookiehangover) + +### 2.1.5 + + * fix possible 'close' event before all temp files are done + +### 2.1.4 + + * fix crash for invalid requests + +### 2.1.3 + + * add `file.size` + +### 2.1.2 + + * proper backpressure support + * update s3 example + +### 2.1.1 + + * fix uploads larger than 2KB + * fix both s3 and upload example + * add part.byteCount and part.byteOffset + +### 2.1.0 (recalled) + + * Complete rewrite. See README for changes and new API. + +### v1.0.13 + +* Only update hash if update method exists (Sven Lito) +* According to travis v0.10 needs to go quoted (Sven Lito) +* Bumping build node versions (Sven Lito) +* Additional fix for empty requests (Eugene Girshov) +* Change the default to 1000, to match the new Node behaviour. (OrangeDog) +* Add ability to control maxKeys in the querystring parser. (OrangeDog) +* Adjust test case to work with node 0.9.x (Eugene Girshov) +* Update package.json (Sven Lito) +* Path adjustment according to eb4468b (Markus Ast) + +### v1.0.12 + +* Emit error on aborted connections (Eugene Girshov) +* Add support for empty requests (Eugene Girshov) +* Fix name/filename handling in Content-Disposition (jesperp) +* Tolerate malformed closing boundary in multipart (Eugene Girshov) +* Ignore preamble in multipart messages (Eugene Girshov) +* Add support for application/json (Mike Frey, Carlos Rodriguez) +* Add support for Base64 encoding (Elmer Bulthuis) +* Add File#toJSON (TJ Holowaychuk) +* Remove support for Node.js 0.4 & 0.6 (Andrew Kelley) +* Documentation improvements (Sven Lito, Andre Azevedo) +* Add support for application/octet-stream (Ion Lupascu, Chris Scribner) +* Use os.tmpDir() to get tmp directory (Andrew Kelley) +* Improve package.json (Andrew Kelley, Sven Lito) +* Fix benchmark script (Andrew Kelley) +* Fix scope issue in incoming_forms (Sven Lito) +* Fix file handle leak on error (OrangeDog) + +### v1.0.11 + +* Calculate checksums for incoming files (sreuter) +* Add definition parameters to "IncomingForm" as an argument (Math-) + +### v1.0.10 + +* Make parts to be proper Streams (Matt Robenolt) + +### v1.0.9 + +* Emit progress when content length header parsed (Tim Koschützki) +* Fix Readme syntax due to GitHub changes (goob) +* Replace references to old 'sys' module in Readme with 'util' (Peter Sugihara) + +### v1.0.8 + +* Strip potentially unsafe characters when using `keepExtensions: true`. +* Switch to utest / urun for testing +* Add travis build + +### v1.0.7 + +* Remove file from package that was causing problems when installing on windows. (#102) +* Fix typos in Readme (Jason Davies). + +### v1.0.6 + +* Do not default to the default to the field name for file uploads where + filename="". + +### v1.0.5 + +* Support filename="" in multipart parts +* Explain unexpected end() errors in parser better + +**Note:** Starting with this version, formidable emits 'file' events for empty +file input fields. Previously those were incorrectly emitted as regular file +input fields with value = "". + +### v1.0.4 + +* Detect a good default tmp directory regardless of platform. (#88) + +### v1.0.3 + +* Fix problems with utf8 characters (#84) / semicolons in filenames (#58) +* Small performance improvements +* New test suite and fixture system + +### v1.0.2 + +* Exclude node\_modules folder from git +* Implement new `'aborted'` event +* Fix files in example folder to work with recent node versions +* Make gently a devDependency + +[See Commits](https://github.com/felixge/node-formidable/compare/v1.0.1...v1.0.2) + +### v1.0.1 + +* Fix package.json to refer to proper main directory. (#68, Dean Landolt) + +[See Commits](https://github.com/felixge/node-formidable/compare/v1.0.0...v1.0.1) + +### v1.0.0 + +* Add support for multipart boundaries that are quoted strings. (Jeff Craig) + +This marks the beginning of development on version 2.0 which will include +several architectural improvements. + +[See Commits](https://github.com/felixge/node-formidable/compare/v0.9.11...v1.0.0) + +### v0.9.11 + +* Emit `'progress'` event when receiving data, regardless of parsing it. (Tim Koschützki) +* Use [W3C FileAPI Draft](http://dev.w3.org/2006/webapi/FileAPI/) properties for File class + +**Important:** The old property names of the File class will be removed in a +future release. + +[See Commits](https://github.com/felixge/node-formidable/compare/v0.9.10...v0.9.11) + +### Older releases + +These releases were done before starting to maintain the above Changelog: + +* [v0.9.10](https://github.com/felixge/node-formidable/compare/v0.9.9...v0.9.10) +* [v0.9.9](https://github.com/felixge/node-formidable/compare/v0.9.8...v0.9.9) +* [v0.9.8](https://github.com/felixge/node-formidable/compare/v0.9.7...v0.9.8) +* [v0.9.7](https://github.com/felixge/node-formidable/compare/v0.9.6...v0.9.7) +* [v0.9.6](https://github.com/felixge/node-formidable/compare/v0.9.5...v0.9.6) +* [v0.9.5](https://github.com/felixge/node-formidable/compare/v0.9.4...v0.9.5) +* [v0.9.4](https://github.com/felixge/node-formidable/compare/v0.9.3...v0.9.4) +* [v0.9.3](https://github.com/felixge/node-formidable/compare/v0.9.2...v0.9.3) +* [v0.9.2](https://github.com/felixge/node-formidable/compare/v0.9.1...v0.9.2) +* [v0.9.1](https://github.com/felixge/node-formidable/compare/v0.9.0...v0.9.1) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0) +* [v0.1.0](https://github.com/felixge/node-formidable/commits/v0.1.0) diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/LICENSE b/server/node_modules/express/node_modules/connect/node_modules/multiparty/LICENSE new file mode 100644 index 0000000..8488a40 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/LICENSE @@ -0,0 +1,7 @@ +Copyright (C) 2011-2013 Felix Geisendörfer, Andrew Kelley + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/README.md b/server/node_modules/express/node_modules/connect/node_modules/multiparty/README.md new file mode 100644 index 0000000..f149ac0 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/README.md @@ -0,0 +1,177 @@ +[![Build Status](https://travis-ci.org/superjoe30/node-multiparty.png?branch=master)](https://travis-ci.org/superjoe30/node-multiparty) +# multiparty + +Parse http requests with content-type `multipart/form-data`, also known as file uploads. + +See also [busboy](https://github.com/mscdex/busboy) - a +[faster](https://github.com/mscdex/dicer/wiki/Benchmarks) alternative +which may be worth looking into. + +### Why the fork? + + * This module uses the Node.js v0.10 streams properly, *even in Node.js v0.8* + * It will not create a temp file for you unless you want it to. + * Counts bytes and does math to help you figure out the `Content-Length` of + each part. + * You can easily stream uploads to s3 with + [knox](https://github.com/LearnBoost/knox), for [example](examples/s3.js). + * Less bugs. This code is simpler, has all deprecated functionality removed, + has cleaner tests, and does not try to do anything beyond multipart stream + parsing. + +## Installation + +``` +npm install multiparty +``` + +## Usage + + * See [examples](examples). + +Parse an incoming `multipart/form-data` request. + +```js +var multiparty = require('multiparty') + , http = require('http') + , util = require('util') + +http.createServer(function(req, res) { + if (req.url === '/upload' && req.method === 'POST') { + // parse a file upload + var form = new multiparty.Form(); + + form.parse(req, function(err, fields, files) { + res.writeHead(200, {'content-type': 'text/plain'}); + res.write('received upload:\n\n'); + res.end(util.inspect({fields: fields, files: files})); + }); + + return; + } + + // show a file upload form + res.writeHead(200, {'content-type': 'text/html'}); + res.end( + '
    '+ + '
    '+ + '
    '+ + ''+ + '
    ' + ); +}).listen(8080); +``` + +## API + +### multiparty.Form +```js +var form = new multiparty.Form(options) +``` +Creates a new form. Options: + + * `encoding` - sets encoding for the incoming form fields. Defaults to `utf8`. + * `maxFieldSize` - Limits the amount of memory a field (not a file) can + allocate in bytes. If this value is exceeded, an `error` event is emitted. + The default size is 2MB. + * `maxFields` - Limits the number of fields that will be parsed before + emitting an `error` event. A file counts as a field in this case. + Defaults to 1000. + * `autoFields` - Enables `field` events. This is automatically set to `true` + if you add a `field` listener. + * `autoFiles` - Enables `file` events. This is automatically set to `true` + if you add a `file` listener. + * `uploadDir` - Only relevant when `autoFiles` is `true`. The directory for + placing file uploads in. You can move them later using `fs.rename()`. + Defaults to `os.tmpDir()`. + * `hash` - Only relevant when `autoFiles` is `true`. If you want checksums + calculated for incoming files, set this to either `sha1` or `md5`. + Defaults to off. + +#### form.parse(request, [cb]) + +Parses an incoming node.js `request` containing form data. If `cb` is +provided, `autoFields` and `autoFiles` are set to `true` and all fields and +files are collected and passed to the callback: + +```js +form.parse(req, function(err, fieldsObject, filesObject, fieldsList, filesList) { + // ... +}); +``` + +It is often convenient to access a field or file by name. In this situation, +use `fieldsObject` or `filesObject`. However sometimes, as in the case of a +`` the multipart stream will contain +multiple files of the same input name, and you are interested in all of them. +In this case, use `filesList`. + +Another example is when you do not care what the field name of a file is; you +are merely interested in a single upload. In this case, set `maxFields` to 1 +(assuming no other fields expected besides the file) and use `filesList[0]`. + +#### form.bytesReceived + +The amount of bytes received for this form so far. + +#### form.bytesExpected + +The expected number of bytes in this form. + +### Events + +#### 'error' (err) + +You definitely want to handle this event. If not your server *will* crash when +users submit bogus multipart requests! + +#### 'part' (part) + +Emitted when a part is encountered in the request. `part` is a +`ReadableStream`. It also has the following properties: + + * `headers` - the headers for this part. For example, you may be interested + in `content-type`. + * `name` - the field name for this part + * `filename` - only if the part is an incoming file + * `byteOffset` - the byte offset of this part in the request body + * `byteCount` - assuming that this is the last part in the request, + this is the size of this part in bytes. You could use this, for + example, to set the `Content-Length` header if uploading to S3. + If the part had a `Content-Length` header then that value is used + here instead. + +#### 'aborted' + +Emitted when the request is aborted. This event will be followed shortly +by an `error` event. In practice you do not need to handle this event. + +#### 'progress' (bytesReceived, bytesExpected) + +#### 'close' + +Emitted after all parts have been parsed and emitted. Not emitted if an `error` +event is emitted. This is typically when you would send your response. + +#### 'file' (name, file) + +**By default multiparty will not touch your hard drive.** But if you add this +listener, multiparty automatically sets `form.autoFiles` to `true` and will +stream uploads to disk for you. + + * `name` - the field name for this file + * `file` - an object with these properties: + - `fieldName` - same as `name` - the field name for this file + - `originalFilename` - the filename that the user reports for the file + - `path` - the absolute path of the uploaded file on disk + - `headers` - the HTTP headers that were sent along with this file + - `size` - size of the file in bytes + +If you set the `form.hash` option, then `file` will also contain a `hash` +property which is the checksum of the file. + +#### 'field' (name, value) + + * `name` - field name + * `value` - string field value + diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/examples/azureblobstorage.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/examples/azureblobstorage.js new file mode 100644 index 0000000..273c332 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/examples/azureblobstorage.js @@ -0,0 +1,41 @@ +var http = require('http') + , util = require('util') + , multiparty = require('../') + , azure = require('azure') + , PORT = process.env.PORT || 27372 + +var server = http.createServer(function(req, res) { + if (req.url === '/') { + res.writeHead(200, {'content-type': 'text/html'}); + res.end( + '
    '+ + '
    '+ + '
    '+ + ''+ + '
    ' + ); + } else if (req.url === '/upload') { + + var blobService = azure.createBlobService(); + var form = new multiparty.Form(); + form.on('part', function(part) { + if (!part.filename) return; + + var size = part.byteCount - part.byteOffset; + var name = part.filename; + var container = 'blobContainerName'; + + blobService.createBlockBlobFromStream(container, name, part, size, function(error) { + if (error) { + // error handling + } + }); + }); + form.parse(req); + + res.send('File uploaded successfully'); + } +}); +server.listen(PORT, function() { + console.info('listening on http://0.0.0.0:'+PORT+'/'); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/examples/s3.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/examples/s3.js new file mode 100644 index 0000000..60617ba --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/examples/s3.js @@ -0,0 +1,74 @@ +var http = require('http') + , util = require('util') + , multiparty = require('../') + , knox = require('knox') + , Batch = require('batch') + , PORT = process.env.PORT || 27372 + +var s3Client = knox.createClient({ + secure: false, + key: process.env.S3_KEY, + secret: process.env.S3_SECRET, + bucket: process.env.S3_BUCKET, +}); + +var server = http.createServer(function(req, res) { + if (req.url === '/') { + res.writeHead(200, {'content-type': 'text/html'}); + res.end( + '
    '+ + '
    '+ + '
    '+ + ''+ + '
    ' + ); + } else if (req.url === '/upload') { + var headers = { + 'x-amz-acl': 'public-read', + }; + var form = new multiparty.Form(); + var batch = new Batch(); + batch.push(function(cb) { + form.on('field', function(name, value) { + if (name === 'path') { + var destPath = value; + if (destPath[0] !== '/') destPath = '/' + destPath; + cb(null, destPath); + } + }); + }); + batch.push(function(cb) { + form.on('part', function(part) { + if (! part.filename) return; + cb(null, part); + }); + }); + batch.end(function(err, results) { + if (err) throw err; + form.removeListener('close', onEnd); + var destPath = results[0] + , part = results[1]; + + headers['Content-Length'] = part.byteCount; + s3Client.putStream(part, destPath, headers, function(err, s3Response) { + if (err) throw err; + res.statusCode = s3Response.statusCode; + s3Response.pipe(res); + console.log("https://s3.amazonaws.com/" + process.env.S3_BUCKET + destPath); + }); + }); + form.on('close', onEnd); + form.parse(req); + + } else { + res.writeHead(404, {'content-type': 'text/plain'}); + res.end('404'); + } + + function onEnd() { + throw new Error("no uploaded file"); + } +}); +server.listen(PORT, function() { + console.info('listening on http://0.0.0.0:'+PORT+'/'); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/examples/upload.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/examples/upload.js new file mode 100644 index 0000000..5dd3926 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/examples/upload.js @@ -0,0 +1,37 @@ +var http = require('http') + , util = require('util') + , multiparty = require('../') + , PORT = process.env.PORT || 27372 + +var server = http.createServer(function(req, res) { + if (req.url === '/') { + res.writeHead(200, {'content-type': 'text/html'}); + res.end( + '
    '+ + '
    '+ + '
    '+ + ''+ + '
    ' + ); + } else if (req.url === '/upload') { + var form = new multiparty.Form(); + + form.parse(req, function(err, fields, files) { + if (err) { + res.writeHead(400, {'content-type': 'text/plain'}); + res.end("invalid request: " + err.message); + return; + } + res.writeHead(200, {'content-type': 'text/plain'}); + res.write('received fields:\n\n '+util.inspect(fields)); + res.write('\n\n'); + res.end('received files:\n\n '+util.inspect(files)); + }); + } else { + res.writeHead(404, {'content-type': 'text/plain'}); + res.end('404'); + } +}); +server.listen(PORT, function() { + console.info('listening on http://0.0.0.0:'+PORT+'/'); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/index.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/index.js new file mode 100644 index 0000000..71c88ae --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/index.js @@ -0,0 +1,618 @@ +exports.Form = Form; + +var stream = require('readable-stream') + , util = require('util') + , fs = require('fs') + , crypto = require('crypto') + , path = require('path') + , os = require('os') + , StringDecoder = require('string_decoder').StringDecoder + , StreamCounter = require('stream-counter') + +var START = 0 + , START_BOUNDARY = 1 + , HEADER_FIELD_START = 2 + , HEADER_FIELD = 3 + , HEADER_VALUE_START = 4 + , HEADER_VALUE = 5 + , HEADER_VALUE_ALMOST_DONE = 6 + , HEADERS_ALMOST_DONE = 7 + , PART_DATA_START = 8 + , PART_DATA = 9 + , PART_END = 10 + , END = 11 + + , LF = 10 + , CR = 13 + , SPACE = 32 + , HYPHEN = 45 + , COLON = 58 + , A = 97 + , Z = 122 + +var CONTENT_TYPE_RE = /^multipart\/(form-data|related);\s*boundary=(?:"([^"]+)"|([^;]+))$/i; +var FILE_EXT_RE = /(\.[_\-a-zA-Z0-9]{0,16}).*/; +var LAST_BOUNDARY_SUFFIX_LEN = 4; // --\r\n + +util.inherits(Form, stream.Writable); +function Form(options) { + var self = this; + stream.Writable.call(self); + + options = options || {}; + + self.error = null; + self.finished = false; + + self.autoFields = !!options.autoFields; + self.autoFiles = !!options.autoFields; + + self.maxFields = options.maxFields || 1000; + self.maxFieldsSize = options.maxFieldsSize || 2 * 1024 * 1024; + self.uploadDir = options.uploadDir || os.tmpDir(); + self.encoding = options.encoding || 'utf8'; + self.hash = options.hash || false; + + self.bytesReceived = 0; + self.bytesExpected = null; + + self.openedFiles = []; + self.totalFieldSize = 0; + self.totalFieldCount = 0; + self.flushing = 0; + + self.backpressure = false; + self.writeCbs = []; + + if (options.boundary) setUpParser(self, options.boundary); + + self.on('newListener', function(eventName) { + if (eventName === 'file') { + self.autoFiles = true; + } else if (eventName === 'field') { + self.autoFields = true; + } + }); +} + +Form.prototype.parse = function(req, cb) { + var self = this; + + // if the user supplies a callback, this implies autoFields and autoFiles + if (cb) { + self.autoFields = true; + self.autoFiles = true; + } + + self.handleError = handleError; + self.bytesExpected = getBytesExpected(req.headers); + + req.on('error', handleError); + req.on('aborted', onReqAborted); + + var contentType = req.headers['content-type']; + if (!contentType) { + handleError(new Error('missing content-type header')); + return; + } + + var m = contentType.match(CONTENT_TYPE_RE); + if (!m) { + handleError(new Error('unrecognized content-type: ' + contentType)); + return; + } + var boundary = m[2] || m[3]; + setUpParser(self, boundary); + req.pipe(self); + + if (cb) { + var fieldsTable = {}; + var filesTable = {}; + var fieldsList = []; + var filesList = []; + self.on('error', function(err) { + cb(err); + }); + self.on('field', function(name, value) { + fieldsTable[name] = value; + fieldsList.push({name: name, value: value}); + }); + self.on('file', function(name, file) { + filesTable[name] = file; + filesList.push(file); + }); + self.on('close', function() { + cb(null, fieldsTable, filesTable, fieldsList, filesList); + }); + } + + function onReqAborted() { + self.emit('aborted'); + handleError(new Error("Request aborted")); + } + + function handleError(err) { + var first = !self.error; + if (first) { + self.error = err; + req.removeListener('aborted', onReqAborted); + + // welp. 0.8 doesn't support unpipe, too bad so sad. + // let's drop support for 0.8 soon. + if (req.unpipe) { + req.unpipe(self); + } + } + + self.openedFiles.forEach(function(file) { + file.ws.destroy(); + fs.unlink(file.path, function(err) { + // this is already an error condition, ignore 2nd error + }); + }); + self.openedFiles = []; + + if (first) { + self.emit('error', err); + } + } + +}; + +Form.prototype._write = function(buffer, encoding, cb) { + var self = this + , i = 0 + , len = buffer.length + , prevIndex = self.index + , index = self.index + , state = self.state + , lookbehind = self.lookbehind + , boundary = self.boundary + , boundaryChars = self.boundaryChars + , boundaryLength = self.boundary.length + , boundaryEnd = boundaryLength - 1 + , bufferLength = buffer.length + , c + , cl + + for (i = 0; i < len; i++) { + c = buffer[i]; + switch (state) { + case START: + index = 0; + state = START_BOUNDARY; + /* falls through */ + case START_BOUNDARY: + if (index === boundaryLength - 2) { + if (c !== CR) return self.handleError(new Error("Expected CR Received " + c)); + index++; + break; + } else if (index === boundaryLength - 1) { + if (c !== LF) return self.handleError(new Error("Expected LF Received " + c)); + index = 0; + self.onParsePartBegin(); + state = HEADER_FIELD_START; + break; + } + + if (c !== boundary[index+2]) index = -2; + if (c === boundary[index+2]) index++; + break; + case HEADER_FIELD_START: + state = HEADER_FIELD; + self.headerFieldMark = i; + index = 0; + /* falls through */ + case HEADER_FIELD: + if (c === CR) { + self.headerFieldMark = null; + state = HEADERS_ALMOST_DONE; + break; + } + + index++; + if (c === HYPHEN) break; + + if (c === COLON) { + if (index === 1) { + // empty header field + self.handleError(new Error("Empty header field")); + return; + } + self.onParseHeaderField(buffer.slice(self.headerFieldMark, i)); + self.headerFieldMark = null; + state = HEADER_VALUE_START; + break; + } + + cl = lower(c); + if (cl < A || cl > Z) { + self.handleError(new Error("Expected alphabetic character, received " + c)); + return; + } + break; + case HEADER_VALUE_START: + if (c === SPACE) break; + + self.headerValueMark = i; + state = HEADER_VALUE; + /* falls through */ + case HEADER_VALUE: + if (c === CR) { + self.onParseHeaderValue(buffer.slice(self.headerValueMark, i)); + self.headerValueMark = null; + self.onParseHeaderEnd(); + state = HEADER_VALUE_ALMOST_DONE; + } + break; + case HEADER_VALUE_ALMOST_DONE: + if (c !== LF) return self.handleError(new Error("Expected LF Received " + c)); + state = HEADER_FIELD_START; + break; + case HEADERS_ALMOST_DONE: + if (c !== LF) return self.handleError(new Error("Expected LF Received " + c)); + var err = self.onParseHeadersEnd(i + 1); + if (err) return self.handleError(err); + state = PART_DATA_START; + break; + case PART_DATA_START: + state = PART_DATA; + self.partDataMark = i; + /* falls through */ + case PART_DATA: + prevIndex = index; + + if (index === 0) { + // boyer-moore derrived algorithm to safely skip non-boundary data + i += boundaryEnd; + while (i < bufferLength && !(buffer[i] in boundaryChars)) { + i += boundaryLength; + } + i -= boundaryEnd; + c = buffer[i]; + } + + if (index < boundaryLength) { + if (boundary[index] === c) { + if (index === 0) { + self.onParsePartData(buffer.slice(self.partDataMark, i)); + self.partDataMark = null; + } + index++; + } else { + index = 0; + } + } else if (index === boundaryLength) { + index++; + if (c === CR) { + // CR = part boundary + self.partBoundaryFlag = true; + } else if (c === HYPHEN) { + // HYPHEN = end boundary + self.lastBoundaryFlag = true; + } else { + index = 0; + } + } else if (index - 1 === boundaryLength) { + if (self.partBoundaryFlag) { + index = 0; + if (c === LF) { + self.partBoundaryFlag = false; + self.onParsePartEnd(); + self.onParsePartBegin(); + state = HEADER_FIELD_START; + break; + } + } else if (self.lastBoundaryFlag) { + if (c === HYPHEN) { + self.onParsePartEnd(); + self.end(); + state = END; + } else { + index = 0; + } + } else { + index = 0; + } + } + + if (index > 0) { + // when matching a possible boundary, keep a lookbehind reference + // in case it turns out to be a false lead + lookbehind[index-1] = c; + } else if (prevIndex > 0) { + // if our boundary turned out to be rubbish, the captured lookbehind + // belongs to partData + self.onParsePartData(lookbehind.slice(0, prevIndex)); + prevIndex = 0; + self.partDataMark = i; + + // reconsider the current character even so it interrupted the sequence + // it could be the beginning of a new sequence + i--; + } + + break; + case END: + break; + default: + self.handleError(new Error("Parser has invalid state.")); + return; + } + } + + if (self.headerFieldMark != null) { + self.onParseHeaderField(buffer.slice(self.headerFieldMark)); + self.headerFieldMark = 0; + } + if (self.headerValueMark != null) { + self.onParseHeaderValue(buffer.slice(self.headerValueMark)); + self.headerValueMark = 0; + } + if (self.partDataMark != null) { + self.onParsePartData(buffer.slice(self.partDataMark)); + self.partDataMark = 0; + } + + self.index = index; + self.state = state; + + self.bytesReceived += buffer.length; + self.emit('progress', self.bytesReceived, self.bytesExpected); + + if (self.backpressure) { + self.writeCbs.push(cb); + } else { + cb(); + } +}; + +Form.prototype.onParsePartBegin = function() { + clearPartVars(this); +} + +Form.prototype.onParseHeaderField = function(b) { + this.headerField += this.headerFieldDecoder.write(b); +} + +Form.prototype.onParseHeaderValue = function(b) { + this.headerValue += this.headerValueDecoder.write(b); +} + +Form.prototype.onParseHeaderEnd = function() { + this.headerField = this.headerField.toLowerCase(); + this.partHeaders[this.headerField] = this.headerValue; + + var m; + if (this.headerField === 'content-disposition') { + if (m = this.headerValue.match(/\bname="([^"]+)"/i)) { + this.partName = m[1]; + } + this.partFilename = parseFilename(this.headerValue); + } else if (this.headerField === 'content-transfer-encoding') { + this.partTransferEncoding = this.headerValue.toLowerCase(); + } + + this.headerFieldDecoder = new StringDecoder(this.encoding); + this.headerField = ''; + this.headerValueDecoder = new StringDecoder(this.encoding); + this.headerValue = ''; +} + +Form.prototype.onParsePartData = function(b) { + if (this.partTransferEncoding === 'base64') { + this.backpressure = ! this.destStream.write(b.toString('ascii'), 'base64'); + } else { + this.backpressure = ! this.destStream.write(b); + } +} + +Form.prototype.onParsePartEnd = function() { + if (this.destStream) { + flushWriteCbs(this); + var s = this.destStream; + process.nextTick(function() { + s.end(); + }); + } + clearPartVars(this); +} + +Form.prototype.onParseHeadersEnd = function(offset) { + var self = this; + switch(self.partTransferEncoding){ + case 'binary': + case '7bit': + case '8bit': + self.partTransferEncoding = 'binary'; + break; + + case 'base64': break; + default: + return new Error("unknown transfer-encoding: " + self.partTransferEncoding); + } + + self.totalFieldCount += 1; + if (self.totalFieldCount >= self.maxFields) { + return new Error("maxFields " + self.maxFields + " exceeded."); + } + + self.destStream = new stream.PassThrough(); + self.destStream.on('drain', function() { + flushWriteCbs(self); + }); + self.destStream.headers = self.partHeaders; + self.destStream.name = self.partName; + self.destStream.filename = self.partFilename; + self.destStream.byteOffset = self.bytesReceived + offset; + var partContentLength = self.destStream.headers['content-length']; + self.destStream.byteCount = partContentLength ? + parseInt(partContentLength, 10) : + (self.bytesExpected - self.destStream.byteOffset - + self.boundary.length - LAST_BOUNDARY_SUFFIX_LEN); + + self.emit('part', self.destStream); + if (self.destStream.filename == null && self.autoFields) { + handleField(self, self.destStream); + } else if (self.destStream.filename != null && self.autoFiles) { + handleFile(self, self.destStream); + } +} + +function flushWriteCbs(self) { + self.writeCbs.forEach(function(cb) { + process.nextTick(cb); + }); + self.writeCbs = []; + self.backpressure = false; +} + +function getBytesExpected(headers) { + var contentLength = headers['content-length']; + if (contentLength) { + return parseInt(contentLength, 10); + } else if (headers['transfer-encoding'] == null) { + return 0; + } else { + return null; + } +} + +function beginFlush(self) { + self.flushing += 1; +} + +function endFlush(self) { + self.flushing -= 1; + maybeClose(self); +} + +function maybeClose(self) { + if (!self.flushing && self.finished && !self.error) { + self.emit('close'); + } +} + +function handleFile(self, fileStream) { + beginFlush(self); + var file = { + fieldName: fileStream.name, + originalFilename: fileStream.filename, + path: uploadPath(self.uploadDir, fileStream.filename), + headers: fileStream.headers, + }; + file.ws = fs.createWriteStream(file.path); + self.openedFiles.push(file); + fileStream.pipe(file.ws); + var counter = new StreamCounter(); + fileStream.pipe(counter); + var hashWorkaroundStream + , hash = null; + if (self.hash) { + // workaround stream because https://github.com/joyent/node/issues/5216 + hashWorkaroundStream = stream.Writable(); + hash = crypto.createHash(self.hash); + hashWorkaroundStream._write = function(buffer, encoding, callback) { + hash.update(buffer); + callback(); + }; + fileStream.pipe(hashWorkaroundStream); + } + file.ws.on('error', function(err) { + if (!self.error) self.handleError(err); + }); + file.ws.on('close', function() { + if (hash) file.hash = hash.digest('hex'); + file.size = counter.bytes; + self.emit('file', fileStream.name, file); + endFlush(self); + }); +} + +function handleField(self, fieldStream) { + var value = ''; + var decoder = new StringDecoder(self.encoding); + + beginFlush(self); + fieldStream.on('readable', function() { + var buffer = fieldStream.read(); + if (!buffer) return; + + self.totalFieldSize += buffer.length; + if (self.totalFieldSize > self.maxFieldsSize) { + self.handleError(new Error("maxFieldsSize " + self.maxFieldsSize + " exceeded")); + return; + } + value += decoder.write(buffer); + }); + + fieldStream.on('end', function() { + self.emit('field', fieldStream.name, value); + endFlush(self); + }); +} + +function clearPartVars(self) { + self.partHeaders = {}; + self.partName = null; + self.partFilename = null; + self.partTransferEncoding = 'binary'; + self.destStream = null; + + self.headerFieldDecoder = new StringDecoder(self.encoding); + self.headerField = ""; + self.headerValueDecoder = new StringDecoder(self.encoding); + self.headerValue = ""; +} + +function setUpParser(self, boundary) { + self.boundary = new Buffer(boundary.length + 4); + self.boundary.write('\r\n--', 0, boundary.length + 4, 'ascii'); + self.boundary.write(boundary, 4, boundary.length, 'ascii'); + self.lookbehind = new Buffer(self.boundary.length + 8); + self.state = START; + self.boundaryChars = {}; + for (var i = 0; i < self.boundary.length; i++) { + self.boundaryChars[self.boundary[i]] = true; + } + + self.index = null; + self.partBoundaryFlag = false; + self.lastBoundaryFlag = false; + + self.on('finish', function() { + if ((self.state === HEADER_FIELD_START && self.index === 0) || + (self.state === PART_DATA && self.index === self.boundary.length)) + { + self.onParsePartEnd(); + } else if (self.state !== END) { + self.handleError(new Error('stream ended unexpectedly')); + } + self.finished = true; + maybeClose(self); + }); +} + +function uploadPath(baseDir, filename) { + var ext = path.extname(filename).replace(FILE_EXT_RE, '$1'); + var name = process.pid + '-' + + (Math.random() * 0x100000000 + 1).toString(36) + ext; + return path.join(baseDir, name); +} + +function parseFilename(headerValue) { + var m = headerValue.match(/\bfilename="(.*?)"($|; )/i); + if (!m) return; + + var filename = m[1].substr(m[1].lastIndexOf('\\') + 1); + filename = filename.replace(/%22/g, '"'); + filename = filename.replace(/&#([\d]{4});/g, function(m, code) { + return String.fromCharCode(code); + }); + return filename; +} + +function lower(c) { + return c | 0x20; +} + diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/LICENSE b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/LICENSE new file mode 100644 index 0000000..e3d4e69 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/LICENSE @@ -0,0 +1,18 @@ +Copyright Joyent, Inc. and other Node contributors. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/README.md b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/README.md new file mode 100644 index 0000000..be97668 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/README.md @@ -0,0 +1,768 @@ +# readable-stream + +A new class of streams for Node.js + +This module provides the new Stream base classes introduced in Node +v0.10, for use in Node v0.8. You can use it to have programs that +have to work with node v0.8, while being forward-compatible for v0.10 +and beyond. When you drop support for v0.8, you can remove this +module, and only use the native streams. + +This is almost exactly the same codebase as appears in Node v0.10. +However: + +1. The exported object is actually the Readable class. Decorating the + native `stream` module would be global pollution. +2. In v0.10, you can safely use `base64` as an argument to + `setEncoding` in Readable streams. However, in v0.8, the + StringDecoder class has no `end()` method, which is problematic for + Base64. So, don't use that, because it'll break and be weird. + +Other than that, the API is the same as `require('stream')` in v0.10, +so the API docs are reproduced below. + +---------- + + Stability: 2 - Unstable + +A stream is an abstract interface implemented by various objects in +Node. For example a request to an HTTP server is a stream, as is +stdout. Streams are readable, writable, or both. All streams are +instances of [EventEmitter][] + +You can load the Stream base classes by doing `require('stream')`. +There are base classes provided for Readable streams, Writable +streams, Duplex streams, and Transform streams. + +## Compatibility + +In earlier versions of Node, the Readable stream interface was +simpler, but also less powerful and less useful. + +* Rather than waiting for you to call the `read()` method, `'data'` + events would start emitting immediately. If you needed to do some + I/O to decide how to handle data, then you had to store the chunks + in some kind of buffer so that they would not be lost. +* The `pause()` method was advisory, rather than guaranteed. This + meant that you still had to be prepared to receive `'data'` events + even when the stream was in a paused state. + +In Node v0.10, the Readable class described below was added. For +backwards compatibility with older Node programs, Readable streams +switch into "old mode" when a `'data'` event handler is added, or when +the `pause()` or `resume()` methods are called. The effect is that, +even if you are not using the new `read()` method and `'readable'` +event, you no longer have to worry about losing `'data'` chunks. + +Most programs will continue to function normally. However, this +introduces an edge case in the following conditions: + +* No `'data'` event handler is added. +* The `pause()` and `resume()` methods are never called. + +For example, consider the following code: + +```javascript +// WARNING! BROKEN! +net.createServer(function(socket) { + + // we add an 'end' method, but never consume the data + socket.on('end', function() { + // It will never get here. + socket.end('I got your message (but didnt read it)\n'); + }); + +}).listen(1337); +``` + +In versions of node prior to v0.10, the incoming message data would be +simply discarded. However, in Node v0.10 and beyond, the socket will +remain paused forever. + +The workaround in this situation is to call the `resume()` method to +trigger "old mode" behavior: + +```javascript +// Workaround +net.createServer(function(socket) { + + socket.on('end', function() { + socket.end('I got your message (but didnt read it)\n'); + }); + + // start the flow of data, discarding it. + socket.resume(); + +}).listen(1337); +``` + +In addition to new Readable streams switching into old-mode, pre-v0.10 +style streams can be wrapped in a Readable class using the `wrap()` +method. + +## Class: stream.Readable + + + +A `Readable Stream` has the following methods, members, and events. + +Note that `stream.Readable` is an abstract class designed to be +extended with an underlying implementation of the `_read(size)` +method. (See below.) + +### new stream.Readable([options]) + +* `options` {Object} + * `highWaterMark` {Number} The maximum number of bytes to store in + the internal buffer before ceasing to read from the underlying + resource. Default=16kb + * `encoding` {String} If specified, then buffers will be decoded to + strings using the specified encoding. Default=null + * `objectMode` {Boolean} Whether this stream should behave + as a stream of objects. Meaning that stream.read(n) returns + a single value instead of a Buffer of size n + +In classes that extend the Readable class, make sure to call the +constructor so that the buffering settings can be properly +initialized. + +### readable.\_read(size) + +* `size` {Number} Number of bytes to read asynchronously + +Note: **This function should NOT be called directly.** It should be +implemented by child classes, and called by the internal Readable +class methods only. + +All Readable stream implementations must provide a `_read` method +to fetch data from the underlying resource. + +This method is prefixed with an underscore because it is internal to +the class that defines it, and should not be called directly by user +programs. However, you **are** expected to override this method in +your own extension classes. + +When data is available, put it into the read queue by calling +`readable.push(chunk)`. If `push` returns false, then you should stop +reading. When `_read` is called again, you should start pushing more +data. + +The `size` argument is advisory. Implementations where a "read" is a +single call that returns data can use this to know how much data to +fetch. Implementations where that is not relevant, such as TCP or +TLS, may ignore this argument, and simply provide data whenever it +becomes available. There is no need, for example to "wait" until +`size` bytes are available before calling `stream.push(chunk)`. + +### readable.push(chunk) + +* `chunk` {Buffer | null | String} Chunk of data to push into the read queue +* return {Boolean} Whether or not more pushes should be performed + +Note: **This function should be called by Readable implementors, NOT +by consumers of Readable subclasses.** The `_read()` function will not +be called again until at least one `push(chunk)` call is made. If no +data is available, then you MAY call `push('')` (an empty string) to +allow a future `_read` call, without adding any data to the queue. + +The `Readable` class works by putting data into a read queue to be +pulled out later by calling the `read()` method when the `'readable'` +event fires. + +The `push()` method will explicitly insert some data into the read +queue. If it is called with `null` then it will signal the end of the +data. + +In some cases, you may be wrapping a lower-level source which has some +sort of pause/resume mechanism, and a data callback. In those cases, +you could wrap the low-level source object by doing something like +this: + +```javascript +// source is an object with readStop() and readStart() methods, +// and an `ondata` member that gets called when it has data, and +// an `onend` member that gets called when the data is over. + +var stream = new Readable(); + +source.ondata = function(chunk) { + // if push() returns false, then we need to stop reading from source + if (!stream.push(chunk)) + source.readStop(); +}; + +source.onend = function() { + stream.push(null); +}; + +// _read will be called when the stream wants to pull more data in +// the advisory size argument is ignored in this case. +stream._read = function(n) { + source.readStart(); +}; +``` + +### readable.unshift(chunk) + +* `chunk` {Buffer | null | String} Chunk of data to unshift onto the read queue +* return {Boolean} Whether or not more pushes should be performed + +This is the corollary of `readable.push(chunk)`. Rather than putting +the data at the *end* of the read queue, it puts it at the *front* of +the read queue. + +This is useful in certain use-cases where a stream is being consumed +by a parser, which needs to "un-consume" some data that it has +optimistically pulled out of the source. + +```javascript +// A parser for a simple data protocol. +// The "header" is a JSON object, followed by 2 \n characters, and +// then a message body. +// +// Note: This can be done more simply as a Transform stream. See below. + +function SimpleProtocol(source, options) { + if (!(this instanceof SimpleProtocol)) + return new SimpleProtocol(options); + + Readable.call(this, options); + this._inBody = false; + this._sawFirstCr = false; + + // source is a readable stream, such as a socket or file + this._source = source; + + var self = this; + source.on('end', function() { + self.push(null); + }); + + // give it a kick whenever the source is readable + // read(0) will not consume any bytes + source.on('readable', function() { + self.read(0); + }); + + this._rawHeader = []; + this.header = null; +} + +SimpleProtocol.prototype = Object.create( + Readable.prototype, { constructor: { value: SimpleProtocol }}); + +SimpleProtocol.prototype._read = function(n) { + if (!this._inBody) { + var chunk = this._source.read(); + + // if the source doesn't have data, we don't have data yet. + if (chunk === null) + return this.push(''); + + // check if the chunk has a \n\n + var split = -1; + for (var i = 0; i < chunk.length; i++) { + if (chunk[i] === 10) { // '\n' + if (this._sawFirstCr) { + split = i; + break; + } else { + this._sawFirstCr = true; + } + } else { + this._sawFirstCr = false; + } + } + + if (split === -1) { + // still waiting for the \n\n + // stash the chunk, and try again. + this._rawHeader.push(chunk); + this.push(''); + } else { + this._inBody = true; + var h = chunk.slice(0, split); + this._rawHeader.push(h); + var header = Buffer.concat(this._rawHeader).toString(); + try { + this.header = JSON.parse(header); + } catch (er) { + this.emit('error', new Error('invalid simple protocol data')); + return; + } + // now, because we got some extra data, unshift the rest + // back into the read queue so that our consumer will see it. + var b = chunk.slice(split); + this.unshift(b); + + // and let them know that we are done parsing the header. + this.emit('header', this.header); + } + } else { + // from there on, just provide the data to our consumer. + // careful not to push(null), since that would indicate EOF. + var chunk = this._source.read(); + if (chunk) this.push(chunk); + } +}; + +// Usage: +var parser = new SimpleProtocol(source); +// Now parser is a readable stream that will emit 'header' +// with the parsed header data. +``` + +### readable.wrap(stream) + +* `stream` {Stream} An "old style" readable stream + +If you are using an older Node library that emits `'data'` events and +has a `pause()` method that is advisory only, then you can use the +`wrap()` method to create a Readable stream that uses the old stream +as its data source. + +For example: + +```javascript +var OldReader = require('./old-api-module.js').OldReader; +var oreader = new OldReader; +var Readable = require('stream').Readable; +var myReader = new Readable().wrap(oreader); + +myReader.on('readable', function() { + myReader.read(); // etc. +}); +``` + +### Event: 'readable' + +When there is data ready to be consumed, this event will fire. + +When this event emits, call the `read()` method to consume the data. + +### Event: 'end' + +Emitted when the stream has received an EOF (FIN in TCP terminology). +Indicates that no more `'data'` events will happen. If the stream is +also writable, it may be possible to continue writing. + +### Event: 'data' + +The `'data'` event emits either a `Buffer` (by default) or a string if +`setEncoding()` was used. + +Note that adding a `'data'` event listener will switch the Readable +stream into "old mode", where data is emitted as soon as it is +available, rather than waiting for you to call `read()` to consume it. + +### Event: 'error' + +Emitted if there was an error receiving data. + +### Event: 'close' + +Emitted when the underlying resource (for example, the backing file +descriptor) has been closed. Not all streams will emit this. + +### readable.setEncoding(encoding) + +Makes the `'data'` event emit a string instead of a `Buffer`. `encoding` +can be `'utf8'`, `'utf16le'` (`'ucs2'`), `'ascii'`, or `'hex'`. + +The encoding can also be set by specifying an `encoding` field to the +constructor. + +### readable.read([size]) + +* `size` {Number | null} Optional number of bytes to read. +* Return: {Buffer | String | null} + +Note: **This function SHOULD be called by Readable stream users.** + +Call this method to consume data once the `'readable'` event is +emitted. + +The `size` argument will set a minimum number of bytes that you are +interested in. If not set, then the entire content of the internal +buffer is returned. + +If there is no data to consume, or if there are fewer bytes in the +internal buffer than the `size` argument, then `null` is returned, and +a future `'readable'` event will be emitted when more is available. + +Calling `stream.read(0)` will always return `null`, and will trigger a +refresh of the internal buffer, but otherwise be a no-op. + +### readable.pipe(destination, [options]) + +* `destination` {Writable Stream} +* `options` {Object} Optional + * `end` {Boolean} Default=true + +Connects this readable stream to `destination` WriteStream. Incoming +data on this stream gets written to `destination`. Properly manages +back-pressure so that a slow destination will not be overwhelmed by a +fast readable stream. + +This function returns the `destination` stream. + +For example, emulating the Unix `cat` command: + + process.stdin.pipe(process.stdout); + +By default `end()` is called on the destination when the source stream +emits `end`, so that `destination` is no longer writable. Pass `{ end: +false }` as `options` to keep the destination stream open. + +This keeps `writer` open so that "Goodbye" can be written at the +end. + + reader.pipe(writer, { end: false }); + reader.on("end", function() { + writer.end("Goodbye\n"); + }); + +Note that `process.stderr` and `process.stdout` are never closed until +the process exits, regardless of the specified options. + +### readable.unpipe([destination]) + +* `destination` {Writable Stream} Optional + +Undo a previously established `pipe()`. If no destination is +provided, then all previously established pipes are removed. + +### readable.pause() + +Switches the readable stream into "old mode", where data is emitted +using a `'data'` event rather than being buffered for consumption via +the `read()` method. + +Ceases the flow of data. No `'data'` events are emitted while the +stream is in a paused state. + +### readable.resume() + +Switches the readable stream into "old mode", where data is emitted +using a `'data'` event rather than being buffered for consumption via +the `read()` method. + +Resumes the incoming `'data'` events after a `pause()`. + + +## Class: stream.Writable + + + +A `Writable` Stream has the following methods, members, and events. + +Note that `stream.Writable` is an abstract class designed to be +extended with an underlying implementation of the +`_write(chunk, encoding, cb)` method. (See below.) + +### new stream.Writable([options]) + +* `options` {Object} + * `highWaterMark` {Number} Buffer level when `write()` starts + returning false. Default=16kb + * `decodeStrings` {Boolean} Whether or not to decode strings into + Buffers before passing them to `_write()`. Default=true + +In classes that extend the Writable class, make sure to call the +constructor so that the buffering settings can be properly +initialized. + +### writable.\_write(chunk, encoding, callback) + +* `chunk` {Buffer | String} The chunk to be written. Will always + be a buffer unless the `decodeStrings` option was set to `false`. +* `encoding` {String} If the chunk is a string, then this is the + encoding type. Ignore chunk is a buffer. Note that chunk will + **always** be a buffer unless the `decodeStrings` option is + explicitly set to `false`. +* `callback` {Function} Call this function (optionally with an error + argument) when you are done processing the supplied chunk. + +All Writable stream implementations must provide a `_write` method to +send data to the underlying resource. + +Note: **This function MUST NOT be called directly.** It should be +implemented by child classes, and called by the internal Writable +class methods only. + +Call the callback using the standard `callback(error)` pattern to +signal that the write completed successfully or with an error. + +If the `decodeStrings` flag is set in the constructor options, then +`chunk` may be a string rather than a Buffer, and `encoding` will +indicate the sort of string that it is. This is to support +implementations that have an optimized handling for certain string +data encodings. If you do not explicitly set the `decodeStrings` +option to `false`, then you can safely ignore the `encoding` argument, +and assume that `chunk` will always be a Buffer. + +This method is prefixed with an underscore because it is internal to +the class that defines it, and should not be called directly by user +programs. However, you **are** expected to override this method in +your own extension classes. + + +### writable.write(chunk, [encoding], [callback]) + +* `chunk` {Buffer | String} Data to be written +* `encoding` {String} Optional. If `chunk` is a string, then encoding + defaults to `'utf8'` +* `callback` {Function} Optional. Called when this chunk is + successfully written. +* Returns {Boolean} + +Writes `chunk` to the stream. Returns `true` if the data has been +flushed to the underlying resource. Returns `false` to indicate that +the buffer is full, and the data will be sent out in the future. The +`'drain'` event will indicate when the buffer is empty again. + +The specifics of when `write()` will return false, is determined by +the `highWaterMark` option provided to the constructor. + +### writable.end([chunk], [encoding], [callback]) + +* `chunk` {Buffer | String} Optional final data to be written +* `encoding` {String} Optional. If `chunk` is a string, then encoding + defaults to `'utf8'` +* `callback` {Function} Optional. Called when the final chunk is + successfully written. + +Call this method to signal the end of the data being written to the +stream. + +### Event: 'drain' + +Emitted when the stream's write queue empties and it's safe to write +without buffering again. Listen for it when `stream.write()` returns +`false`. + +### Event: 'close' + +Emitted when the underlying resource (for example, the backing file +descriptor) has been closed. Not all streams will emit this. + +### Event: 'finish' + +When `end()` is called and there are no more chunks to write, this +event is emitted. + +### Event: 'pipe' + +* `source` {Readable Stream} + +Emitted when the stream is passed to a readable stream's pipe method. + +### Event 'unpipe' + +* `source` {Readable Stream} + +Emitted when a previously established `pipe()` is removed using the +source Readable stream's `unpipe()` method. + +## Class: stream.Duplex + + + +A "duplex" stream is one that is both Readable and Writable, such as a +TCP socket connection. + +Note that `stream.Duplex` is an abstract class designed to be +extended with an underlying implementation of the `_read(size)` +and `_write(chunk, encoding, callback)` methods as you would with a Readable or +Writable stream class. + +Since JavaScript doesn't have multiple prototypal inheritance, this +class prototypally inherits from Readable, and then parasitically from +Writable. It is thus up to the user to implement both the lowlevel +`_read(n)` method as well as the lowlevel `_write(chunk, encoding, cb)` method +on extension duplex classes. + +### new stream.Duplex(options) + +* `options` {Object} Passed to both Writable and Readable + constructors. Also has the following fields: + * `allowHalfOpen` {Boolean} Default=true. If set to `false`, then + the stream will automatically end the readable side when the + writable side ends and vice versa. + +In classes that extend the Duplex class, make sure to call the +constructor so that the buffering settings can be properly +initialized. + +## Class: stream.Transform + +A "transform" stream is a duplex stream where the output is causally +connected in some way to the input, such as a zlib stream or a crypto +stream. + +There is no requirement that the output be the same size as the input, +the same number of chunks, or arrive at the same time. For example, a +Hash stream will only ever have a single chunk of output which is +provided when the input is ended. A zlib stream will either produce +much smaller or much larger than its input. + +Rather than implement the `_read()` and `_write()` methods, Transform +classes must implement the `_transform()` method, and may optionally +also implement the `_flush()` method. (See below.) + +### new stream.Transform([options]) + +* `options` {Object} Passed to both Writable and Readable + constructors. + +In classes that extend the Transform class, make sure to call the +constructor so that the buffering settings can be properly +initialized. + +### transform.\_transform(chunk, encoding, callback) + +* `chunk` {Buffer | String} The chunk to be transformed. Will always + be a buffer unless the `decodeStrings` option was set to `false`. +* `encoding` {String} If the chunk is a string, then this is the + encoding type. (Ignore if `decodeStrings` chunk is a buffer.) +* `callback` {Function} Call this function (optionally with an error + argument) when you are done processing the supplied chunk. + +Note: **This function MUST NOT be called directly.** It should be +implemented by child classes, and called by the internal Transform +class methods only. + +All Transform stream implementations must provide a `_transform` +method to accept input and produce output. + +`_transform` should do whatever has to be done in this specific +Transform class, to handle the bytes being written, and pass them off +to the readable portion of the interface. Do asynchronous I/O, +process things, and so on. + +Call `transform.push(outputChunk)` 0 or more times to generate output +from this input chunk, depending on how much data you want to output +as a result of this chunk. + +Call the callback function only when the current chunk is completely +consumed. Note that there may or may not be output as a result of any +particular input chunk. + +This method is prefixed with an underscore because it is internal to +the class that defines it, and should not be called directly by user +programs. However, you **are** expected to override this method in +your own extension classes. + +### transform.\_flush(callback) + +* `callback` {Function} Call this function (optionally with an error + argument) when you are done flushing any remaining data. + +Note: **This function MUST NOT be called directly.** It MAY be implemented +by child classes, and if so, will be called by the internal Transform +class methods only. + +In some cases, your transform operation may need to emit a bit more +data at the end of the stream. For example, a `Zlib` compression +stream will store up some internal state so that it can optimally +compress the output. At the end, however, it needs to do the best it +can with what is left, so that the data will be complete. + +In those cases, you can implement a `_flush` method, which will be +called at the very end, after all the written data is consumed, but +before emitting `end` to signal the end of the readable side. Just +like with `_transform`, call `transform.push(chunk)` zero or more +times, as appropriate, and call `callback` when the flush operation is +complete. + +This method is prefixed with an underscore because it is internal to +the class that defines it, and should not be called directly by user +programs. However, you **are** expected to override this method in +your own extension classes. + +### Example: `SimpleProtocol` parser + +The example above of a simple protocol parser can be implemented much +more simply by using the higher level `Transform` stream class. + +In this example, rather than providing the input as an argument, it +would be piped into the parser, which is a more idiomatic Node stream +approach. + +```javascript +function SimpleProtocol(options) { + if (!(this instanceof SimpleProtocol)) + return new SimpleProtocol(options); + + Transform.call(this, options); + this._inBody = false; + this._sawFirstCr = false; + this._rawHeader = []; + this.header = null; +} + +SimpleProtocol.prototype = Object.create( + Transform.prototype, { constructor: { value: SimpleProtocol }}); + +SimpleProtocol.prototype._transform = function(chunk, encoding, done) { + if (!this._inBody) { + // check if the chunk has a \n\n + var split = -1; + for (var i = 0; i < chunk.length; i++) { + if (chunk[i] === 10) { // '\n' + if (this._sawFirstCr) { + split = i; + break; + } else { + this._sawFirstCr = true; + } + } else { + this._sawFirstCr = false; + } + } + + if (split === -1) { + // still waiting for the \n\n + // stash the chunk, and try again. + this._rawHeader.push(chunk); + } else { + this._inBody = true; + var h = chunk.slice(0, split); + this._rawHeader.push(h); + var header = Buffer.concat(this._rawHeader).toString(); + try { + this.header = JSON.parse(header); + } catch (er) { + this.emit('error', new Error('invalid simple protocol data')); + return; + } + // and let them know that we are done parsing the header. + this.emit('header', this.header); + + // now, because we got some extra data, emit this first. + this.push(b); + } + } else { + // from there on, just provide the data to our consumer as-is. + this.push(b); + } + done(); +}; + +var parser = new SimpleProtocol(); +source.pipe(parser) + +// Now parser is a readable stream that will emit 'header' +// with the parsed header data. +``` + + +## Class: stream.PassThrough + +This is a trivial implementation of a `Transform` stream that simply +passes the input bytes across to the output. Its purpose is mainly +for examples and testing, but there are occasionally use cases where +it can come in handy. + + +[EventEmitter]: events.html#events_class_events_eventemitter diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/duplex.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/duplex.js new file mode 100644 index 0000000..ca807af --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/duplex.js @@ -0,0 +1 @@ +module.exports = require("./lib/_stream_duplex.js") diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/examples/CAPSLOCKTYPER.JS b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/examples/CAPSLOCKTYPER.JS new file mode 100644 index 0000000..205a425 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/examples/CAPSLOCKTYPER.JS @@ -0,0 +1,32 @@ +var Transform = require('../transform'); +var inherits = require('util').inherits; + +// subclass +function MyStream () { + Transform.call(this, { + lowWaterMark: 0, + encoding: 'utf8' + }); +} +inherits(MyStream, Transform); + +MyStream.prototype._transform = function (chunk, outputFn, callback) { + outputFn(new Buffer(String(chunk).toUpperCase())); + callback(); +}; + +// use it! +var s = new MyStream(); +process.stdin.resume(); +process.stdin.pipe(s).pipe(process.stdout); +if (process.stdin.setRawMode) + process.stdin.setRawMode(true); +process.stdin.on('data', function (c) { + c = c.toString(); + if (c === '\u0003' || c === '\u0004') { + process.stdin.pause(); + s.end(); + } + if (c === '\r') + process.stdout.write('\n'); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/examples/typer-fsr.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/examples/typer-fsr.js new file mode 100644 index 0000000..7e71584 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/examples/typer-fsr.js @@ -0,0 +1,15 @@ +var fs = require('fs'); +var FSReadable = require('../fs.js'); +var rst = new FSReadable(__filename); + +rst.on('end', function() { + process.stdin.pause(); +}); + +process.stdin.setRawMode(true); +process.stdin.on('data', function() { + var c = rst.read(3); + if (!c) return; + process.stdout.write(c); +}); +process.stdin.resume(); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/examples/typer.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/examples/typer.js new file mode 100644 index 0000000..c16eb6f --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/examples/typer.js @@ -0,0 +1,17 @@ +var fs = require('fs'); +var fst = fs.createReadStream(__filename); +var Readable = require('../readable.js'); +var rst = new Readable(); +rst.wrap(fst); + +rst.on('end', function() { + process.stdin.pause(); +}); + +process.stdin.setRawMode(true); +process.stdin.on('data', function() { + var c = rst.read(3); + if (!c) return setTimeout(process.exit, 500) + process.stdout.write(c); +}); +process.stdin.resume(); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/float.patch b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/float.patch new file mode 100644 index 0000000..b984607 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/float.patch @@ -0,0 +1,923 @@ +diff --git a/lib/_stream_duplex.js b/lib/_stream_duplex.js +index c5a741c..a2e0d8e 100644 +--- a/lib/_stream_duplex.js ++++ b/lib/_stream_duplex.js +@@ -26,8 +26,8 @@ + + module.exports = Duplex; + var util = require('util'); +-var Readable = require('_stream_readable'); +-var Writable = require('_stream_writable'); ++var Readable = require('./_stream_readable'); ++var Writable = require('./_stream_writable'); + + util.inherits(Duplex, Readable); + +diff --git a/lib/_stream_passthrough.js b/lib/_stream_passthrough.js +index a5e9864..330c247 100644 +--- a/lib/_stream_passthrough.js ++++ b/lib/_stream_passthrough.js +@@ -25,7 +25,7 @@ + + module.exports = PassThrough; + +-var Transform = require('_stream_transform'); ++var Transform = require('./_stream_transform'); + var util = require('util'); + util.inherits(PassThrough, Transform); + +diff --git a/lib/_stream_readable.js b/lib/_stream_readable.js +index 0c3fe3e..90a8298 100644 +--- a/lib/_stream_readable.js ++++ b/lib/_stream_readable.js +@@ -23,10 +23,34 @@ module.exports = Readable; + Readable.ReadableState = ReadableState; + + var EE = require('events').EventEmitter; ++if (!EE.listenerCount) EE.listenerCount = function(emitter, type) { ++ return emitter.listeners(type).length; ++}; ++ ++if (!global.setImmediate) global.setImmediate = function setImmediate(fn) { ++ return setTimeout(fn, 0); ++}; ++if (!global.clearImmediate) global.clearImmediate = function clearImmediate(i) { ++ return clearTimeout(i); ++}; ++ + var Stream = require('stream'); + var util = require('util'); ++if (!util.isUndefined) { ++ var utilIs = require('core-util-is'); ++ for (var f in utilIs) { ++ util[f] = utilIs[f]; ++ } ++} + var StringDecoder; +-var debug = util.debuglog('stream'); ++var debug; ++if (util.debuglog) ++ debug = util.debuglog('stream'); ++else try { ++ debug = require('debuglog')('stream'); ++} catch (er) { ++ debug = function() {}; ++} + + util.inherits(Readable, Stream); + +@@ -380,7 +404,7 @@ function chunkInvalid(state, chunk) { + + + function onEofChunk(stream, state) { +- if (state.decoder && !state.ended) { ++ if (state.decoder && !state.ended && state.decoder.end) { + var chunk = state.decoder.end(); + if (chunk && chunk.length) { + state.buffer.push(chunk); +diff --git a/lib/_stream_transform.js b/lib/_stream_transform.js +index b1f9fcc..b0caf57 100644 +--- a/lib/_stream_transform.js ++++ b/lib/_stream_transform.js +@@ -64,8 +64,14 @@ + + module.exports = Transform; + +-var Duplex = require('_stream_duplex'); ++var Duplex = require('./_stream_duplex'); + var util = require('util'); ++if (!util.isUndefined) { ++ var utilIs = require('core-util-is'); ++ for (var f in utilIs) { ++ util[f] = utilIs[f]; ++ } ++} + util.inherits(Transform, Duplex); + + +diff --git a/lib/_stream_writable.js b/lib/_stream_writable.js +index ba2e920..f49288b 100644 +--- a/lib/_stream_writable.js ++++ b/lib/_stream_writable.js +@@ -27,6 +27,12 @@ module.exports = Writable; + Writable.WritableState = WritableState; + + var util = require('util'); ++if (!util.isUndefined) { ++ var utilIs = require('core-util-is'); ++ for (var f in utilIs) { ++ util[f] = utilIs[f]; ++ } ++} + var Stream = require('stream'); + + util.inherits(Writable, Stream); +@@ -119,7 +125,7 @@ function WritableState(options, stream) { + function Writable(options) { + // Writable ctor is applied to Duplexes, though they're not + // instanceof Writable, they're instanceof Readable. +- if (!(this instanceof Writable) && !(this instanceof Stream.Duplex)) ++ if (!(this instanceof Writable) && !(this instanceof require('./_stream_duplex'))) + return new Writable(options); + + this._writableState = new WritableState(options, this); +diff --git a/test/simple/test-stream-big-push.js b/test/simple/test-stream-big-push.js +index e3787e4..8cd2127 100644 +--- a/test/simple/test-stream-big-push.js ++++ b/test/simple/test-stream-big-push.js +@@ -21,7 +21,7 @@ + + var common = require('../common'); + var assert = require('assert'); +-var stream = require('stream'); ++var stream = require('../../'); + var str = 'asdfasdfasdfasdfasdf'; + + var r = new stream.Readable({ +diff --git a/test/simple/test-stream-end-paused.js b/test/simple/test-stream-end-paused.js +index bb73777..d40efc7 100644 +--- a/test/simple/test-stream-end-paused.js ++++ b/test/simple/test-stream-end-paused.js +@@ -25,7 +25,7 @@ var gotEnd = false; + + // Make sure we don't miss the end event for paused 0-length streams + +-var Readable = require('stream').Readable; ++var Readable = require('../../').Readable; + var stream = new Readable(); + var calledRead = false; + stream._read = function() { +diff --git a/test/simple/test-stream-pipe-after-end.js b/test/simple/test-stream-pipe-after-end.js +index b46ee90..0be8366 100644 +--- a/test/simple/test-stream-pipe-after-end.js ++++ b/test/simple/test-stream-pipe-after-end.js +@@ -22,8 +22,8 @@ + var common = require('../common'); + var assert = require('assert'); + +-var Readable = require('_stream_readable'); +-var Writable = require('_stream_writable'); ++var Readable = require('../../lib/_stream_readable'); ++var Writable = require('../../lib/_stream_writable'); + var util = require('util'); + + util.inherits(TestReadable, Readable); +diff --git a/test/simple/test-stream-pipe-cleanup.js b/test/simple/test-stream-pipe-cleanup.js +deleted file mode 100644 +index f689358..0000000 +--- a/test/simple/test-stream-pipe-cleanup.js ++++ /dev/null +@@ -1,122 +0,0 @@ +-// Copyright Joyent, Inc. and other Node contributors. +-// +-// Permission is hereby granted, free of charge, to any person obtaining a +-// copy of this software and associated documentation files (the +-// "Software"), to deal in the Software without restriction, including +-// without limitation the rights to use, copy, modify, merge, publish, +-// distribute, sublicense, and/or sell copies of the Software, and to permit +-// persons to whom the Software is furnished to do so, subject to the +-// following conditions: +-// +-// The above copyright notice and this permission notice shall be included +-// in all copies or substantial portions of the Software. +-// +-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +-// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +-// USE OR OTHER DEALINGS IN THE SOFTWARE. +- +-// This test asserts that Stream.prototype.pipe does not leave listeners +-// hanging on the source or dest. +- +-var common = require('../common'); +-var stream = require('stream'); +-var assert = require('assert'); +-var util = require('util'); +- +-function Writable() { +- this.writable = true; +- this.endCalls = 0; +- stream.Stream.call(this); +-} +-util.inherits(Writable, stream.Stream); +-Writable.prototype.end = function() { +- this.endCalls++; +-}; +- +-Writable.prototype.destroy = function() { +- this.endCalls++; +-}; +- +-function Readable() { +- this.readable = true; +- stream.Stream.call(this); +-} +-util.inherits(Readable, stream.Stream); +- +-function Duplex() { +- this.readable = true; +- Writable.call(this); +-} +-util.inherits(Duplex, Writable); +- +-var i = 0; +-var limit = 100; +- +-var w = new Writable(); +- +-var r; +- +-for (i = 0; i < limit; i++) { +- r = new Readable(); +- r.pipe(w); +- r.emit('end'); +-} +-assert.equal(0, r.listeners('end').length); +-assert.equal(limit, w.endCalls); +- +-w.endCalls = 0; +- +-for (i = 0; i < limit; i++) { +- r = new Readable(); +- r.pipe(w); +- r.emit('close'); +-} +-assert.equal(0, r.listeners('close').length); +-assert.equal(limit, w.endCalls); +- +-w.endCalls = 0; +- +-r = new Readable(); +- +-for (i = 0; i < limit; i++) { +- w = new Writable(); +- r.pipe(w); +- w.emit('close'); +-} +-assert.equal(0, w.listeners('close').length); +- +-r = new Readable(); +-w = new Writable(); +-var d = new Duplex(); +-r.pipe(d); // pipeline A +-d.pipe(w); // pipeline B +-assert.equal(r.listeners('end').length, 2); // A.onend, A.cleanup +-assert.equal(r.listeners('close').length, 2); // A.onclose, A.cleanup +-assert.equal(d.listeners('end').length, 2); // B.onend, B.cleanup +-assert.equal(d.listeners('close').length, 3); // A.cleanup, B.onclose, B.cleanup +-assert.equal(w.listeners('end').length, 0); +-assert.equal(w.listeners('close').length, 1); // B.cleanup +- +-r.emit('end'); +-assert.equal(d.endCalls, 1); +-assert.equal(w.endCalls, 0); +-assert.equal(r.listeners('end').length, 0); +-assert.equal(r.listeners('close').length, 0); +-assert.equal(d.listeners('end').length, 2); // B.onend, B.cleanup +-assert.equal(d.listeners('close').length, 2); // B.onclose, B.cleanup +-assert.equal(w.listeners('end').length, 0); +-assert.equal(w.listeners('close').length, 1); // B.cleanup +- +-d.emit('end'); +-assert.equal(d.endCalls, 1); +-assert.equal(w.endCalls, 1); +-assert.equal(r.listeners('end').length, 0); +-assert.equal(r.listeners('close').length, 0); +-assert.equal(d.listeners('end').length, 0); +-assert.equal(d.listeners('close').length, 0); +-assert.equal(w.listeners('end').length, 0); +-assert.equal(w.listeners('close').length, 0); +diff --git a/test/simple/test-stream-pipe-error-handling.js b/test/simple/test-stream-pipe-error-handling.js +index c5d724b..c7d6b7d 100644 +--- a/test/simple/test-stream-pipe-error-handling.js ++++ b/test/simple/test-stream-pipe-error-handling.js +@@ -21,7 +21,7 @@ + + var common = require('../common'); + var assert = require('assert'); +-var Stream = require('stream').Stream; ++var Stream = require('../../').Stream; + + (function testErrorListenerCatches() { + var source = new Stream(); +diff --git a/test/simple/test-stream-pipe-event.js b/test/simple/test-stream-pipe-event.js +index cb9d5fe..56f8d61 100644 +--- a/test/simple/test-stream-pipe-event.js ++++ b/test/simple/test-stream-pipe-event.js +@@ -20,7 +20,7 @@ + // USE OR OTHER DEALINGS IN THE SOFTWARE. + + var common = require('../common'); +-var stream = require('stream'); ++var stream = require('../../'); + var assert = require('assert'); + var util = require('util'); + +diff --git a/test/simple/test-stream-push-order.js b/test/simple/test-stream-push-order.js +index f2e6ec2..a5c9bf9 100644 +--- a/test/simple/test-stream-push-order.js ++++ b/test/simple/test-stream-push-order.js +@@ -20,7 +20,7 @@ + // USE OR OTHER DEALINGS IN THE SOFTWARE. + + var common = require('../common.js'); +-var Readable = require('stream').Readable; ++var Readable = require('../../').Readable; + var assert = require('assert'); + + var s = new Readable({ +diff --git a/test/simple/test-stream-push-strings.js b/test/simple/test-stream-push-strings.js +index 06f43dc..1701a9a 100644 +--- a/test/simple/test-stream-push-strings.js ++++ b/test/simple/test-stream-push-strings.js +@@ -22,7 +22,7 @@ + var common = require('../common'); + var assert = require('assert'); + +-var Readable = require('stream').Readable; ++var Readable = require('../../').Readable; + var util = require('util'); + + util.inherits(MyStream, Readable); +diff --git a/test/simple/test-stream-readable-event.js b/test/simple/test-stream-readable-event.js +index ba6a577..a8e6f7b 100644 +--- a/test/simple/test-stream-readable-event.js ++++ b/test/simple/test-stream-readable-event.js +@@ -22,7 +22,7 @@ + var common = require('../common'); + var assert = require('assert'); + +-var Readable = require('stream').Readable; ++var Readable = require('../../').Readable; + + (function first() { + // First test, not reading when the readable is added. +diff --git a/test/simple/test-stream-readable-flow-recursion.js b/test/simple/test-stream-readable-flow-recursion.js +index 2891ad6..11689ba 100644 +--- a/test/simple/test-stream-readable-flow-recursion.js ++++ b/test/simple/test-stream-readable-flow-recursion.js +@@ -27,7 +27,7 @@ var assert = require('assert'); + // more data continuously, but without triggering a nextTick + // warning or RangeError. + +-var Readable = require('stream').Readable; ++var Readable = require('../../').Readable; + + // throw an error if we trigger a nextTick warning. + process.throwDeprecation = true; +diff --git a/test/simple/test-stream-unshift-empty-chunk.js b/test/simple/test-stream-unshift-empty-chunk.js +index 0c96476..7827538 100644 +--- a/test/simple/test-stream-unshift-empty-chunk.js ++++ b/test/simple/test-stream-unshift-empty-chunk.js +@@ -24,7 +24,7 @@ var assert = require('assert'); + + // This test verifies that stream.unshift(Buffer(0)) or + // stream.unshift('') does not set state.reading=false. +-var Readable = require('stream').Readable; ++var Readable = require('../../').Readable; + + var r = new Readable(); + var nChunks = 10; +diff --git a/test/simple/test-stream-unshift-read-race.js b/test/simple/test-stream-unshift-read-race.js +index 83fd9fa..17c18aa 100644 +--- a/test/simple/test-stream-unshift-read-race.js ++++ b/test/simple/test-stream-unshift-read-race.js +@@ -29,7 +29,7 @@ var assert = require('assert'); + // 3. push() after the EOF signaling null is an error. + // 4. _read() is not called after pushing the EOF null chunk. + +-var stream = require('stream'); ++var stream = require('../../'); + var hwm = 10; + var r = stream.Readable({ highWaterMark: hwm }); + var chunks = 10; +@@ -51,7 +51,14 @@ r._read = function(n) { + + function push(fast) { + assert(!pushedNull, 'push() after null push'); +- var c = pos >= data.length ? null : data.slice(pos, pos + n); ++ var c; ++ if (pos >= data.length) ++ c = null; ++ else { ++ if (n + pos > data.length) ++ n = data.length - pos; ++ c = data.slice(pos, pos + n); ++ } + pushedNull = c === null; + if (fast) { + pos += n; +diff --git a/test/simple/test-stream-writev.js b/test/simple/test-stream-writev.js +index 5b49e6e..b5321f3 100644 +--- a/test/simple/test-stream-writev.js ++++ b/test/simple/test-stream-writev.js +@@ -22,7 +22,7 @@ + var common = require('../common'); + var assert = require('assert'); + +-var stream = require('stream'); ++var stream = require('../../'); + + var queue = []; + for (var decode = 0; decode < 2; decode++) { +diff --git a/test/simple/test-stream2-basic.js b/test/simple/test-stream2-basic.js +index 3814bf0..248c1be 100644 +--- a/test/simple/test-stream2-basic.js ++++ b/test/simple/test-stream2-basic.js +@@ -21,7 +21,7 @@ + + + var common = require('../common.js'); +-var R = require('_stream_readable'); ++var R = require('../../lib/_stream_readable'); + var assert = require('assert'); + + var util = require('util'); +diff --git a/test/simple/test-stream2-compatibility.js b/test/simple/test-stream2-compatibility.js +index 6cdd4e9..f0fa84b 100644 +--- a/test/simple/test-stream2-compatibility.js ++++ b/test/simple/test-stream2-compatibility.js +@@ -21,7 +21,7 @@ + + + var common = require('../common.js'); +-var R = require('_stream_readable'); ++var R = require('../../lib/_stream_readable'); + var assert = require('assert'); + + var util = require('util'); +diff --git a/test/simple/test-stream2-finish-pipe.js b/test/simple/test-stream2-finish-pipe.js +index 39b274f..006a19b 100644 +--- a/test/simple/test-stream2-finish-pipe.js ++++ b/test/simple/test-stream2-finish-pipe.js +@@ -20,7 +20,7 @@ + // USE OR OTHER DEALINGS IN THE SOFTWARE. + + var common = require('../common.js'); +-var stream = require('stream'); ++var stream = require('../../'); + var Buffer = require('buffer').Buffer; + + var r = new stream.Readable(); +diff --git a/test/simple/test-stream2-fs.js b/test/simple/test-stream2-fs.js +deleted file mode 100644 +index e162406..0000000 +--- a/test/simple/test-stream2-fs.js ++++ /dev/null +@@ -1,72 +0,0 @@ +-// Copyright Joyent, Inc. and other Node contributors. +-// +-// Permission is hereby granted, free of charge, to any person obtaining a +-// copy of this software and associated documentation files (the +-// "Software"), to deal in the Software without restriction, including +-// without limitation the rights to use, copy, modify, merge, publish, +-// distribute, sublicense, and/or sell copies of the Software, and to permit +-// persons to whom the Software is furnished to do so, subject to the +-// following conditions: +-// +-// The above copyright notice and this permission notice shall be included +-// in all copies or substantial portions of the Software. +-// +-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +-// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +-// USE OR OTHER DEALINGS IN THE SOFTWARE. +- +- +-var common = require('../common.js'); +-var R = require('_stream_readable'); +-var assert = require('assert'); +- +-var fs = require('fs'); +-var FSReadable = fs.ReadStream; +- +-var path = require('path'); +-var file = path.resolve(common.fixturesDir, 'x1024.txt'); +- +-var size = fs.statSync(file).size; +- +-var expectLengths = [1024]; +- +-var util = require('util'); +-var Stream = require('stream'); +- +-util.inherits(TestWriter, Stream); +- +-function TestWriter() { +- Stream.apply(this); +- this.buffer = []; +- this.length = 0; +-} +- +-TestWriter.prototype.write = function(c) { +- this.buffer.push(c.toString()); +- this.length += c.length; +- return true; +-}; +- +-TestWriter.prototype.end = function(c) { +- if (c) this.buffer.push(c.toString()); +- this.emit('results', this.buffer); +-} +- +-var r = new FSReadable(file); +-var w = new TestWriter(); +- +-w.on('results', function(res) { +- console.error(res, w.length); +- assert.equal(w.length, size); +- var l = 0; +- assert.deepEqual(res.map(function (c) { +- return c.length; +- }), expectLengths); +- console.log('ok'); +-}); +- +-r.pipe(w); +diff --git a/test/simple/test-stream2-httpclient-response-end.js b/test/simple/test-stream2-httpclient-response-end.js +deleted file mode 100644 +index 15cffc2..0000000 +--- a/test/simple/test-stream2-httpclient-response-end.js ++++ /dev/null +@@ -1,52 +0,0 @@ +-// Copyright Joyent, Inc. and other Node contributors. +-// +-// Permission is hereby granted, free of charge, to any person obtaining a +-// copy of this software and associated documentation files (the +-// "Software"), to deal in the Software without restriction, including +-// without limitation the rights to use, copy, modify, merge, publish, +-// distribute, sublicense, and/or sell copies of the Software, and to permit +-// persons to whom the Software is furnished to do so, subject to the +-// following conditions: +-// +-// The above copyright notice and this permission notice shall be included +-// in all copies or substantial portions of the Software. +-// +-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +-// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +-// USE OR OTHER DEALINGS IN THE SOFTWARE. +- +-var common = require('../common.js'); +-var assert = require('assert'); +-var http = require('http'); +-var msg = 'Hello'; +-var readable_event = false; +-var end_event = false; +-var server = http.createServer(function(req, res) { +- res.writeHead(200, {'Content-Type': 'text/plain'}); +- res.end(msg); +-}).listen(common.PORT, function() { +- http.get({port: common.PORT}, function(res) { +- var data = ''; +- res.on('readable', function() { +- console.log('readable event'); +- readable_event = true; +- data += res.read(); +- }); +- res.on('end', function() { +- console.log('end event'); +- end_event = true; +- assert.strictEqual(msg, data); +- server.close(); +- }); +- }); +-}); +- +-process.on('exit', function() { +- assert(readable_event); +- assert(end_event); +-}); +- +diff --git a/test/simple/test-stream2-large-read-stall.js b/test/simple/test-stream2-large-read-stall.js +index 2fbfbca..667985b 100644 +--- a/test/simple/test-stream2-large-read-stall.js ++++ b/test/simple/test-stream2-large-read-stall.js +@@ -30,7 +30,7 @@ var PUSHSIZE = 20; + var PUSHCOUNT = 1000; + var HWM = 50; + +-var Readable = require('stream').Readable; ++var Readable = require('../../').Readable; + var r = new Readable({ + highWaterMark: HWM + }); +@@ -39,23 +39,23 @@ var rs = r._readableState; + r._read = push; + + r.on('readable', function() { +- console.error('>> readable'); ++ //console.error('>> readable'); + do { +- console.error(' > read(%d)', READSIZE); ++ //console.error(' > read(%d)', READSIZE); + var ret = r.read(READSIZE); +- console.error(' < %j (%d remain)', ret && ret.length, rs.length); ++ //console.error(' < %j (%d remain)', ret && ret.length, rs.length); + } while (ret && ret.length === READSIZE); + +- console.error('<< after read()', +- ret && ret.length, +- rs.needReadable, +- rs.length); ++ //console.error('<< after read()', ++ // ret && ret.length, ++ // rs.needReadable, ++ // rs.length); + }); + + var endEmitted = false; + r.on('end', function() { + endEmitted = true; +- console.error('end'); ++ //console.error('end'); + }); + + var pushes = 0; +@@ -64,11 +64,11 @@ function push() { + return; + + if (pushes++ === PUSHCOUNT) { +- console.error(' push(EOF)'); ++ //console.error(' push(EOF)'); + return r.push(null); + } + +- console.error(' push #%d', pushes); ++ //console.error(' push #%d', pushes); + if (r.push(new Buffer(PUSHSIZE))) + setTimeout(push); + } +diff --git a/test/simple/test-stream2-objects.js b/test/simple/test-stream2-objects.js +index 3e6931d..ff47d89 100644 +--- a/test/simple/test-stream2-objects.js ++++ b/test/simple/test-stream2-objects.js +@@ -21,8 +21,8 @@ + + + var common = require('../common.js'); +-var Readable = require('_stream_readable'); +-var Writable = require('_stream_writable'); ++var Readable = require('../../lib/_stream_readable'); ++var Writable = require('../../lib/_stream_writable'); + var assert = require('assert'); + + // tiny node-tap lookalike. +diff --git a/test/simple/test-stream2-pipe-error-handling.js b/test/simple/test-stream2-pipe-error-handling.js +index cf7531c..e3f3e4e 100644 +--- a/test/simple/test-stream2-pipe-error-handling.js ++++ b/test/simple/test-stream2-pipe-error-handling.js +@@ -21,7 +21,7 @@ + + var common = require('../common'); + var assert = require('assert'); +-var stream = require('stream'); ++var stream = require('../../'); + + (function testErrorListenerCatches() { + var count = 1000; +diff --git a/test/simple/test-stream2-pipe-error-once-listener.js b/test/simple/test-stream2-pipe-error-once-listener.js +index 5e8e3cb..53b2616 100755 +--- a/test/simple/test-stream2-pipe-error-once-listener.js ++++ b/test/simple/test-stream2-pipe-error-once-listener.js +@@ -24,7 +24,7 @@ var common = require('../common.js'); + var assert = require('assert'); + + var util = require('util'); +-var stream = require('stream'); ++var stream = require('../../'); + + + var Read = function() { +diff --git a/test/simple/test-stream2-push.js b/test/simple/test-stream2-push.js +index b63edc3..eb2b0e9 100644 +--- a/test/simple/test-stream2-push.js ++++ b/test/simple/test-stream2-push.js +@@ -20,7 +20,7 @@ + // USE OR OTHER DEALINGS IN THE SOFTWARE. + + var common = require('../common.js'); +-var stream = require('stream'); ++var stream = require('../../'); + var Readable = stream.Readable; + var Writable = stream.Writable; + var assert = require('assert'); +diff --git a/test/simple/test-stream2-read-sync-stack.js b/test/simple/test-stream2-read-sync-stack.js +index e8a7305..9740a47 100644 +--- a/test/simple/test-stream2-read-sync-stack.js ++++ b/test/simple/test-stream2-read-sync-stack.js +@@ -21,7 +21,7 @@ + + var common = require('../common'); + var assert = require('assert'); +-var Readable = require('stream').Readable; ++var Readable = require('../../').Readable; + var r = new Readable(); + var N = 256 * 1024; + +diff --git a/test/simple/test-stream2-readable-empty-buffer-no-eof.js b/test/simple/test-stream2-readable-empty-buffer-no-eof.js +index cd30178..4b1659d 100644 +--- a/test/simple/test-stream2-readable-empty-buffer-no-eof.js ++++ b/test/simple/test-stream2-readable-empty-buffer-no-eof.js +@@ -22,10 +22,9 @@ + var common = require('../common'); + var assert = require('assert'); + +-var Readable = require('stream').Readable; ++var Readable = require('../../').Readable; + + test1(); +-test2(); + + function test1() { + var r = new Readable(); +@@ -88,31 +87,3 @@ function test1() { + console.log('ok'); + }); + } +- +-function test2() { +- var r = new Readable({ encoding: 'base64' }); +- var reads = 5; +- r._read = function(n) { +- if (!reads--) +- return r.push(null); // EOF +- else +- return r.push(new Buffer('x')); +- }; +- +- var results = []; +- function flow() { +- var chunk; +- while (null !== (chunk = r.read())) +- results.push(chunk + ''); +- } +- r.on('readable', flow); +- r.on('end', function() { +- results.push('EOF'); +- }); +- flow(); +- +- process.on('exit', function() { +- assert.deepEqual(results, [ 'eHh4', 'eHg=', 'EOF' ]); +- console.log('ok'); +- }); +-} +diff --git a/test/simple/test-stream2-readable-from-list.js b/test/simple/test-stream2-readable-from-list.js +index 7c96ffe..04a96f5 100644 +--- a/test/simple/test-stream2-readable-from-list.js ++++ b/test/simple/test-stream2-readable-from-list.js +@@ -21,7 +21,7 @@ + + var assert = require('assert'); + var common = require('../common.js'); +-var fromList = require('_stream_readable')._fromList; ++var fromList = require('../../lib/_stream_readable')._fromList; + + // tiny node-tap lookalike. + var tests = []; +diff --git a/test/simple/test-stream2-readable-legacy-drain.js b/test/simple/test-stream2-readable-legacy-drain.js +index 675da8e..51fd3d5 100644 +--- a/test/simple/test-stream2-readable-legacy-drain.js ++++ b/test/simple/test-stream2-readable-legacy-drain.js +@@ -22,7 +22,7 @@ + var common = require('../common'); + var assert = require('assert'); + +-var Stream = require('stream'); ++var Stream = require('../../'); + var Readable = Stream.Readable; + + var r = new Readable(); +diff --git a/test/simple/test-stream2-readable-non-empty-end.js b/test/simple/test-stream2-readable-non-empty-end.js +index 7314ae7..c971898 100644 +--- a/test/simple/test-stream2-readable-non-empty-end.js ++++ b/test/simple/test-stream2-readable-non-empty-end.js +@@ -21,7 +21,7 @@ + + var assert = require('assert'); + var common = require('../common.js'); +-var Readable = require('_stream_readable'); ++var Readable = require('../../lib/_stream_readable'); + + var len = 0; + var chunks = new Array(10); +diff --git a/test/simple/test-stream2-readable-wrap-empty.js b/test/simple/test-stream2-readable-wrap-empty.js +index 2e5cf25..fd8a3dc 100644 +--- a/test/simple/test-stream2-readable-wrap-empty.js ++++ b/test/simple/test-stream2-readable-wrap-empty.js +@@ -22,7 +22,7 @@ + var common = require('../common'); + var assert = require('assert'); + +-var Readable = require('_stream_readable'); ++var Readable = require('../../lib/_stream_readable'); + var EE = require('events').EventEmitter; + + var oldStream = new EE(); +diff --git a/test/simple/test-stream2-readable-wrap.js b/test/simple/test-stream2-readable-wrap.js +index 90eea01..6b177f7 100644 +--- a/test/simple/test-stream2-readable-wrap.js ++++ b/test/simple/test-stream2-readable-wrap.js +@@ -22,8 +22,8 @@ + var common = require('../common'); + var assert = require('assert'); + +-var Readable = require('_stream_readable'); +-var Writable = require('_stream_writable'); ++var Readable = require('../../lib/_stream_readable'); ++var Writable = require('../../lib/_stream_writable'); + var EE = require('events').EventEmitter; + + var testRuns = 0, completedRuns = 0; +diff --git a/test/simple/test-stream2-set-encoding.js b/test/simple/test-stream2-set-encoding.js +index 5d2c32a..685531b 100644 +--- a/test/simple/test-stream2-set-encoding.js ++++ b/test/simple/test-stream2-set-encoding.js +@@ -22,7 +22,7 @@ + + var common = require('../common.js'); + var assert = require('assert'); +-var R = require('_stream_readable'); ++var R = require('../../lib/_stream_readable'); + var util = require('util'); + + // tiny node-tap lookalike. +diff --git a/test/simple/test-stream2-transform.js b/test/simple/test-stream2-transform.js +index 9c9ddd8..a0cacc6 100644 +--- a/test/simple/test-stream2-transform.js ++++ b/test/simple/test-stream2-transform.js +@@ -21,8 +21,8 @@ + + var assert = require('assert'); + var common = require('../common.js'); +-var PassThrough = require('_stream_passthrough'); +-var Transform = require('_stream_transform'); ++var PassThrough = require('../../').PassThrough; ++var Transform = require('../../').Transform; + + // tiny node-tap lookalike. + var tests = []; +diff --git a/test/simple/test-stream2-unpipe-drain.js b/test/simple/test-stream2-unpipe-drain.js +index d66dc3c..365b327 100644 +--- a/test/simple/test-stream2-unpipe-drain.js ++++ b/test/simple/test-stream2-unpipe-drain.js +@@ -22,7 +22,7 @@ + + var common = require('../common.js'); + var assert = require('assert'); +-var stream = require('stream'); ++var stream = require('../../'); + var crypto = require('crypto'); + + var util = require('util'); +diff --git a/test/simple/test-stream2-unpipe-leak.js b/test/simple/test-stream2-unpipe-leak.js +index 99f8746..17c92ae 100644 +--- a/test/simple/test-stream2-unpipe-leak.js ++++ b/test/simple/test-stream2-unpipe-leak.js +@@ -22,7 +22,7 @@ + + var common = require('../common.js'); + var assert = require('assert'); +-var stream = require('stream'); ++var stream = require('../../'); + + var chunk = new Buffer('hallo'); + +diff --git a/test/simple/test-stream2-writable.js b/test/simple/test-stream2-writable.js +index 704100c..209c3a6 100644 +--- a/test/simple/test-stream2-writable.js ++++ b/test/simple/test-stream2-writable.js +@@ -20,8 +20,8 @@ + // USE OR OTHER DEALINGS IN THE SOFTWARE. + + var common = require('../common.js'); +-var W = require('_stream_writable'); +-var D = require('_stream_duplex'); ++var W = require('../../').Writable; ++var D = require('../../').Duplex; + var assert = require('assert'); + + var util = require('util'); +diff --git a/test/simple/test-stream3-pause-then-read.js b/test/simple/test-stream3-pause-then-read.js +index b91bde3..2f72c15 100644 +--- a/test/simple/test-stream3-pause-then-read.js ++++ b/test/simple/test-stream3-pause-then-read.js +@@ -22,7 +22,7 @@ + var common = require('../common'); + var assert = require('assert'); + +-var stream = require('stream'); ++var stream = require('../../'); + var Readable = stream.Readable; + var Writable = stream.Writable; + diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/fs.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/fs.js new file mode 100644 index 0000000..a663af8 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/fs.js @@ -0,0 +1,1705 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// Maintainers, keep in mind that octal literals are not allowed +// in strict mode. Use the decimal value and add a comment with +// the octal value. Example: +// +// var mode = 438; /* mode=0666 */ + +var util = require('util'); +var pathModule = require('path'); + +var binding = process.binding('fs'); +var constants = process.binding('constants'); +var fs = exports; +var Stream = require('stream').Stream; +var EventEmitter = require('events').EventEmitter; + +var Readable = require('./lib/_stream_readable.js'); +var Writable = require('./lib/_stream_writable.js'); + +var kMinPoolSpace = 128; +var kPoolSize = 40 * 1024; + +var O_APPEND = constants.O_APPEND || 0; +var O_CREAT = constants.O_CREAT || 0; +var O_DIRECTORY = constants.O_DIRECTORY || 0; +var O_EXCL = constants.O_EXCL || 0; +var O_NOCTTY = constants.O_NOCTTY || 0; +var O_NOFOLLOW = constants.O_NOFOLLOW || 0; +var O_RDONLY = constants.O_RDONLY || 0; +var O_RDWR = constants.O_RDWR || 0; +var O_SYMLINK = constants.O_SYMLINK || 0; +var O_SYNC = constants.O_SYNC || 0; +var O_TRUNC = constants.O_TRUNC || 0; +var O_WRONLY = constants.O_WRONLY || 0; + +var isWindows = process.platform === 'win32'; + +var DEBUG = process.env.NODE_DEBUG && /fs/.test(process.env.NODE_DEBUG); + +function rethrow() { + // Only enable in debug mode. A backtrace uses ~1000 bytes of heap space and + // is fairly slow to generate. + if (DEBUG) { + var backtrace = new Error; + return function(err) { + if (err) { + backtrace.message = err.message; + err = backtrace; + throw err; + } + }; + } + + return function(err) { + if (err) { + throw err; // Forgot a callback but don't know where? Use NODE_DEBUG=fs + } + }; +} + +function maybeCallback(cb) { + return typeof cb === 'function' ? cb : rethrow(); +} + +// Ensure that callbacks run in the global context. Only use this function +// for callbacks that are passed to the binding layer, callbacks that are +// invoked from JS already run in the proper scope. +function makeCallback(cb) { + if (typeof cb !== 'function') { + return rethrow(); + } + + return function() { + return cb.apply(null, arguments); + }; +} + +function assertEncoding(encoding) { + if (encoding && !Buffer.isEncoding(encoding)) { + throw new Error('Unknown encoding: ' + encoding); + } +} + +function nullCheck(path, callback) { + if (('' + path).indexOf('\u0000') !== -1) { + var er = new Error('Path must be a string without null bytes.'); + if (!callback) + throw er; + process.nextTick(function() { + callback(er); + }); + return false; + } + return true; +} + +fs.Stats = binding.Stats; + +fs.Stats.prototype._checkModeProperty = function(property) { + return ((this.mode & constants.S_IFMT) === property); +}; + +fs.Stats.prototype.isDirectory = function() { + return this._checkModeProperty(constants.S_IFDIR); +}; + +fs.Stats.prototype.isFile = function() { + return this._checkModeProperty(constants.S_IFREG); +}; + +fs.Stats.prototype.isBlockDevice = function() { + return this._checkModeProperty(constants.S_IFBLK); +}; + +fs.Stats.prototype.isCharacterDevice = function() { + return this._checkModeProperty(constants.S_IFCHR); +}; + +fs.Stats.prototype.isSymbolicLink = function() { + return this._checkModeProperty(constants.S_IFLNK); +}; + +fs.Stats.prototype.isFIFO = function() { + return this._checkModeProperty(constants.S_IFIFO); +}; + +fs.Stats.prototype.isSocket = function() { + return this._checkModeProperty(constants.S_IFSOCK); +}; + +fs.exists = function(path, callback) { + if (!nullCheck(path, cb)) return; + binding.stat(pathModule._makeLong(path), cb); + function cb(err, stats) { + if (callback) callback(err ? false : true); + } +}; + +fs.existsSync = function(path) { + try { + nullCheck(path); + binding.stat(pathModule._makeLong(path)); + return true; + } catch (e) { + return false; + } +}; + +fs.readFile = function(path, encoding_) { + var encoding = typeof(encoding_) === 'string' ? encoding_ : null; + var callback = maybeCallback(arguments[arguments.length - 1]); + + assertEncoding(encoding); + + // first, stat the file, so we know the size. + var size; + var buffer; // single buffer with file data + var buffers; // list for when size is unknown + var pos = 0; + var fd; + + fs.open(path, constants.O_RDONLY, 438 /*=0666*/, function(er, fd_) { + if (er) return callback(er); + fd = fd_; + + fs.fstat(fd, function(er, st) { + if (er) return callback(er); + size = st.size; + if (size === 0) { + // the kernel lies about many files. + // Go ahead and try to read some bytes. + buffers = []; + return read(); + } + + buffer = new Buffer(size); + read(); + }); + }); + + function read() { + if (size === 0) { + buffer = new Buffer(8192); + fs.read(fd, buffer, 0, 8192, -1, afterRead); + } else { + fs.read(fd, buffer, pos, size - pos, -1, afterRead); + } + } + + function afterRead(er, bytesRead) { + if (er) { + return fs.close(fd, function(er2) { + return callback(er); + }); + } + + if (bytesRead === 0) { + return close(); + } + + pos += bytesRead; + if (size !== 0) { + if (pos === size) close(); + else read(); + } else { + // unknown size, just read until we don't get bytes. + buffers.push(buffer.slice(0, bytesRead)); + read(); + } + } + + function close() { + fs.close(fd, function(er) { + if (size === 0) { + // collected the data into the buffers list. + buffer = Buffer.concat(buffers, pos); + } else if (pos < size) { + buffer = buffer.slice(0, pos); + } + + if (encoding) buffer = buffer.toString(encoding); + return callback(er, buffer); + }); + } +}; + +fs.readFileSync = function(path, encoding) { + assertEncoding(encoding); + + var fd = fs.openSync(path, constants.O_RDONLY, 438 /*=0666*/); + + var size; + var threw = true; + try { + size = fs.fstatSync(fd).size; + threw = false; + } finally { + if (threw) fs.closeSync(fd); + } + + var pos = 0; + var buffer; // single buffer with file data + var buffers; // list for when size is unknown + + if (size === 0) { + buffers = []; + } else { + buffer = new Buffer(size); + } + + var done = false; + while (!done) { + var threw = true; + try { + if (size !== 0) { + var bytesRead = fs.readSync(fd, buffer, pos, size - pos); + } else { + // the kernel lies about many files. + // Go ahead and try to read some bytes. + buffer = new Buffer(8192); + var bytesRead = fs.readSync(fd, buffer, 0, 8192); + if (bytesRead) { + buffers.push(buffer.slice(0, bytesRead)); + } + } + threw = false; + } finally { + if (threw) fs.closeSync(fd); + } + + pos += bytesRead; + done = (bytesRead === 0) || (size !== 0 && pos >= size); + } + + fs.closeSync(fd); + + if (size === 0) { + // data was collected into the buffers list. + buffer = Buffer.concat(buffers, pos); + } else if (pos < size) { + buffer = buffer.slice(0, pos); + } + + if (encoding) buffer = buffer.toString(encoding); + return buffer; +}; + + +// Used by binding.open and friends +function stringToFlags(flag) { + // Only mess with strings + if (typeof flag !== 'string') { + return flag; + } + + // O_EXCL is mandated by POSIX, Windows supports it too. + // Let's add a check anyway, just in case. + if (!O_EXCL && ~flag.indexOf('x')) { + throw errnoException('ENOSYS', 'fs.open(O_EXCL)'); + } + + switch (flag) { + case 'r' : return O_RDONLY; + case 'rs' : return O_RDONLY | O_SYNC; + case 'r+' : return O_RDWR; + case 'rs+' : return O_RDWR | O_SYNC; + + case 'w' : return O_TRUNC | O_CREAT | O_WRONLY; + case 'wx' : // fall through + case 'xw' : return O_TRUNC | O_CREAT | O_WRONLY | O_EXCL; + + case 'w+' : return O_TRUNC | O_CREAT | O_RDWR; + case 'wx+': // fall through + case 'xw+': return O_TRUNC | O_CREAT | O_RDWR | O_EXCL; + + case 'a' : return O_APPEND | O_CREAT | O_WRONLY; + case 'ax' : // fall through + case 'xa' : return O_APPEND | O_CREAT | O_WRONLY | O_EXCL; + + case 'a+' : return O_APPEND | O_CREAT | O_RDWR; + case 'ax+': // fall through + case 'xa+': return O_APPEND | O_CREAT | O_RDWR | O_EXCL; + } + + throw new Error('Unknown file open flag: ' + flag); +} + +// exported but hidden, only used by test/simple/test-fs-open-flags.js +Object.defineProperty(exports, '_stringToFlags', { + enumerable: false, + value: stringToFlags +}); + + +// Yes, the follow could be easily DRYed up but I provide the explicit +// list to make the arguments clear. + +fs.close = function(fd, callback) { + binding.close(fd, makeCallback(callback)); +}; + +fs.closeSync = function(fd) { + return binding.close(fd); +}; + +function modeNum(m, def) { + switch (typeof m) { + case 'number': return m; + case 'string': return parseInt(m, 8); + default: + if (def) { + return modeNum(def); + } else { + return undefined; + } + } +} + +fs.open = function(path, flags, mode, callback) { + callback = makeCallback(arguments[arguments.length - 1]); + mode = modeNum(mode, 438 /*=0666*/); + + if (!nullCheck(path, callback)) return; + binding.open(pathModule._makeLong(path), + stringToFlags(flags), + mode, + callback); +}; + +fs.openSync = function(path, flags, mode) { + mode = modeNum(mode, 438 /*=0666*/); + nullCheck(path); + return binding.open(pathModule._makeLong(path), stringToFlags(flags), mode); +}; + +fs.read = function(fd, buffer, offset, length, position, callback) { + if (!Buffer.isBuffer(buffer)) { + // legacy string interface (fd, length, position, encoding, callback) + var cb = arguments[4], + encoding = arguments[3]; + + assertEncoding(encoding); + + position = arguments[2]; + length = arguments[1]; + buffer = new Buffer(length); + offset = 0; + + callback = function(err, bytesRead) { + if (!cb) return; + + var str = (bytesRead > 0) ? buffer.toString(encoding, 0, bytesRead) : ''; + + (cb)(err, str, bytesRead); + }; + } + + function wrapper(err, bytesRead) { + // Retain a reference to buffer so that it can't be GC'ed too soon. + callback && callback(err, bytesRead || 0, buffer); + } + + binding.read(fd, buffer, offset, length, position, wrapper); +}; + +fs.readSync = function(fd, buffer, offset, length, position) { + var legacy = false; + if (!Buffer.isBuffer(buffer)) { + // legacy string interface (fd, length, position, encoding, callback) + legacy = true; + var encoding = arguments[3]; + + assertEncoding(encoding); + + position = arguments[2]; + length = arguments[1]; + buffer = new Buffer(length); + + offset = 0; + } + + var r = binding.read(fd, buffer, offset, length, position); + if (!legacy) { + return r; + } + + var str = (r > 0) ? buffer.toString(encoding, 0, r) : ''; + return [str, r]; +}; + +fs.write = function(fd, buffer, offset, length, position, callback) { + if (!Buffer.isBuffer(buffer)) { + // legacy string interface (fd, data, position, encoding, callback) + callback = arguments[4]; + position = arguments[2]; + assertEncoding(arguments[3]); + + buffer = new Buffer('' + arguments[1], arguments[3]); + offset = 0; + length = buffer.length; + } + + if (!length) { + if (typeof callback == 'function') { + process.nextTick(function() { + callback(undefined, 0); + }); + } + return; + } + + callback = maybeCallback(callback); + + function wrapper(err, written) { + // Retain a reference to buffer so that it can't be GC'ed too soon. + callback(err, written || 0, buffer); + } + + binding.write(fd, buffer, offset, length, position, wrapper); +}; + +fs.writeSync = function(fd, buffer, offset, length, position) { + if (!Buffer.isBuffer(buffer)) { + // legacy string interface (fd, data, position, encoding) + position = arguments[2]; + assertEncoding(arguments[3]); + + buffer = new Buffer('' + arguments[1], arguments[3]); + offset = 0; + length = buffer.length; + } + if (!length) return 0; + + return binding.write(fd, buffer, offset, length, position); +}; + +fs.rename = function(oldPath, newPath, callback) { + callback = makeCallback(callback); + if (!nullCheck(oldPath, callback)) return; + if (!nullCheck(newPath, callback)) return; + binding.rename(pathModule._makeLong(oldPath), + pathModule._makeLong(newPath), + callback); +}; + +fs.renameSync = function(oldPath, newPath) { + nullCheck(oldPath); + nullCheck(newPath); + return binding.rename(pathModule._makeLong(oldPath), + pathModule._makeLong(newPath)); +}; + +fs.truncate = function(path, len, callback) { + if (typeof path === 'number') { + // legacy + return fs.ftruncate(path, len, callback); + } + if (typeof len === 'function') { + callback = len; + len = 0; + } else if (typeof len === 'undefined') { + len = 0; + } + callback = maybeCallback(callback); + fs.open(path, 'w', function(er, fd) { + if (er) return callback(er); + binding.ftruncate(fd, len, function(er) { + fs.close(fd, function(er2) { + callback(er || er2); + }); + }); + }); +}; + +fs.truncateSync = function(path, len) { + if (typeof path === 'number') { + // legacy + return fs.ftruncateSync(path, len); + } + if (typeof len === 'undefined') { + len = 0; + } + // allow error to be thrown, but still close fd. + var fd = fs.openSync(path, 'w'); + try { + var ret = fs.ftruncateSync(fd, len); + } finally { + fs.closeSync(fd); + } + return ret; +}; + +fs.ftruncate = function(fd, len, callback) { + if (typeof len === 'function') { + callback = len; + len = 0; + } else if (typeof len === 'undefined') { + len = 0; + } + binding.ftruncate(fd, len, makeCallback(callback)); +}; + +fs.ftruncateSync = function(fd, len) { + if (typeof len === 'undefined') { + len = 0; + } + return binding.ftruncate(fd, len); +}; + +fs.rmdir = function(path, callback) { + callback = makeCallback(callback); + if (!nullCheck(path, callback)) return; + binding.rmdir(pathModule._makeLong(path), callback); +}; + +fs.rmdirSync = function(path) { + nullCheck(path); + return binding.rmdir(pathModule._makeLong(path)); +}; + +fs.fdatasync = function(fd, callback) { + binding.fdatasync(fd, makeCallback(callback)); +}; + +fs.fdatasyncSync = function(fd) { + return binding.fdatasync(fd); +}; + +fs.fsync = function(fd, callback) { + binding.fsync(fd, makeCallback(callback)); +}; + +fs.fsyncSync = function(fd) { + return binding.fsync(fd); +}; + +fs.mkdir = function(path, mode, callback) { + if (typeof mode === 'function') callback = mode; + callback = makeCallback(callback); + if (!nullCheck(path, callback)) return; + binding.mkdir(pathModule._makeLong(path), + modeNum(mode, 511 /*=0777*/), + callback); +}; + +fs.mkdirSync = function(path, mode) { + nullCheck(path); + return binding.mkdir(pathModule._makeLong(path), + modeNum(mode, 511 /*=0777*/)); +}; + +fs.sendfile = function(outFd, inFd, inOffset, length, callback) { + binding.sendfile(outFd, inFd, inOffset, length, makeCallback(callback)); +}; + +fs.sendfileSync = function(outFd, inFd, inOffset, length) { + return binding.sendfile(outFd, inFd, inOffset, length); +}; + +fs.readdir = function(path, callback) { + callback = makeCallback(callback); + if (!nullCheck(path, callback)) return; + binding.readdir(pathModule._makeLong(path), callback); +}; + +fs.readdirSync = function(path) { + nullCheck(path); + return binding.readdir(pathModule._makeLong(path)); +}; + +fs.fstat = function(fd, callback) { + binding.fstat(fd, makeCallback(callback)); +}; + +fs.lstat = function(path, callback) { + callback = makeCallback(callback); + if (!nullCheck(path, callback)) return; + binding.lstat(pathModule._makeLong(path), callback); +}; + +fs.stat = function(path, callback) { + callback = makeCallback(callback); + if (!nullCheck(path, callback)) return; + binding.stat(pathModule._makeLong(path), callback); +}; + +fs.fstatSync = function(fd) { + return binding.fstat(fd); +}; + +fs.lstatSync = function(path) { + nullCheck(path); + return binding.lstat(pathModule._makeLong(path)); +}; + +fs.statSync = function(path) { + nullCheck(path); + return binding.stat(pathModule._makeLong(path)); +}; + +fs.readlink = function(path, callback) { + callback = makeCallback(callback); + if (!nullCheck(path, callback)) return; + binding.readlink(pathModule._makeLong(path), callback); +}; + +fs.readlinkSync = function(path) { + nullCheck(path); + return binding.readlink(pathModule._makeLong(path)); +}; + +function preprocessSymlinkDestination(path, type) { + if (!isWindows) { + // No preprocessing is needed on Unix. + return path; + } else if (type === 'junction') { + // Junctions paths need to be absolute and \\?\-prefixed. + return pathModule._makeLong(path); + } else { + // Windows symlinks don't tolerate forward slashes. + return ('' + path).replace(/\//g, '\\'); + } +} + +fs.symlink = function(destination, path, type_, callback) { + var type = (typeof type_ === 'string' ? type_ : null); + var callback = makeCallback(arguments[arguments.length - 1]); + + if (!nullCheck(destination, callback)) return; + if (!nullCheck(path, callback)) return; + + binding.symlink(preprocessSymlinkDestination(destination, type), + pathModule._makeLong(path), + type, + callback); +}; + +fs.symlinkSync = function(destination, path, type) { + type = (typeof type === 'string' ? type : null); + + nullCheck(destination); + nullCheck(path); + + return binding.symlink(preprocessSymlinkDestination(destination, type), + pathModule._makeLong(path), + type); +}; + +fs.link = function(srcpath, dstpath, callback) { + callback = makeCallback(callback); + if (!nullCheck(srcpath, callback)) return; + if (!nullCheck(dstpath, callback)) return; + + binding.link(pathModule._makeLong(srcpath), + pathModule._makeLong(dstpath), + callback); +}; + +fs.linkSync = function(srcpath, dstpath) { + nullCheck(srcpath); + nullCheck(dstpath); + return binding.link(pathModule._makeLong(srcpath), + pathModule._makeLong(dstpath)); +}; + +fs.unlink = function(path, callback) { + callback = makeCallback(callback); + if (!nullCheck(path, callback)) return; + binding.unlink(pathModule._makeLong(path), callback); +}; + +fs.unlinkSync = function(path) { + nullCheck(path); + return binding.unlink(pathModule._makeLong(path)); +}; + +fs.fchmod = function(fd, mode, callback) { + binding.fchmod(fd, modeNum(mode), makeCallback(callback)); +}; + +fs.fchmodSync = function(fd, mode) { + return binding.fchmod(fd, modeNum(mode)); +}; + +if (constants.hasOwnProperty('O_SYMLINK')) { + fs.lchmod = function(path, mode, callback) { + callback = maybeCallback(callback); + fs.open(path, constants.O_WRONLY | constants.O_SYMLINK, function(err, fd) { + if (err) { + callback(err); + return; + } + // prefer to return the chmod error, if one occurs, + // but still try to close, and report closing errors if they occur. + fs.fchmod(fd, mode, function(err) { + fs.close(fd, function(err2) { + callback(err || err2); + }); + }); + }); + }; + + fs.lchmodSync = function(path, mode) { + var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK); + + // prefer to return the chmod error, if one occurs, + // but still try to close, and report closing errors if they occur. + var err, err2; + try { + var ret = fs.fchmodSync(fd, mode); + } catch (er) { + err = er; + } + try { + fs.closeSync(fd); + } catch (er) { + err2 = er; + } + if (err || err2) throw (err || err2); + return ret; + }; +} + + +fs.chmod = function(path, mode, callback) { + callback = makeCallback(callback); + if (!nullCheck(path, callback)) return; + binding.chmod(pathModule._makeLong(path), + modeNum(mode), + callback); +}; + +fs.chmodSync = function(path, mode) { + nullCheck(path); + return binding.chmod(pathModule._makeLong(path), modeNum(mode)); +}; + +if (constants.hasOwnProperty('O_SYMLINK')) { + fs.lchown = function(path, uid, gid, callback) { + callback = maybeCallback(callback); + fs.open(path, constants.O_WRONLY | constants.O_SYMLINK, function(err, fd) { + if (err) { + callback(err); + return; + } + fs.fchown(fd, uid, gid, callback); + }); + }; + + fs.lchownSync = function(path, uid, gid) { + var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK); + return fs.fchownSync(fd, uid, gid); + }; +} + +fs.fchown = function(fd, uid, gid, callback) { + binding.fchown(fd, uid, gid, makeCallback(callback)); +}; + +fs.fchownSync = function(fd, uid, gid) { + return binding.fchown(fd, uid, gid); +}; + +fs.chown = function(path, uid, gid, callback) { + callback = makeCallback(callback); + if (!nullCheck(path, callback)) return; + binding.chown(pathModule._makeLong(path), uid, gid, callback); +}; + +fs.chownSync = function(path, uid, gid) { + nullCheck(path); + return binding.chown(pathModule._makeLong(path), uid, gid); +}; + +// converts Date or number to a fractional UNIX timestamp +function toUnixTimestamp(time) { + if (typeof time == 'number') { + return time; + } + if (time instanceof Date) { + // convert to 123.456 UNIX timestamp + return time.getTime() / 1000; + } + throw new Error('Cannot parse time: ' + time); +} + +// exported for unit tests, not for public consumption +fs._toUnixTimestamp = toUnixTimestamp; + +fs.utimes = function(path, atime, mtime, callback) { + callback = makeCallback(callback); + if (!nullCheck(path, callback)) return; + binding.utimes(pathModule._makeLong(path), + toUnixTimestamp(atime), + toUnixTimestamp(mtime), + callback); +}; + +fs.utimesSync = function(path, atime, mtime) { + nullCheck(path); + atime = toUnixTimestamp(atime); + mtime = toUnixTimestamp(mtime); + binding.utimes(pathModule._makeLong(path), atime, mtime); +}; + +fs.futimes = function(fd, atime, mtime, callback) { + atime = toUnixTimestamp(atime); + mtime = toUnixTimestamp(mtime); + binding.futimes(fd, atime, mtime, makeCallback(callback)); +}; + +fs.futimesSync = function(fd, atime, mtime) { + atime = toUnixTimestamp(atime); + mtime = toUnixTimestamp(mtime); + binding.futimes(fd, atime, mtime); +}; + +function writeAll(fd, buffer, offset, length, position, callback) { + callback = maybeCallback(arguments[arguments.length - 1]); + + // write(fd, buffer, offset, length, position, callback) + fs.write(fd, buffer, offset, length, position, function(writeErr, written) { + if (writeErr) { + fs.close(fd, function() { + if (callback) callback(writeErr); + }); + } else { + if (written === length) { + fs.close(fd, callback); + } else { + offset += written; + length -= written; + position += written; + writeAll(fd, buffer, offset, length, position, callback); + } + } + }); +} + +fs.writeFile = function(path, data, encoding_, callback) { + var encoding = (typeof(encoding_) == 'string' ? encoding_ : 'utf8'); + assertEncoding(encoding); + + callback = maybeCallback(arguments[arguments.length - 1]); + fs.open(path, 'w', 438 /*=0666*/, function(openErr, fd) { + if (openErr) { + if (callback) callback(openErr); + } else { + var buffer = Buffer.isBuffer(data) ? data : new Buffer('' + data, + encoding); + writeAll(fd, buffer, 0, buffer.length, 0, callback); + } + }); +}; + +fs.writeFileSync = function(path, data, encoding) { + assertEncoding(encoding); + + var fd = fs.openSync(path, 'w'); + if (!Buffer.isBuffer(data)) { + data = new Buffer('' + data, encoding || 'utf8'); + } + var written = 0; + var length = data.length; + try { + while (written < length) { + written += fs.writeSync(fd, data, written, length - written, written); + } + } finally { + fs.closeSync(fd); + } +}; + +fs.appendFile = function(path, data, encoding_, callback) { + var encoding = (typeof(encoding_) == 'string' ? encoding_ : 'utf8'); + assertEncoding(encoding); + + callback = maybeCallback(arguments[arguments.length - 1]); + + fs.open(path, 'a', 438 /*=0666*/, function(err, fd) { + if (err) return callback(err); + var buffer = Buffer.isBuffer(data) ? data : new Buffer('' + data, encoding); + writeAll(fd, buffer, 0, buffer.length, null, callback); + }); +}; + +fs.appendFileSync = function(path, data, encoding) { + assertEncoding(encoding); + + var fd = fs.openSync(path, 'a'); + if (!Buffer.isBuffer(data)) { + data = new Buffer('' + data, encoding || 'utf8'); + } + var written = 0; + var position = null; + var length = data.length; + + try { + while (written < length) { + written += fs.writeSync(fd, data, written, length - written, position); + position += written; // XXX not safe with multiple concurrent writers? + } + } finally { + fs.closeSync(fd); + } +}; + +function errnoException(errorno, syscall) { + // TODO make this more compatible with ErrnoException from src/node.cc + // Once all of Node is using this function the ErrnoException from + // src/node.cc should be removed. + var e = new Error(syscall + ' ' + errorno); + e.errno = e.code = errorno; + e.syscall = syscall; + return e; +} + + +function FSWatcher() { + EventEmitter.call(this); + + var self = this; + var FSEvent = process.binding('fs_event_wrap').FSEvent; + this._handle = new FSEvent(); + this._handle.owner = this; + + this._handle.onchange = function(status, event, filename) { + if (status) { + self._handle.close(); + self.emit('error', errnoException(errno, 'watch')); + } else { + self.emit('change', event, filename); + } + }; +} +util.inherits(FSWatcher, EventEmitter); + +FSWatcher.prototype.start = function(filename, persistent) { + nullCheck(filename); + var r = this._handle.start(pathModule._makeLong(filename), persistent); + + if (r) { + this._handle.close(); + throw errnoException(errno, 'watch'); + } +}; + +FSWatcher.prototype.close = function() { + this._handle.close(); +}; + +fs.watch = function(filename) { + nullCheck(filename); + var watcher; + var options; + var listener; + + if ('object' == typeof arguments[1]) { + options = arguments[1]; + listener = arguments[2]; + } else { + options = {}; + listener = arguments[1]; + } + + if (options.persistent === undefined) options.persistent = true; + + watcher = new FSWatcher(); + watcher.start(filename, options.persistent); + + if (listener) { + watcher.addListener('change', listener); + } + + return watcher; +}; + + +// Stat Change Watchers + +function StatWatcher() { + EventEmitter.call(this); + + var self = this; + this._handle = new binding.StatWatcher(); + + // uv_fs_poll is a little more powerful than ev_stat but we curb it for + // the sake of backwards compatibility + var oldStatus = -1; + + this._handle.onchange = function(current, previous, newStatus) { + if (oldStatus === -1 && + newStatus === -1 && + current.nlink === previous.nlink) return; + + oldStatus = newStatus; + self.emit('change', current, previous); + }; + + this._handle.onstop = function() { + self.emit('stop'); + }; +} +util.inherits(StatWatcher, EventEmitter); + + +StatWatcher.prototype.start = function(filename, persistent, interval) { + nullCheck(filename); + this._handle.start(pathModule._makeLong(filename), persistent, interval); +}; + + +StatWatcher.prototype.stop = function() { + this._handle.stop(); +}; + + +var statWatchers = {}; +function inStatWatchers(filename) { + return Object.prototype.hasOwnProperty.call(statWatchers, filename) && + statWatchers[filename]; +} + + +fs.watchFile = function(filename) { + nullCheck(filename); + var stat; + var listener; + + var options = { + // Poll interval in milliseconds. 5007 is what libev used to use. It's + // a little on the slow side but let's stick with it for now to keep + // behavioral changes to a minimum. + interval: 5007, + persistent: true + }; + + if ('object' == typeof arguments[1]) { + options = util._extend(options, arguments[1]); + listener = arguments[2]; + } else { + listener = arguments[1]; + } + + if (!listener) { + throw new Error('watchFile requires a listener function'); + } + + if (inStatWatchers(filename)) { + stat = statWatchers[filename]; + } else { + stat = statWatchers[filename] = new StatWatcher(); + stat.start(filename, options.persistent, options.interval); + } + stat.addListener('change', listener); + return stat; +}; + +fs.unwatchFile = function(filename, listener) { + nullCheck(filename); + if (!inStatWatchers(filename)) return; + + var stat = statWatchers[filename]; + + if (typeof listener === 'function') { + stat.removeListener('change', listener); + } else { + stat.removeAllListeners('change'); + } + + if (stat.listeners('change').length === 0) { + stat.stop(); + statWatchers[filename] = undefined; + } +}; + +// Realpath +// Not using realpath(2) because it's bad. +// See: http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html + +var normalize = pathModule.normalize; + +// Regexp that finds the next partion of a (partial) path +// result is [base_with_slash, base], e.g. ['somedir/', 'somedir'] +if (isWindows) { + var nextPartRe = /(.*?)(?:[\/\\]+|$)/g; +} else { + var nextPartRe = /(.*?)(?:[\/]+|$)/g; +} + +// Regex to find the device root, including trailing slash. E.g. 'c:\\'. +if (isWindows) { + var splitRootRe = /^(?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?[\\\/]*/; +} else { + var splitRootRe = /^[\/]*/; +} + +fs.realpathSync = function realpathSync(p, cache) { + // make p is absolute + p = pathModule.resolve(p); + + if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { + return cache[p]; + } + + var original = p, + seenLinks = {}, + knownHard = {}; + + // current character position in p + var pos; + // the partial path so far, including a trailing slash if any + var current; + // the partial path without a trailing slash (except when pointing at a root) + var base; + // the partial path scanned in the previous round, with slash + var previous; + + start(); + + function start() { + // Skip over roots + var m = splitRootRe.exec(p); + pos = m[0].length; + current = m[0]; + base = m[0]; + previous = ''; + + // On windows, check that the root exists. On unix there is no need. + if (isWindows && !knownHard[base]) { + fs.lstatSync(base); + knownHard[base] = true; + } + } + + // walk down the path, swapping out linked pathparts for their real + // values + // NB: p.length changes. + while (pos < p.length) { + // find the next part + nextPartRe.lastIndex = pos; + var result = nextPartRe.exec(p); + previous = current; + current += result[0]; + base = previous + result[1]; + pos = nextPartRe.lastIndex; + + // continue if not a symlink + if (knownHard[base] || (cache && cache[base] === base)) { + continue; + } + + var resolvedLink; + if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { + // some known symbolic link. no need to stat again. + resolvedLink = cache[base]; + } else { + var stat = fs.lstatSync(base); + if (!stat.isSymbolicLink()) { + knownHard[base] = true; + if (cache) cache[base] = base; + continue; + } + + // read the link if it wasn't read before + // dev/ino always return 0 on windows, so skip the check. + var linkTarget = null; + if (!isWindows) { + var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); + if (seenLinks.hasOwnProperty(id)) { + linkTarget = seenLinks[id]; + } + } + if (linkTarget === null) { + fs.statSync(base); + linkTarget = fs.readlinkSync(base); + } + resolvedLink = pathModule.resolve(previous, linkTarget); + // track this, if given a cache. + if (cache) cache[base] = resolvedLink; + if (!isWindows) seenLinks[id] = linkTarget; + } + + // resolve the link, then start over + p = pathModule.resolve(resolvedLink, p.slice(pos)); + start(); + } + + if (cache) cache[original] = p; + + return p; +}; + + +fs.realpath = function realpath(p, cache, cb) { + if (typeof cb !== 'function') { + cb = maybeCallback(cache); + cache = null; + } + + // make p is absolute + p = pathModule.resolve(p); + + if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { + return process.nextTick(cb.bind(null, null, cache[p])); + } + + var original = p, + seenLinks = {}, + knownHard = {}; + + // current character position in p + var pos; + // the partial path so far, including a trailing slash if any + var current; + // the partial path without a trailing slash (except when pointing at a root) + var base; + // the partial path scanned in the previous round, with slash + var previous; + + start(); + + function start() { + // Skip over roots + var m = splitRootRe.exec(p); + pos = m[0].length; + current = m[0]; + base = m[0]; + previous = ''; + + // On windows, check that the root exists. On unix there is no need. + if (isWindows && !knownHard[base]) { + fs.lstat(base, function(err) { + if (err) return cb(err); + knownHard[base] = true; + LOOP(); + }); + } else { + process.nextTick(LOOP); + } + } + + // walk down the path, swapping out linked pathparts for their real + // values + function LOOP() { + // stop if scanned past end of path + if (pos >= p.length) { + if (cache) cache[original] = p; + return cb(null, p); + } + + // find the next part + nextPartRe.lastIndex = pos; + var result = nextPartRe.exec(p); + previous = current; + current += result[0]; + base = previous + result[1]; + pos = nextPartRe.lastIndex; + + // continue if not a symlink + if (knownHard[base] || (cache && cache[base] === base)) { + return process.nextTick(LOOP); + } + + if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { + // known symbolic link. no need to stat again. + return gotResolvedLink(cache[base]); + } + + return fs.lstat(base, gotStat); + } + + function gotStat(err, stat) { + if (err) return cb(err); + + // if not a symlink, skip to the next path part + if (!stat.isSymbolicLink()) { + knownHard[base] = true; + if (cache) cache[base] = base; + return process.nextTick(LOOP); + } + + // stat & read the link if not read before + // call gotTarget as soon as the link target is known + // dev/ino always return 0 on windows, so skip the check. + if (!isWindows) { + var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); + if (seenLinks.hasOwnProperty(id)) { + return gotTarget(null, seenLinks[id], base); + } + } + fs.stat(base, function(err) { + if (err) return cb(err); + + fs.readlink(base, function(err, target) { + if (!isWindows) seenLinks[id] = target; + gotTarget(err, target); + }); + }); + } + + function gotTarget(err, target, base) { + if (err) return cb(err); + + var resolvedLink = pathModule.resolve(previous, target); + if (cache) cache[base] = resolvedLink; + gotResolvedLink(resolvedLink); + } + + function gotResolvedLink(resolvedLink) { + // resolve the link, then start over + p = pathModule.resolve(resolvedLink, p.slice(pos)); + start(); + } +}; + + + +var pool; + +function allocNewPool() { + pool = new Buffer(kPoolSize); + pool.used = 0; +} + + + +fs.createReadStream = function(path, options) { + return new ReadStream(path, options); +}; + +util.inherits(ReadStream, Readable); +fs.ReadStream = ReadStream; + +function ReadStream(path, options) { + if (!(this instanceof ReadStream)) + return new ReadStream(path, options); + + // a little bit bigger buffer and water marks by default + options = util._extend({ + bufferSize: 64 * 1024, + lowWaterMark: 16 * 1024, + highWaterMark: 64 * 1024 + }, options || {}); + + Readable.call(this, options); + + this.path = path; + this.fd = options.hasOwnProperty('fd') ? options.fd : null; + this.flags = options.hasOwnProperty('flags') ? options.flags : 'r'; + this.mode = options.hasOwnProperty('mode') ? options.mode : 438; /*=0666*/ + + this.start = options.hasOwnProperty('start') ? options.start : undefined; + this.end = options.hasOwnProperty('start') ? options.end : undefined; + this.pos = undefined; + + if (this.start !== undefined) { + if ('number' !== typeof this.start) { + throw TypeError('start must be a Number'); + } + if (this.end === undefined) { + this.end = Infinity; + } else if ('number' !== typeof this.end) { + throw TypeError('end must be a Number'); + } + + if (this.start > this.end) { + throw new Error('start must be <= end'); + } + + this.pos = this.start; + } + + if (typeof this.fd !== 'number') + this.open(); + + this.on('end', function() { + this.destroy(); + }); +} + +fs.FileReadStream = fs.ReadStream; // support the legacy name + +ReadStream.prototype.open = function() { + var self = this; + fs.open(this.path, this.flags, this.mode, function(er, fd) { + if (er) { + self.destroy(); + self.emit('error', er); + return; + } + + self.fd = fd; + self.emit('open', fd); + // start the flow of data. + self.read(); + }); +}; + +ReadStream.prototype._read = function(n, cb) { + if (typeof this.fd !== 'number') + return this.once('open', function() { + this._read(n, cb); + }); + + if (this.destroyed) + return; + + if (!pool || pool.length - pool.used < kMinPoolSpace) { + // discard the old pool. Can't add to the free list because + // users might have refernces to slices on it. + pool = null; + allocNewPool(); + } + + // Grab another reference to the pool in the case that while we're + // in the thread pool another read() finishes up the pool, and + // allocates a new one. + var thisPool = pool; + var toRead = Math.min(pool.length - pool.used, n); + var start = pool.used; + + if (this.pos !== undefined) + toRead = Math.min(this.end - this.pos + 1, toRead); + + // already read everything we were supposed to read! + // treat as EOF. + if (toRead <= 0) + return cb(); + + // the actual read. + var self = this; + fs.read(this.fd, pool, pool.used, toRead, this.pos, onread); + + // move the pool positions, and internal position for reading. + if (this.pos !== undefined) + this.pos += toRead; + pool.used += toRead; + + function onread(er, bytesRead) { + if (er) { + self.destroy(); + return cb(er); + } + + var b = null; + if (bytesRead > 0) + b = thisPool.slice(start, start + bytesRead); + + cb(null, b); + } +}; + + +ReadStream.prototype.destroy = function() { + if (this.destroyed) + return; + this.destroyed = true; + if ('number' === typeof this.fd) + this.close(); +}; + + +ReadStream.prototype.close = function(cb) { + if (cb) + this.once('close', cb); + if (this.closed || 'number' !== typeof this.fd) { + if ('number' !== typeof this.fd) + this.once('open', close); + return process.nextTick(this.emit.bind(this, 'close')); + } + this.closed = true; + var self = this; + close(); + + function close() { + fs.close(self.fd, function(er) { + if (er) + self.emit('error', er); + else + self.emit('close'); + }); + } +}; + + + + +fs.createWriteStream = function(path, options) { + return new WriteStream(path, options); +}; + +util.inherits(WriteStream, Writable); +fs.WriteStream = WriteStream; +function WriteStream(path, options) { + if (!(this instanceof WriteStream)) + return new WriteStream(path, options); + + // a little bit bigger buffer and water marks by default + options = util._extend({ + bufferSize: 64 * 1024, + lowWaterMark: 16 * 1024, + highWaterMark: 64 * 1024 + }, options || {}); + + Writable.call(this, options); + + this.path = path; + this.fd = null; + + this.fd = options.hasOwnProperty('fd') ? options.fd : null; + this.flags = options.hasOwnProperty('flags') ? options.flags : 'w'; + this.mode = options.hasOwnProperty('mode') ? options.mode : 438; /*=0666*/ + + this.start = options.hasOwnProperty('start') ? options.start : undefined; + this.pos = undefined; + this.bytesWritten = 0; + + if (this.start !== undefined) { + if ('number' !== typeof this.start) { + throw TypeError('start must be a Number'); + } + if (this.start < 0) { + throw new Error('start must be >= zero'); + } + + this.pos = this.start; + } + + if ('number' !== typeof this.fd) + this.open(); + + // dispose on finish. + this.once('finish', this.close); +} + +fs.FileWriteStream = fs.WriteStream; // support the legacy name + + +WriteStream.prototype.open = function() { + fs.open(this.path, this.flags, this.mode, function(er, fd) { + if (er) { + this.destroy(); + this.emit('error', er); + return; + } + + this.fd = fd; + this.emit('open', fd); + }.bind(this)); +}; + + +WriteStream.prototype._write = function(data, cb) { + if (!Buffer.isBuffer(data)) + return this.emit('error', new Error('Invalid data')); + + if (typeof this.fd !== 'number') + return this.once('open', this._write.bind(this, data, cb)); + + fs.write(this.fd, data, 0, data.length, this.pos, function(er, bytes) { + if (er) { + this.destroy(); + return cb(er); + } + this.bytesWritten += bytes; + cb(); + }.bind(this)); + + if (this.pos !== undefined) + this.pos += data.length; +}; + + +WriteStream.prototype.destroy = ReadStream.prototype.destroy; +WriteStream.prototype.close = ReadStream.prototype.close; + +// There is no shutdown() for files. +WriteStream.prototype.destroySoon = WriteStream.prototype.end; + + +// SyncWriteStream is internal. DO NOT USE. +// Temporary hack for process.stdout and process.stderr when piped to files. +function SyncWriteStream(fd) { + Stream.call(this); + + this.fd = fd; + this.writable = true; + this.readable = false; +} + +util.inherits(SyncWriteStream, Stream); + + +// Export +fs.SyncWriteStream = SyncWriteStream; + + +SyncWriteStream.prototype.write = function(data, arg1, arg2) { + var encoding, cb; + + // parse arguments + if (arg1) { + if (typeof arg1 === 'string') { + encoding = arg1; + cb = arg2; + } else if (typeof arg1 === 'function') { + cb = arg1; + } else { + throw new Error('bad arg'); + } + } + assertEncoding(encoding); + + // Change strings to buffers. SLOW + if (typeof data == 'string') { + data = new Buffer(data, encoding); + } + + fs.writeSync(this.fd, data, 0, data.length); + + if (cb) { + process.nextTick(cb); + } + + return true; +}; + + +SyncWriteStream.prototype.end = function(data, arg1, arg2) { + if (data) { + this.write(data, arg1, arg2); + } + this.destroy(); +}; + + +SyncWriteStream.prototype.destroy = function() { + fs.closeSync(this.fd); + this.fd = null; + this.emit('close'); + return true; +}; + +SyncWriteStream.prototype.destroySoon = SyncWriteStream.prototype.destroy; diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/lib/_stream_duplex.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/lib/_stream_duplex.js new file mode 100644 index 0000000..a2e0d8e --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/lib/_stream_duplex.js @@ -0,0 +1,69 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// a duplex stream is just a stream that is both readable and writable. +// Since JS doesn't have multiple prototypal inheritance, this class +// prototypally inherits from Readable, and then parasitically from +// Writable. + +module.exports = Duplex; +var util = require('util'); +var Readable = require('./_stream_readable'); +var Writable = require('./_stream_writable'); + +util.inherits(Duplex, Readable); + +Object.keys(Writable.prototype).forEach(function(method) { + if (!Duplex.prototype[method]) + Duplex.prototype[method] = Writable.prototype[method]; +}); + +function Duplex(options) { + if (!(this instanceof Duplex)) + return new Duplex(options); + + Readable.call(this, options); + Writable.call(this, options); + + if (options && options.readable === false) + this.readable = false; + + if (options && options.writable === false) + this.writable = false; + + this.allowHalfOpen = true; + if (options && options.allowHalfOpen === false) + this.allowHalfOpen = false; + + this.once('end', onend); +} + +// the no-half-open enforcer +function onend() { + // if we allow half-open state, or if the writable side ended, + // then we're ok. + if (this.allowHalfOpen || this._writableState.ended) + return; + + // no more data can be written. + // But allow more writes to happen in this tick. + process.nextTick(this.end.bind(this)); +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/lib/_stream_passthrough.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/lib/_stream_passthrough.js new file mode 100644 index 0000000..330c247 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/lib/_stream_passthrough.js @@ -0,0 +1,41 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// a passthrough stream. +// basically just the most minimal sort of Transform stream. +// Every written chunk gets output as-is. + +module.exports = PassThrough; + +var Transform = require('./_stream_transform'); +var util = require('util'); +util.inherits(PassThrough, Transform); + +function PassThrough(options) { + if (!(this instanceof PassThrough)) + return new PassThrough(options); + + Transform.call(this, options); +} + +PassThrough.prototype._transform = function(chunk, encoding, cb) { + cb(null, chunk); +}; diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/lib/_stream_readable.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/lib/_stream_readable.js new file mode 100644 index 0000000..90a8298 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/lib/_stream_readable.js @@ -0,0 +1,920 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +module.exports = Readable; +Readable.ReadableState = ReadableState; + +var EE = require('events').EventEmitter; +if (!EE.listenerCount) EE.listenerCount = function(emitter, type) { + return emitter.listeners(type).length; +}; + +if (!global.setImmediate) global.setImmediate = function setImmediate(fn) { + return setTimeout(fn, 0); +}; +if (!global.clearImmediate) global.clearImmediate = function clearImmediate(i) { + return clearTimeout(i); +}; + +var Stream = require('stream'); +var util = require('util'); +if (!util.isUndefined) { + var utilIs = require('core-util-is'); + for (var f in utilIs) { + util[f] = utilIs[f]; + } +} +var StringDecoder; +var debug; +if (util.debuglog) + debug = util.debuglog('stream'); +else try { + debug = require('debuglog')('stream'); +} catch (er) { + debug = function() {}; +} + +util.inherits(Readable, Stream); + +function ReadableState(options, stream) { + options = options || {}; + + // the point at which it stops calling _read() to fill the buffer + // Note: 0 is a valid value, means "don't call _read preemptively ever" + var hwm = options.highWaterMark; + this.highWaterMark = (hwm || hwm === 0) ? hwm : 16 * 1024; + + // cast to ints. + this.highWaterMark = ~~this.highWaterMark; + + this.buffer = []; + this.length = 0; + this.pipes = null; + this.pipesCount = 0; + this.flowing = null; + this.ended = false; + this.endEmitted = false; + this.reading = false; + + // a flag to be able to tell if the onwrite cb is called immediately, + // or on a later tick. We set this to true at first, because any + // actions that shouldn't happen until "later" should generally also + // not happen before the first write call. + this.sync = true; + + // whenever we return null, then we set a flag to say + // that we're awaiting a 'readable' event emission. + this.needReadable = false; + this.emittedReadable = false; + this.readableListening = false; + + + // object stream flag. Used to make read(n) ignore n and to + // make all the buffer merging and length checks go away + this.objectMode = !!options.objectMode; + + // Crypto is kind of old and crusty. Historically, its default string + // encoding is 'binary' so we have to make this configurable. + // Everything else in the universe uses 'utf8', though. + this.defaultEncoding = options.defaultEncoding || 'utf8'; + + // when piping, we only care about 'readable' events that happen + // after read()ing all the bytes and not getting any pushback. + this.ranOut = false; + + // the number of writers that are awaiting a drain event in .pipe()s + this.awaitDrain = 0; + + // if true, a maybeReadMore has been scheduled + this.readingMore = false; + + this.decoder = null; + this.encoding = null; + if (options.encoding) { + if (!StringDecoder) + StringDecoder = require('string_decoder').StringDecoder; + this.decoder = new StringDecoder(options.encoding); + this.encoding = options.encoding; + } +} + +function Readable(options) { + if (!(this instanceof Readable)) + return new Readable(options); + + this._readableState = new ReadableState(options, this); + + // legacy + this.readable = true; + + Stream.call(this); +} + +// Manually shove something into the read() buffer. +// This returns true if the highWaterMark has not been hit yet, +// similar to how Writable.write() returns true if you should +// write() some more. +Readable.prototype.push = function(chunk, encoding) { + var state = this._readableState; + + if (util.isString(chunk) && !state.objectMode) { + encoding = encoding || state.defaultEncoding; + if (encoding !== state.encoding) { + chunk = new Buffer(chunk, encoding); + encoding = ''; + } + } + + return readableAddChunk(this, state, chunk, encoding, false); +}; + +// Unshift should *always* be something directly out of read() +Readable.prototype.unshift = function(chunk) { + var state = this._readableState; + return readableAddChunk(this, state, chunk, '', true); +}; + +function readableAddChunk(stream, state, chunk, encoding, addToFront) { + var er = chunkInvalid(state, chunk); + if (er) { + stream.emit('error', er); + } else if (util.isNullOrUndefined(chunk)) { + state.reading = false; + if (!state.ended) + onEofChunk(stream, state); + } else if (state.objectMode || chunk && chunk.length > 0) { + if (state.ended && !addToFront) { + var e = new Error('stream.push() after EOF'); + stream.emit('error', e); + } else if (state.endEmitted && addToFront) { + var e = new Error('stream.unshift() after end event'); + stream.emit('error', e); + } else { + if (state.decoder && !addToFront && !encoding) + chunk = state.decoder.write(chunk); + + if (!addToFront) + state.reading = false; + + // if we want the data now, just emit it. + if (state.flowing && state.length === 0 && !state.sync) { + stream.emit('data', chunk); + stream.read(0); + } else { + // update the buffer info. + state.length += state.objectMode ? 1 : chunk.length; + if (addToFront) + state.buffer.unshift(chunk); + else + state.buffer.push(chunk); + + if (state.needReadable) + emitReadable(stream); + } + + maybeReadMore(stream, state); + } + } else if (!addToFront) { + state.reading = false; + } + + return needMoreData(state); +} + + + +// if it's past the high water mark, we can push in some more. +// Also, if we have no data yet, we can stand some +// more bytes. This is to work around cases where hwm=0, +// such as the repl. Also, if the push() triggered a +// readable event, and the user called read(largeNumber) such that +// needReadable was set, then we ought to push more, so that another +// 'readable' event will be triggered. +function needMoreData(state) { + return !state.ended && + (state.needReadable || + state.length < state.highWaterMark || + state.length === 0); +} + +// backwards compatibility. +Readable.prototype.setEncoding = function(enc) { + if (!StringDecoder) + StringDecoder = require('string_decoder').StringDecoder; + this._readableState.decoder = new StringDecoder(enc); + this._readableState.encoding = enc; +}; + +// Don't raise the hwm > 128MB +var MAX_HWM = 0x800000; +function roundUpToNextPowerOf2(n) { + if (n >= MAX_HWM) { + n = MAX_HWM; + } else { + // Get the next highest power of 2 + n--; + for (var p = 1; p < 32; p <<= 1) n |= n >> p; + n++; + } + return n; +} + +function howMuchToRead(n, state) { + if (state.length === 0 && state.ended) + return 0; + + if (state.objectMode) + return n === 0 ? 0 : 1; + + if (isNaN(n) || util.isNull(n)) { + // only flow one buffer at a time + if (state.flowing && state.buffer.length) + return state.buffer[0].length; + else + return state.length; + } + + if (n <= 0) + return 0; + + // If we're asking for more than the target buffer level, + // then raise the water mark. Bump up to the next highest + // power of 2, to prevent increasing it excessively in tiny + // amounts. + if (n > state.highWaterMark) + state.highWaterMark = roundUpToNextPowerOf2(n); + + // don't have that much. return null, unless we've ended. + if (n > state.length) { + if (!state.ended) { + state.needReadable = true; + return 0; + } else + return state.length; + } + + return n; +} + +// you can override either this method, or the async _read(n) below. +Readable.prototype.read = function(n) { + debug('read', n); + var state = this._readableState; + var nOrig = n; + + if (!util.isNumber(n) || n > 0) + state.emittedReadable = false; + + // if we're doing read(0) to trigger a readable event, but we + // already have a bunch of data in the buffer, then just trigger + // the 'readable' event and move on. + if (n === 0 && + state.needReadable && + (state.length >= state.highWaterMark || state.ended)) { + debug('read: emitReadable', state.length, state.ended); + if (state.length === 0 && state.ended) + endReadable(this); + else + emitReadable(this); + return null; + } + + n = howMuchToRead(n, state); + + // if we've ended, and we're now clear, then finish it up. + if (n === 0 && state.ended) { + if (state.length === 0) + endReadable(this); + return null; + } + + // All the actual chunk generation logic needs to be + // *below* the call to _read. The reason is that in certain + // synthetic stream cases, such as passthrough streams, _read + // may be a completely synchronous operation which may change + // the state of the read buffer, providing enough data when + // before there was *not* enough. + // + // So, the steps are: + // 1. Figure out what the state of things will be after we do + // a read from the buffer. + // + // 2. If that resulting state will trigger a _read, then call _read. + // Note that this may be asynchronous, or synchronous. Yes, it is + // deeply ugly to write APIs this way, but that still doesn't mean + // that the Readable class should behave improperly, as streams are + // designed to be sync/async agnostic. + // Take note if the _read call is sync or async (ie, if the read call + // has returned yet), so that we know whether or not it's safe to emit + // 'readable' etc. + // + // 3. Actually pull the requested chunks out of the buffer and return. + + // if we need a readable event, then we need to do some reading. + var doRead = state.needReadable; + debug('need readable', doRead); + + // if we currently have less than the highWaterMark, then also read some + if (state.length === 0 || state.length - n < state.highWaterMark) { + doRead = true; + debug('length less than watermark', doRead); + } + + // however, if we've ended, then there's no point, and if we're already + // reading, then it's unnecessary. + if (state.ended || state.reading) { + doRead = false; + debug('reading or ended', doRead); + } + + if (doRead) { + debug('do read'); + state.reading = true; + state.sync = true; + // if the length is currently zero, then we *need* a readable event. + if (state.length === 0) + state.needReadable = true; + // call internal read method + this._read(state.highWaterMark); + state.sync = false; + } + + // If _read pushed data synchronously, then `reading` will be false, + // and we need to re-evaluate how much data we can return to the user. + if (doRead && !state.reading) + n = howMuchToRead(nOrig, state); + + var ret; + if (n > 0) + ret = fromList(n, state); + else + ret = null; + + if (util.isNull(ret)) { + state.needReadable = true; + n = 0; + } + + state.length -= n; + + // If we have nothing in the buffer, then we want to know + // as soon as we *do* get something into the buffer. + if (state.length === 0 && !state.ended) + state.needReadable = true; + + // If we tried to read() past the EOF, then emit end on the next tick. + if (nOrig !== n && state.ended && state.length === 0) + endReadable(this); + + if (!util.isNull(ret)) + this.emit('data', ret); + + return ret; +}; + +function chunkInvalid(state, chunk) { + var er = null; + if (!util.isBuffer(chunk) && + !util.isString(chunk) && + !util.isNullOrUndefined(chunk) && + !state.objectMode && + !er) { + er = new TypeError('Invalid non-string/buffer chunk'); + } + return er; +} + + +function onEofChunk(stream, state) { + if (state.decoder && !state.ended && state.decoder.end) { + var chunk = state.decoder.end(); + if (chunk && chunk.length) { + state.buffer.push(chunk); + state.length += state.objectMode ? 1 : chunk.length; + } + } + state.ended = true; + + // emit 'readable' now to make sure it gets picked up. + emitReadable(stream); +} + +// Don't emit readable right away in sync mode, because this can trigger +// another read() call => stack overflow. This way, it might trigger +// a nextTick recursion warning, but that's not so bad. +function emitReadable(stream) { + var state = stream._readableState; + state.needReadable = false; + if (!state.emittedReadable) { + debug('emitReadable', state.flowing); + state.emittedReadable = true; + if (state.sync) + process.nextTick(function() { + emitReadable_(stream); + }); + else + emitReadable_(stream); + } +} + +function emitReadable_(stream) { + debug('emit readable'); + stream.emit('readable'); + flow(stream); +} + + +// at this point, the user has presumably seen the 'readable' event, +// and called read() to consume some data. that may have triggered +// in turn another _read(n) call, in which case reading = true if +// it's in progress. +// However, if we're not ended, or reading, and the length < hwm, +// then go ahead and try to read some more preemptively. +function maybeReadMore(stream, state) { + if (!state.readingMore) { + state.readingMore = true; + process.nextTick(function() { + maybeReadMore_(stream, state); + }); + } +} + +function maybeReadMore_(stream, state) { + var len = state.length; + while (!state.reading && !state.flowing && !state.ended && + state.length < state.highWaterMark) { + debug('maybeReadMore read 0'); + stream.read(0); + if (len === state.length) + // didn't get any data, stop spinning. + break; + else + len = state.length; + } + state.readingMore = false; +} + +// abstract method. to be overridden in specific implementation classes. +// call cb(er, data) where data is <= n in length. +// for virtual (non-string, non-buffer) streams, "length" is somewhat +// arbitrary, and perhaps not very meaningful. +Readable.prototype._read = function(n) { + this.emit('error', new Error('not implemented')); +}; + +Readable.prototype.pipe = function(dest, pipeOpts) { + var src = this; + var state = this._readableState; + + switch (state.pipesCount) { + case 0: + state.pipes = dest; + break; + case 1: + state.pipes = [state.pipes, dest]; + break; + default: + state.pipes.push(dest); + break; + } + state.pipesCount += 1; + debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts); + + var doEnd = (!pipeOpts || pipeOpts.end !== false) && + dest !== process.stdout && + dest !== process.stderr; + + var endFn = doEnd ? onend : cleanup; + if (state.endEmitted) + process.nextTick(endFn); + else + src.once('end', endFn); + + dest.on('unpipe', onunpipe); + function onunpipe(readable) { + debug('onunpipe'); + if (readable === src) { + cleanup(); + } + } + + function onend() { + debug('onend'); + dest.end(); + } + + // when the dest drains, it reduces the awaitDrain counter + // on the source. This would be more elegant with a .once() + // handler in flow(), but adding and removing repeatedly is + // too slow. + var ondrain = pipeOnDrain(src); + dest.on('drain', ondrain); + + function cleanup() { + debug('cleanup'); + // cleanup event handlers once the pipe is broken + dest.removeListener('close', onclose); + dest.removeListener('finish', onfinish); + dest.removeListener('drain', ondrain); + dest.removeListener('error', onerror); + dest.removeListener('unpipe', onunpipe); + src.removeListener('end', onend); + src.removeListener('end', cleanup); + src.removeListener('data', ondata); + + // if the reader is waiting for a drain event from this + // specific writer, then it would cause it to never start + // flowing again. + // So, if this is awaiting a drain, then we just call it now. + // If we don't know, then assume that we are waiting for one. + if (state.awaitDrain && + (!dest._writableState || dest._writableState.needDrain)) + ondrain(); + } + + src.on('data', ondata); + function ondata(chunk) { + debug('ondata'); + var ret = dest.write(chunk); + if (false === ret) { + debug('false write response, pause', + src._readableState.awaitDrain); + src._readableState.awaitDrain++; + src.pause(); + } + } + + // if the dest has an error, then stop piping into it. + // however, don't suppress the throwing behavior for this. + function onerror(er) { + debug('onerror', er); + unpipe(); + dest.removeListener('error', onerror); + if (EE.listenerCount(dest, 'error') === 0) + dest.emit('error', er); + } + // This is a brutally ugly hack to make sure that our error handler + // is attached before any userland ones. NEVER DO THIS. + if (!dest._events.error) + dest.on('error', onerror); + else if (Array.isArray(dest._events.error)) + dest._events.error.unshift(onerror); + else + dest._events.error = [onerror, dest._events.error]; + + + + // Both close and finish should trigger unpipe, but only once. + function onclose() { + dest.removeListener('finish', onfinish); + unpipe(); + } + dest.once('close', onclose); + function onfinish() { + debug('onfinish'); + dest.removeListener('close', onclose); + unpipe(); + } + dest.once('finish', onfinish); + + function unpipe() { + debug('unpipe'); + src.unpipe(dest); + } + + // tell the dest that it's being piped to + dest.emit('pipe', src); + + // start the flow if it hasn't been started already. + if (!state.flowing) { + debug('pipe resume'); + src.resume(); + } + + return dest; +}; + +function pipeOnDrain(src) { + return function() { + var state = src._readableState; + debug('pipeOnDrain', state.awaitDrain); + if (state.awaitDrain) + state.awaitDrain--; + if (state.awaitDrain === 0 && EE.listenerCount(src, 'data')) { + state.flowing = true; + flow(src); + } + }; +} + + +Readable.prototype.unpipe = function(dest) { + var state = this._readableState; + + // if we're not piping anywhere, then do nothing. + if (state.pipesCount === 0) + return this; + + // just one destination. most common case. + if (state.pipesCount === 1) { + // passed in one, but it's not the right one. + if (dest && dest !== state.pipes) + return this; + + if (!dest) + dest = state.pipes; + + // got a match. + state.pipes = null; + state.pipesCount = 0; + state.flowing = false; + if (dest) + dest.emit('unpipe', this); + return this; + } + + // slow case. multiple pipe destinations. + + if (!dest) { + // remove all. + var dests = state.pipes; + var len = state.pipesCount; + state.pipes = null; + state.pipesCount = 0; + state.flowing = false; + + for (var i = 0; i < len; i++) + dests[i].emit('unpipe', this); + return this; + } + + // try to find the right one. + var i = state.pipes.indexOf(dest); + if (i === -1) + return this; + + state.pipes.splice(i, 1); + state.pipesCount -= 1; + if (state.pipesCount === 1) + state.pipes = state.pipes[0]; + + dest.emit('unpipe', this); + + return this; +}; + +// set up data events if they are asked for +// Ensure readable listeners eventually get something +Readable.prototype.on = function(ev, fn) { + var res = Stream.prototype.on.call(this, ev, fn); + + // If listening to data, and it has not explicitly been paused, + // then call resume to start the flow of data on the next tick. + if (ev === 'data' && false !== this._readableState.flowing) { + this.resume(); + } + + if (ev === 'readable' && this.readable) { + var state = this._readableState; + if (!state.readableListening) { + state.readableListening = true; + state.emittedReadable = false; + state.needReadable = true; + if (!state.reading) { + var self = this; + process.nextTick(function() { + debug('readable nexttick read 0'); + self.read(0); + }); + } else if (state.length) { + emitReadable(this, state); + } + } + } + + return res; +}; +Readable.prototype.addListener = Readable.prototype.on; + +// pause() and resume() are remnants of the legacy readable stream API +// If the user uses them, then switch into old mode. +Readable.prototype.resume = function() { + var state = this._readableState; + if (!state.flowing) { + debug('resume'); + state.flowing = true; + if (!state.reading) { + debug('resume read 0'); + this.read(0); + } + resume(this, state); + } +}; + +function resume(stream, state) { + if (!state.resumeScheduled) { + state.resumeScheduled = true; + process.nextTick(function() { + resume_(stream, state); + }); + } +} + +function resume_(stream, state) { + state.resumeScheduled = false; + stream.emit('resume'); + flow(stream); + if (state.flowing && !state.reading) + stream.read(0); +} + +Readable.prototype.pause = function() { + debug('call pause flowing=%j', this._readableState.flowing); + if (false !== this._readableState.flowing) { + debug('pause'); + this._readableState.flowing = false; + this.emit('pause'); + } +}; + +function flow(stream) { + var state = stream._readableState; + debug('flow', state.flowing); + if (state.flowing) { + do { + var chunk = stream.read(); + } while (null !== chunk && state.flowing); + } +} + +// wrap an old-style stream as the async data source. +// This is *not* part of the readable stream interface. +// It is an ugly unfortunate mess of history. +Readable.prototype.wrap = function(stream) { + var state = this._readableState; + var paused = false; + + var self = this; + stream.on('end', function() { + debug('wrapped end'); + if (state.decoder && !state.ended) { + var chunk = state.decoder.end(); + if (chunk && chunk.length) + self.push(chunk); + } + + self.push(null); + }); + + stream.on('data', function(chunk) { + debug('wrapped data'); + if (state.decoder) + chunk = state.decoder.write(chunk); + if (!chunk || !state.objectMode && !chunk.length) + return; + + var ret = self.push(chunk); + if (!ret) { + paused = true; + stream.pause(); + } + }); + + // proxy all the other methods. + // important when wrapping filters and duplexes. + for (var i in stream) { + if (util.isFunction(stream[i]) && util.isUndefined(this[i])) { + this[i] = function(method) { return function() { + return stream[method].apply(stream, arguments); + }}(i); + } + } + + // proxy certain important events. + var events = ['error', 'close', 'destroy', 'pause', 'resume']; + events.forEach(function(ev) { + stream.on(ev, self.emit.bind(self, ev)); + }); + + // when we try to consume some more bytes, simply unpause the + // underlying stream. + self._read = function(n) { + debug('wrapped _read', n); + if (paused) { + paused = false; + stream.resume(); + } + }; + + return self; +}; + + + +// exposed for testing purposes only. +Readable._fromList = fromList; + +// Pluck off n bytes from an array of buffers. +// Length is the combined lengths of all the buffers in the list. +function fromList(n, state) { + var list = state.buffer; + var length = state.length; + var stringMode = !!state.decoder; + var objectMode = !!state.objectMode; + var ret; + + // nothing in the list, definitely empty. + if (list.length === 0) + return null; + + if (length === 0) + ret = null; + else if (objectMode) + ret = list.shift(); + else if (!n || n >= length) { + // read it all, truncate the array. + if (stringMode) + ret = list.join(''); + else + ret = Buffer.concat(list, length); + list.length = 0; + } else { + // read just some of it. + if (n < list[0].length) { + // just take a part of the first list item. + // slice is the same for buffers and strings. + var buf = list[0]; + ret = buf.slice(0, n); + list[0] = buf.slice(n); + } else if (n === list[0].length) { + // first list is a perfect match + ret = list.shift(); + } else { + // complex case. + // we have enough to cover it, but it spans past the first buffer. + if (stringMode) + ret = ''; + else + ret = new Buffer(n); + + var c = 0; + for (var i = 0, l = list.length; i < l && c < n; i++) { + var buf = list[0]; + var cpy = Math.min(n - c, buf.length); + + if (stringMode) + ret += buf.slice(0, cpy); + else + buf.copy(ret, c, 0, cpy); + + if (cpy < buf.length) + list[0] = buf.slice(cpy); + else + list.shift(); + + c += cpy; + } + } + } + + return ret; +} + +function endReadable(stream) { + var state = stream._readableState; + + // If we get here before consuming all the bytes, then that is a + // bug in node. Should never happen. + if (state.length > 0) + throw new Error('endReadable called on non-empty stream'); + + if (!state.endEmitted) { + state.ended = true; + process.nextTick(function() { + // Check that we didn't get one last unshift. + if (!state.endEmitted && state.length === 0) { + state.endEmitted = true; + stream.readable = false; + stream.emit('end'); + } + }); + } +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/lib/_stream_transform.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/lib/_stream_transform.js new file mode 100644 index 0000000..b0caf57 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/lib/_stream_transform.js @@ -0,0 +1,210 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + +// a transform stream is a readable/writable stream where you do +// something with the data. Sometimes it's called a "filter", +// but that's not a great name for it, since that implies a thing where +// some bits pass through, and others are simply ignored. (That would +// be a valid example of a transform, of course.) +// +// While the output is causally related to the input, it's not a +// necessarily symmetric or synchronous transformation. For example, +// a zlib stream might take multiple plain-text writes(), and then +// emit a single compressed chunk some time in the future. +// +// Here's how this works: +// +// The Transform stream has all the aspects of the readable and writable +// stream classes. When you write(chunk), that calls _write(chunk,cb) +// internally, and returns false if there's a lot of pending writes +// buffered up. When you call read(), that calls _read(n) until +// there's enough pending readable data buffered up. +// +// In a transform stream, the written data is placed in a buffer. When +// _read(n) is called, it transforms the queued up data, calling the +// buffered _write cb's as it consumes chunks. If consuming a single +// written chunk would result in multiple output chunks, then the first +// outputted bit calls the readcb, and subsequent chunks just go into +// the read buffer, and will cause it to emit 'readable' if necessary. +// +// This way, back-pressure is actually determined by the reading side, +// since _read has to be called to start processing a new chunk. However, +// a pathological inflate type of transform can cause excessive buffering +// here. For example, imagine a stream where every byte of input is +// interpreted as an integer from 0-255, and then results in that many +// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in +// 1kb of data being output. In this case, you could write a very small +// amount of input, and end up with a very large amount of output. In +// such a pathological inflating mechanism, there'd be no way to tell +// the system to stop doing the transform. A single 4MB write could +// cause the system to run out of memory. +// +// However, even in such a pathological case, only a single written chunk +// would be consumed, and then the rest would wait (un-transformed) until +// the results of the previous transformed chunk were consumed. + +module.exports = Transform; + +var Duplex = require('./_stream_duplex'); +var util = require('util'); +if (!util.isUndefined) { + var utilIs = require('core-util-is'); + for (var f in utilIs) { + util[f] = utilIs[f]; + } +} +util.inherits(Transform, Duplex); + + +function TransformState(options, stream) { + this.afterTransform = function(er, data) { + return afterTransform(stream, er, data); + }; + + this.needTransform = false; + this.transforming = false; + this.writecb = null; + this.writechunk = null; +} + +function afterTransform(stream, er, data) { + var ts = stream._transformState; + ts.transforming = false; + + var cb = ts.writecb; + + if (!cb) + return stream.emit('error', new Error('no writecb in Transform class')); + + ts.writechunk = null; + ts.writecb = null; + + if (!util.isNullOrUndefined(data)) + stream.push(data); + + if (cb) + cb(er); + + var rs = stream._readableState; + rs.reading = false; + if (rs.needReadable || rs.length < rs.highWaterMark) { + stream._read(rs.highWaterMark); + } +} + + +function Transform(options) { + if (!(this instanceof Transform)) + return new Transform(options); + + Duplex.call(this, options); + + this._transformState = new TransformState(options, this); + + // when the writable side finishes, then flush out anything remaining. + var stream = this; + + // start out asking for a readable event once data is transformed. + this._readableState.needReadable = true; + + // we have implemented the _read method, and done the other things + // that Readable wants before the first _read call, so unset the + // sync guard flag. + this._readableState.sync = false; + + this.once('prefinish', function() { + if (util.isFunction(this._flush)) + this._flush(function(er) { + done(stream, er); + }); + else + done(stream); + }); +} + +Transform.prototype.push = function(chunk, encoding) { + this._transformState.needTransform = false; + return Duplex.prototype.push.call(this, chunk, encoding); +}; + +// This is the part where you do stuff! +// override this function in implementation classes. +// 'chunk' is an input chunk. +// +// Call `push(newChunk)` to pass along transformed output +// to the readable side. You may call 'push' zero or more times. +// +// Call `cb(err)` when you are done with this chunk. If you pass +// an error, then that'll put the hurt on the whole operation. If you +// never call cb(), then you'll never get another chunk. +Transform.prototype._transform = function(chunk, encoding, cb) { + throw new Error('not implemented'); +}; + +Transform.prototype._write = function(chunk, encoding, cb) { + var ts = this._transformState; + ts.writecb = cb; + ts.writechunk = chunk; + ts.writeencoding = encoding; + if (!ts.transforming) { + var rs = this._readableState; + if (ts.needTransform || + rs.needReadable || + rs.length < rs.highWaterMark) + this._read(rs.highWaterMark); + } +}; + +// Doesn't matter what the args are here. +// _transform does all the work. +// That we got here means that the readable side wants more data. +Transform.prototype._read = function(n) { + var ts = this._transformState; + + if (!util.isNull(ts.writechunk) && ts.writecb && !ts.transforming) { + ts.transforming = true; + this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform); + } else { + // mark that we need a transform, so that any data that comes in + // will get processed, now that we've asked for it. + ts.needTransform = true; + } +}; + + +function done(stream, er) { + if (er) + return stream.emit('error', er); + + // if there's nothing in the write buffer, then that means + // that nothing more will ever be provided + var ws = stream._writableState; + var ts = stream._transformState; + + if (ws.length) + throw new Error('calling transform done when ws.length != 0'); + + if (ts.transforming) + throw new Error('calling transform done when still transforming'); + + return stream.push(null); +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/lib/_stream_writable.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/lib/_stream_writable.js new file mode 100644 index 0000000..f49288b --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/lib/_stream_writable.js @@ -0,0 +1,459 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// A bit simpler than readable streams. +// Implement an async ._write(chunk, cb), and it'll handle all +// the drain event emission and buffering. + +module.exports = Writable; +Writable.WritableState = WritableState; + +var util = require('util'); +if (!util.isUndefined) { + var utilIs = require('core-util-is'); + for (var f in utilIs) { + util[f] = utilIs[f]; + } +} +var Stream = require('stream'); + +util.inherits(Writable, Stream); + +function WriteReq(chunk, encoding, cb) { + this.chunk = chunk; + this.encoding = encoding; + this.callback = cb; +} + +function WritableState(options, stream) { + options = options || {}; + + // the point at which write() starts returning false + // Note: 0 is a valid value, means that we always return false if + // the entire buffer is not flushed immediately on write() + var hwm = options.highWaterMark; + this.highWaterMark = (hwm || hwm === 0) ? hwm : 16 * 1024; + + // object stream flag to indicate whether or not this stream + // contains buffers or objects. + this.objectMode = !!options.objectMode; + + // cast to ints. + this.highWaterMark = ~~this.highWaterMark; + + this.needDrain = false; + // at the start of calling end() + this.ending = false; + // when end() has been called, and returned + this.ended = false; + // when 'finish' is emitted + this.finished = false; + + // should we decode strings into buffers before passing to _write? + // this is here so that some node-core streams can optimize string + // handling at a lower level. + var noDecode = options.decodeStrings === false; + this.decodeStrings = !noDecode; + + // Crypto is kind of old and crusty. Historically, its default string + // encoding is 'binary' so we have to make this configurable. + // Everything else in the universe uses 'utf8', though. + this.defaultEncoding = options.defaultEncoding || 'utf8'; + + // not an actual buffer we keep track of, but a measurement + // of how much we're waiting to get pushed to some underlying + // socket or file. + this.length = 0; + + // a flag to see when we're in the middle of a write. + this.writing = false; + + // when true all writes will be buffered until .uncork() call + this.corked = 0; + + // a flag to be able to tell if the onwrite cb is called immediately, + // or on a later tick. We set this to true at first, because any + // actions that shouldn't happen until "later" should generally also + // not happen before the first write call. + this.sync = true; + + // a flag to know if we're processing previously buffered items, which + // may call the _write() callback in the same tick, so that we don't + // end up in an overlapped onwrite situation. + this.bufferProcessing = false; + + // the callback that's passed to _write(chunk,cb) + this.onwrite = function(er) { + onwrite(stream, er); + }; + + // the callback that the user supplies to write(chunk,encoding,cb) + this.writecb = null; + + // the amount that is being written when _write is called. + this.writelen = 0; + + this.buffer = []; + + // number of pending user-supplied write callbacks + // this must be 0 before 'finish' can be emitted + this.pendingcb = 0; + + // emit prefinish if the only thing we're waiting for is _write cbs + // This is relevant for synchronous Transform streams + this.prefinished = false; +} + +function Writable(options) { + // Writable ctor is applied to Duplexes, though they're not + // instanceof Writable, they're instanceof Readable. + if (!(this instanceof Writable) && !(this instanceof require('./_stream_duplex'))) + return new Writable(options); + + this._writableState = new WritableState(options, this); + + // legacy. + this.writable = true; + + Stream.call(this); +} + +// Otherwise people can pipe Writable streams, which is just wrong. +Writable.prototype.pipe = function() { + this.emit('error', new Error('Cannot pipe. Not readable.')); +}; + + +function writeAfterEnd(stream, state, cb) { + var er = new Error('write after end'); + // TODO: defer error events consistently everywhere, not just the cb + stream.emit('error', er); + process.nextTick(function() { + cb(er); + }); +} + +// If we get something that is not a buffer, string, null, or undefined, +// and we're not in objectMode, then that's an error. +// Otherwise stream chunks are all considered to be of length=1, and the +// watermarks determine how many objects to keep in the buffer, rather than +// how many bytes or characters. +function validChunk(stream, state, chunk, cb) { + var valid = true; + if (!util.isBuffer(chunk) && + !util.isString(chunk) && + !util.isNullOrUndefined(chunk) && + !state.objectMode) { + var er = new TypeError('Invalid non-string/buffer chunk'); + stream.emit('error', er); + process.nextTick(function() { + cb(er); + }); + valid = false; + } + return valid; +} + +Writable.prototype.write = function(chunk, encoding, cb) { + var state = this._writableState; + var ret = false; + + if (util.isFunction(encoding)) { + cb = encoding; + encoding = null; + } + + if (util.isBuffer(chunk)) + encoding = 'buffer'; + else if (!encoding) + encoding = state.defaultEncoding; + + if (!util.isFunction(cb)) + cb = function() {}; + + if (state.ended) + writeAfterEnd(this, state, cb); + else if (validChunk(this, state, chunk, cb)) { + state.pendingcb++; + ret = writeOrBuffer(this, state, chunk, encoding, cb); + } + + return ret; +}; + +Writable.prototype.cork = function() { + var state = this._writableState; + + state.corked++; +}; + +Writable.prototype.uncork = function() { + var state = this._writableState; + + if (state.corked) { + state.corked--; + + if (!state.writing && + !state.corked && + !state.finished && + !state.bufferProcessing && + state.buffer.length) + clearBuffer(this, state); + } +}; + +function decodeChunk(state, chunk, encoding) { + if (!state.objectMode && + state.decodeStrings !== false && + util.isString(chunk)) { + chunk = new Buffer(chunk, encoding); + } + return chunk; +} + +// if we're already writing something, then just put this +// in the queue, and wait our turn. Otherwise, call _write +// If we return false, then we need a drain event, so set that flag. +function writeOrBuffer(stream, state, chunk, encoding, cb) { + chunk = decodeChunk(state, chunk, encoding); + if (util.isBuffer(chunk)) + encoding = 'buffer'; + var len = state.objectMode ? 1 : chunk.length; + + state.length += len; + + var ret = state.length < state.highWaterMark; + state.needDrain = !ret; + + if (state.writing || state.corked) + state.buffer.push(new WriteReq(chunk, encoding, cb)); + else + doWrite(stream, state, false, len, chunk, encoding, cb); + + return ret; +} + +function doWrite(stream, state, writev, len, chunk, encoding, cb) { + state.writelen = len; + state.writecb = cb; + state.writing = true; + state.sync = true; + if (writev) + stream._writev(chunk, state.onwrite); + else + stream._write(chunk, encoding, state.onwrite); + state.sync = false; +} + +function onwriteError(stream, state, sync, er, cb) { + if (sync) + process.nextTick(function() { + state.pendingcb--; + cb(er); + }); + else { + state.pendingcb--; + cb(er); + } + + stream.emit('error', er); +} + +function onwriteStateUpdate(state) { + state.writing = false; + state.writecb = null; + state.length -= state.writelen; + state.writelen = 0; +} + +function onwrite(stream, er) { + var state = stream._writableState; + var sync = state.sync; + var cb = state.writecb; + + onwriteStateUpdate(state); + + if (er) + onwriteError(stream, state, sync, er, cb); + else { + // Check if we're actually ready to finish, but don't emit yet + var finished = needFinish(stream, state); + + if (!finished && + !state.corked && + !state.bufferProcessing && + state.buffer.length) { + clearBuffer(stream, state); + } + + if (sync) { + process.nextTick(function() { + afterWrite(stream, state, finished, cb); + }); + } else { + afterWrite(stream, state, finished, cb); + } + } +} + +function afterWrite(stream, state, finished, cb) { + if (!finished) + onwriteDrain(stream, state); + state.pendingcb--; + cb(); + finishMaybe(stream, state); +} + +// Must force callback to be called on nextTick, so that we don't +// emit 'drain' before the write() consumer gets the 'false' return +// value, and has a chance to attach a 'drain' listener. +function onwriteDrain(stream, state) { + if (state.length === 0 && state.needDrain) { + state.needDrain = false; + stream.emit('drain'); + } +} + + +// if there's something in the buffer waiting, then process it +function clearBuffer(stream, state) { + state.bufferProcessing = true; + + if (stream._writev && state.buffer.length > 1) { + // Fast case, write everything using _writev() + var cbs = []; + for (var c = 0; c < state.buffer.length; c++) + cbs.push(state.buffer[c].callback); + + // count the one we are adding, as well. + // TODO(isaacs) clean this up + state.pendingcb++; + doWrite(stream, state, true, state.length, state.buffer, '', function(err) { + for (var i = 0; i < cbs.length; i++) { + state.pendingcb--; + cbs[i](err); + } + }); + + // Clear buffer + state.buffer = []; + } else { + // Slow case, write chunks one-by-one + for (var c = 0; c < state.buffer.length; c++) { + var entry = state.buffer[c]; + var chunk = entry.chunk; + var encoding = entry.encoding; + var cb = entry.callback; + var len = state.objectMode ? 1 : chunk.length; + + doWrite(stream, state, false, len, chunk, encoding, cb); + + // if we didn't call the onwrite immediately, then + // it means that we need to wait until it does. + // also, that means that the chunk and cb are currently + // being processed, so move the buffer counter past them. + if (state.writing) { + c++; + break; + } + } + + if (c < state.buffer.length) + state.buffer = state.buffer.slice(c); + else + state.buffer.length = 0; + } + + state.bufferProcessing = false; +} + +Writable.prototype._write = function(chunk, encoding, cb) { + cb(new Error('not implemented')); + +}; + +Writable.prototype._writev = null; + +Writable.prototype.end = function(chunk, encoding, cb) { + var state = this._writableState; + + if (util.isFunction(chunk)) { + cb = chunk; + chunk = null; + encoding = null; + } else if (util.isFunction(encoding)) { + cb = encoding; + encoding = null; + } + + if (!util.isNullOrUndefined(chunk)) + this.write(chunk, encoding); + + // .end() fully uncorks + if (state.corked) { + state.corked = 1; + this.uncork(); + } + + // ignore unnecessary end() calls. + if (!state.ending && !state.finished) + endWritable(this, state, cb); +}; + + +function needFinish(stream, state) { + return (state.ending && + state.length === 0 && + !state.finished && + !state.writing); +} + +function prefinish(stream, state) { + if (!state.prefinished) { + state.prefinished = true; + stream.emit('prefinish'); + } +} + +function finishMaybe(stream, state) { + var need = needFinish(stream, state); + if (need) { + if (state.pendingcb === 0) { + prefinish(stream, state); + state.finished = true; + stream.emit('finish'); + } else + prefinish(stream, state); + } + return need; +} + +function endWritable(stream, state, cb) { + state.ending = true; + finishMaybe(stream, state); + if (cb) { + if (state.finished) + process.nextTick(cb); + else + stream.once('finish', cb); + } + state.ended = true; +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/core-util-is/README.md b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/core-util-is/README.md new file mode 100644 index 0000000..5a76b41 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/core-util-is/README.md @@ -0,0 +1,3 @@ +# core-util-is + +The `util.is*` functions introduced in Node v0.12. diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/core-util-is/float.patch b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/core-util-is/float.patch new file mode 100644 index 0000000..ee596bf --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/core-util-is/float.patch @@ -0,0 +1,590 @@ +diff --git a/lib/util.js b/lib/util.js +index 9901a66..007fa10 100644 +--- a/lib/util.js ++++ b/lib/util.js +@@ -19,425 +19,6 @@ + // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + // USE OR OTHER DEALINGS IN THE SOFTWARE. + +-var formatRegExp = /%[sdj%]/g; +-exports.format = function(f) { +- if (!isString(f)) { +- var objects = []; +- for (var i = 0; i < arguments.length; i++) { +- objects.push(inspect(arguments[i])); +- } +- return objects.join(' '); +- } +- +- var i = 1; +- var args = arguments; +- var len = args.length; +- var str = String(f).replace(formatRegExp, function(x) { +- if (x === '%%') return '%'; +- if (i >= len) return x; +- switch (x) { +- case '%s': return String(args[i++]); +- case '%d': return Number(args[i++]); +- case '%j': +- try { +- return JSON.stringify(args[i++]); +- } catch (_) { +- return '[Circular]'; +- } +- default: +- return x; +- } +- }); +- for (var x = args[i]; i < len; x = args[++i]) { +- if (isNull(x) || !isObject(x)) { +- str += ' ' + x; +- } else { +- str += ' ' + inspect(x); +- } +- } +- return str; +-}; +- +- +-// Mark that a method should not be used. +-// Returns a modified function which warns once by default. +-// If --no-deprecation is set, then it is a no-op. +-exports.deprecate = function(fn, msg) { +- // Allow for deprecating things in the process of starting up. +- if (isUndefined(global.process)) { +- return function() { +- return exports.deprecate(fn, msg).apply(this, arguments); +- }; +- } +- +- if (process.noDeprecation === true) { +- return fn; +- } +- +- var warned = false; +- function deprecated() { +- if (!warned) { +- if (process.throwDeprecation) { +- throw new Error(msg); +- } else if (process.traceDeprecation) { +- console.trace(msg); +- } else { +- console.error(msg); +- } +- warned = true; +- } +- return fn.apply(this, arguments); +- } +- +- return deprecated; +-}; +- +- +-var debugs = {}; +-var debugEnviron; +-exports.debuglog = function(set) { +- if (isUndefined(debugEnviron)) +- debugEnviron = process.env.NODE_DEBUG || ''; +- set = set.toUpperCase(); +- if (!debugs[set]) { +- if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { +- var pid = process.pid; +- debugs[set] = function() { +- var msg = exports.format.apply(exports, arguments); +- console.error('%s %d: %s', set, pid, msg); +- }; +- } else { +- debugs[set] = function() {}; +- } +- } +- return debugs[set]; +-}; +- +- +-/** +- * Echos the value of a value. Trys to print the value out +- * in the best way possible given the different types. +- * +- * @param {Object} obj The object to print out. +- * @param {Object} opts Optional options object that alters the output. +- */ +-/* legacy: obj, showHidden, depth, colors*/ +-function inspect(obj, opts) { +- // default options +- var ctx = { +- seen: [], +- stylize: stylizeNoColor +- }; +- // legacy... +- if (arguments.length >= 3) ctx.depth = arguments[2]; +- if (arguments.length >= 4) ctx.colors = arguments[3]; +- if (isBoolean(opts)) { +- // legacy... +- ctx.showHidden = opts; +- } else if (opts) { +- // got an "options" object +- exports._extend(ctx, opts); +- } +- // set default options +- if (isUndefined(ctx.showHidden)) ctx.showHidden = false; +- if (isUndefined(ctx.depth)) ctx.depth = 2; +- if (isUndefined(ctx.colors)) ctx.colors = false; +- if (isUndefined(ctx.customInspect)) ctx.customInspect = true; +- if (ctx.colors) ctx.stylize = stylizeWithColor; +- return formatValue(ctx, obj, ctx.depth); +-} +-exports.inspect = inspect; +- +- +-// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics +-inspect.colors = { +- 'bold' : [1, 22], +- 'italic' : [3, 23], +- 'underline' : [4, 24], +- 'inverse' : [7, 27], +- 'white' : [37, 39], +- 'grey' : [90, 39], +- 'black' : [30, 39], +- 'blue' : [34, 39], +- 'cyan' : [36, 39], +- 'green' : [32, 39], +- 'magenta' : [35, 39], +- 'red' : [31, 39], +- 'yellow' : [33, 39] +-}; +- +-// Don't use 'blue' not visible on cmd.exe +-inspect.styles = { +- 'special': 'cyan', +- 'number': 'yellow', +- 'boolean': 'yellow', +- 'undefined': 'grey', +- 'null': 'bold', +- 'string': 'green', +- 'date': 'magenta', +- // "name": intentionally not styling +- 'regexp': 'red' +-}; +- +- +-function stylizeWithColor(str, styleType) { +- var style = inspect.styles[styleType]; +- +- if (style) { +- return '\u001b[' + inspect.colors[style][0] + 'm' + str + +- '\u001b[' + inspect.colors[style][1] + 'm'; +- } else { +- return str; +- } +-} +- +- +-function stylizeNoColor(str, styleType) { +- return str; +-} +- +- +-function arrayToHash(array) { +- var hash = {}; +- +- array.forEach(function(val, idx) { +- hash[val] = true; +- }); +- +- return hash; +-} +- +- +-function formatValue(ctx, value, recurseTimes) { +- // Provide a hook for user-specified inspect functions. +- // Check that value is an object with an inspect function on it +- if (ctx.customInspect && +- value && +- isFunction(value.inspect) && +- // Filter out the util module, it's inspect function is special +- value.inspect !== exports.inspect && +- // Also filter out any prototype objects using the circular check. +- !(value.constructor && value.constructor.prototype === value)) { +- var ret = value.inspect(recurseTimes, ctx); +- if (!isString(ret)) { +- ret = formatValue(ctx, ret, recurseTimes); +- } +- return ret; +- } +- +- // Primitive types cannot have properties +- var primitive = formatPrimitive(ctx, value); +- if (primitive) { +- return primitive; +- } +- +- // Look up the keys of the object. +- var keys = Object.keys(value); +- var visibleKeys = arrayToHash(keys); +- +- if (ctx.showHidden) { +- keys = Object.getOwnPropertyNames(value); +- } +- +- // Some type of object without properties can be shortcutted. +- if (keys.length === 0) { +- if (isFunction(value)) { +- var name = value.name ? ': ' + value.name : ''; +- return ctx.stylize('[Function' + name + ']', 'special'); +- } +- if (isRegExp(value)) { +- return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); +- } +- if (isDate(value)) { +- return ctx.stylize(Date.prototype.toString.call(value), 'date'); +- } +- if (isError(value)) { +- return formatError(value); +- } +- } +- +- var base = '', array = false, braces = ['{', '}']; +- +- // Make Array say that they are Array +- if (isArray(value)) { +- array = true; +- braces = ['[', ']']; +- } +- +- // Make functions say that they are functions +- if (isFunction(value)) { +- var n = value.name ? ': ' + value.name : ''; +- base = ' [Function' + n + ']'; +- } +- +- // Make RegExps say that they are RegExps +- if (isRegExp(value)) { +- base = ' ' + RegExp.prototype.toString.call(value); +- } +- +- // Make dates with properties first say the date +- if (isDate(value)) { +- base = ' ' + Date.prototype.toUTCString.call(value); +- } +- +- // Make error with message first say the error +- if (isError(value)) { +- base = ' ' + formatError(value); +- } +- +- if (keys.length === 0 && (!array || value.length == 0)) { +- return braces[0] + base + braces[1]; +- } +- +- if (recurseTimes < 0) { +- if (isRegExp(value)) { +- return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); +- } else { +- return ctx.stylize('[Object]', 'special'); +- } +- } +- +- ctx.seen.push(value); +- +- var output; +- if (array) { +- output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); +- } else { +- output = keys.map(function(key) { +- return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); +- }); +- } +- +- ctx.seen.pop(); +- +- return reduceToSingleString(output, base, braces); +-} +- +- +-function formatPrimitive(ctx, value) { +- if (isUndefined(value)) +- return ctx.stylize('undefined', 'undefined'); +- if (isString(value)) { +- var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') +- .replace(/'/g, "\\'") +- .replace(/\\"/g, '"') + '\''; +- return ctx.stylize(simple, 'string'); +- } +- if (isNumber(value)) +- return ctx.stylize('' + value, 'number'); +- if (isBoolean(value)) +- return ctx.stylize('' + value, 'boolean'); +- // For some reason typeof null is "object", so special case here. +- if (isNull(value)) +- return ctx.stylize('null', 'null'); +-} +- +- +-function formatError(value) { +- return '[' + Error.prototype.toString.call(value) + ']'; +-} +- +- +-function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { +- var output = []; +- for (var i = 0, l = value.length; i < l; ++i) { +- if (hasOwnProperty(value, String(i))) { +- output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, +- String(i), true)); +- } else { +- output.push(''); +- } +- } +- keys.forEach(function(key) { +- if (!key.match(/^\d+$/)) { +- output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, +- key, true)); +- } +- }); +- return output; +-} +- +- +-function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { +- var name, str, desc; +- desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; +- if (desc.get) { +- if (desc.set) { +- str = ctx.stylize('[Getter/Setter]', 'special'); +- } else { +- str = ctx.stylize('[Getter]', 'special'); +- } +- } else { +- if (desc.set) { +- str = ctx.stylize('[Setter]', 'special'); +- } +- } +- if (!hasOwnProperty(visibleKeys, key)) { +- name = '[' + key + ']'; +- } +- if (!str) { +- if (ctx.seen.indexOf(desc.value) < 0) { +- if (isNull(recurseTimes)) { +- str = formatValue(ctx, desc.value, null); +- } else { +- str = formatValue(ctx, desc.value, recurseTimes - 1); +- } +- if (str.indexOf('\n') > -1) { +- if (array) { +- str = str.split('\n').map(function(line) { +- return ' ' + line; +- }).join('\n').substr(2); +- } else { +- str = '\n' + str.split('\n').map(function(line) { +- return ' ' + line; +- }).join('\n'); +- } +- } +- } else { +- str = ctx.stylize('[Circular]', 'special'); +- } +- } +- if (isUndefined(name)) { +- if (array && key.match(/^\d+$/)) { +- return str; +- } +- name = JSON.stringify('' + key); +- if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { +- name = name.substr(1, name.length - 2); +- name = ctx.stylize(name, 'name'); +- } else { +- name = name.replace(/'/g, "\\'") +- .replace(/\\"/g, '"') +- .replace(/(^"|"$)/g, "'"); +- name = ctx.stylize(name, 'string'); +- } +- } +- +- return name + ': ' + str; +-} +- +- +-function reduceToSingleString(output, base, braces) { +- var numLinesEst = 0; +- var length = output.reduce(function(prev, cur) { +- numLinesEst++; +- if (cur.indexOf('\n') >= 0) numLinesEst++; +- return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; +- }, 0); +- +- if (length > 60) { +- return braces[0] + +- (base === '' ? '' : base + '\n ') + +- ' ' + +- output.join(',\n ') + +- ' ' + +- braces[1]; +- } +- +- return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +-} +- +- + // NOTE: These type checking functions intentionally don't use `instanceof` + // because it is fragile and can be easily faked with `Object.create()`. + function isArray(ar) { +@@ -523,159 +104,3 @@ exports.isBuffer = isBuffer; + function objectToString(o) { + return Object.prototype.toString.call(o); + } +- +- +-function pad(n) { +- return n < 10 ? '0' + n.toString(10) : n.toString(10); +-} +- +- +-var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', +- 'Oct', 'Nov', 'Dec']; +- +-// 26 Feb 16:19:34 +-function timestamp() { +- var d = new Date(); +- var time = [pad(d.getHours()), +- pad(d.getMinutes()), +- pad(d.getSeconds())].join(':'); +- return [d.getDate(), months[d.getMonth()], time].join(' '); +-} +- +- +-// log is just a thin wrapper to console.log that prepends a timestamp +-exports.log = function() { +- console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); +-}; +- +- +-/** +- * Inherit the prototype methods from one constructor into another. +- * +- * The Function.prototype.inherits from lang.js rewritten as a standalone +- * function (not on Function.prototype). NOTE: If this file is to be loaded +- * during bootstrapping this function needs to be rewritten using some native +- * functions as prototype setup using normal JavaScript does not work as +- * expected during bootstrapping (see mirror.js in r114903). +- * +- * @param {function} ctor Constructor function which needs to inherit the +- * prototype. +- * @param {function} superCtor Constructor function to inherit prototype from. +- */ +-exports.inherits = function(ctor, superCtor) { +- ctor.super_ = superCtor; +- ctor.prototype = Object.create(superCtor.prototype, { +- constructor: { +- value: ctor, +- enumerable: false, +- writable: true, +- configurable: true +- } +- }); +-}; +- +-exports._extend = function(origin, add) { +- // Don't do anything if add isn't an object +- if (!add || !isObject(add)) return origin; +- +- var keys = Object.keys(add); +- var i = keys.length; +- while (i--) { +- origin[keys[i]] = add[keys[i]]; +- } +- return origin; +-}; +- +-function hasOwnProperty(obj, prop) { +- return Object.prototype.hasOwnProperty.call(obj, prop); +-} +- +- +-// Deprecated old stuff. +- +-exports.p = exports.deprecate(function() { +- for (var i = 0, len = arguments.length; i < len; ++i) { +- console.error(exports.inspect(arguments[i])); +- } +-}, 'util.p: Use console.error() instead'); +- +- +-exports.exec = exports.deprecate(function() { +- return require('child_process').exec.apply(this, arguments); +-}, 'util.exec is now called `child_process.exec`.'); +- +- +-exports.print = exports.deprecate(function() { +- for (var i = 0, len = arguments.length; i < len; ++i) { +- process.stdout.write(String(arguments[i])); +- } +-}, 'util.print: Use console.log instead'); +- +- +-exports.puts = exports.deprecate(function() { +- for (var i = 0, len = arguments.length; i < len; ++i) { +- process.stdout.write(arguments[i] + '\n'); +- } +-}, 'util.puts: Use console.log instead'); +- +- +-exports.debug = exports.deprecate(function(x) { +- process.stderr.write('DEBUG: ' + x + '\n'); +-}, 'util.debug: Use console.error instead'); +- +- +-exports.error = exports.deprecate(function(x) { +- for (var i = 0, len = arguments.length; i < len; ++i) { +- process.stderr.write(arguments[i] + '\n'); +- } +-}, 'util.error: Use console.error instead'); +- +- +-exports.pump = exports.deprecate(function(readStream, writeStream, callback) { +- var callbackCalled = false; +- +- function call(a, b, c) { +- if (callback && !callbackCalled) { +- callback(a, b, c); +- callbackCalled = true; +- } +- } +- +- readStream.addListener('data', function(chunk) { +- if (writeStream.write(chunk) === false) readStream.pause(); +- }); +- +- writeStream.addListener('drain', function() { +- readStream.resume(); +- }); +- +- readStream.addListener('end', function() { +- writeStream.end(); +- }); +- +- readStream.addListener('close', function() { +- call(); +- }); +- +- readStream.addListener('error', function(err) { +- writeStream.end(); +- call(err); +- }); +- +- writeStream.addListener('error', function(err) { +- readStream.destroy(); +- call(err); +- }); +-}, 'util.pump(): Use readableStream.pipe() instead'); +- +- +-var uv; +-exports._errnoException = function(err, syscall) { +- if (isUndefined(uv)) uv = process.binding('uv'); +- var errname = uv.errname(err); +- var e = new Error(syscall + ' ' + errname); +- e.code = errname; +- e.errno = errname; +- e.syscall = syscall; +- return e; +-}; diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/core-util-is/lib/util.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/core-util-is/lib/util.js new file mode 100644 index 0000000..007fa10 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/core-util-is/lib/util.js @@ -0,0 +1,106 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. +function isArray(ar) { + return Array.isArray(ar); +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return isObject(re) && objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isDate(d) { + return isObject(d) && objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return isObject(e) && objectToString(e) === '[object Error]'; +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +function isBuffer(arg) { + return arg instanceof Buffer; +} +exports.isBuffer = isBuffer; + +function objectToString(o) { + return Object.prototype.toString.call(o); +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/core-util-is/package.json b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/core-util-is/package.json new file mode 100644 index 0000000..240a5a6 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/core-util-is/package.json @@ -0,0 +1,39 @@ +{ + "name": "core-util-is", + "version": "1.0.0", + "description": "The `util.is*` functions introduced in Node v0.12.", + "main": "lib/util.js", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/core-util-is" + }, + "keywords": [ + "util", + "isBuffer", + "isArray", + "isNumber", + "isString", + "isRegExp", + "isThis", + "isThat", + "polyfill" + ], + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/isaacs/core-util-is/issues" + }, + "readme": "# core-util-is\n\nThe `util.is*` functions introduced in Node v0.12.\n", + "readmeFilename": "README.md", + "homepage": "https://github.com/isaacs/core-util-is", + "_id": "core-util-is@1.0.0", + "dist": { + "shasum": "aac69629561446a4acaceaa00df8a9eb09fd68fc" + }, + "_from": "core-util-is@~1.0.0", + "_resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.0.tgz" +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/core-util-is/util.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/core-util-is/util.js new file mode 100644 index 0000000..007fa10 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/core-util-is/util.js @@ -0,0 +1,106 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. +function isArray(ar) { + return Array.isArray(ar); +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return isObject(re) && objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isDate(d) { + return isObject(d) && objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return isObject(e) && objectToString(e) === '[object Error]'; +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +function isBuffer(arg) { + return arg instanceof Buffer; +} +exports.isBuffer = isBuffer; + +function objectToString(o) { + return Object.prototype.toString.call(o); +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/debuglog/LICENSE b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/debuglog/LICENSE new file mode 100644 index 0000000..a3187cc --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/debuglog/LICENSE @@ -0,0 +1,19 @@ +Copyright Joyent, Inc. and other Node contributors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/debuglog/README.md b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/debuglog/README.md new file mode 100644 index 0000000..dc6fcce --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/debuglog/README.md @@ -0,0 +1,40 @@ +# debuglog - backport of util.debuglog() from node v0.11 + +To facilitate using the `util.debuglog()` function that will be available when +node v0.12 is released now, this is a copy extracted from the source. + +## require('debuglog') + +Return `util.debuglog`, if it exists, otherwise it will return an internal copy +of the implementation from node v0.11. + +## debuglog(section) + +* `section` {String} The section of the program to be debugged +* Returns: {Function} The logging function + +This is used to create a function which conditionally writes to stderr +based on the existence of a `NODE_DEBUG` environment variable. If the +`section` name appears in that environment variable, then the returned +function will be similar to `console.error()`. If not, then the +returned function is a no-op. + +For example: + +```javascript +var debuglog = util.debuglog('foo'); + +var bar = 123; +debuglog('hello from foo [%d]', bar); +``` + +If this program is run with `NODE_DEBUG=foo` in the environment, then +it will output something like: + + FOO 3245: hello from foo [123] + +where `3245` is the process id. If it is not run with that +environment variable set, then it will not print anything. + +You may separate multiple `NODE_DEBUG` environment variables with a +comma. For example, `NODE_DEBUG=fs,net,tls`. diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/debuglog/debuglog.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/debuglog/debuglog.js new file mode 100644 index 0000000..da465c2 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/debuglog/debuglog.js @@ -0,0 +1,22 @@ +var util = require('util'); + +module.exports = util.debuglog || debuglog; + +var debugs = {}; +var debugEnviron = process.env.NODE_DEBUG || ''; + +function debuglog(set) { + set = set.toUpperCase(); + if (!debugs[set]) { + if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { + var pid = process.pid; + debugs[set] = function() { + var msg = util.format.apply(exports, arguments); + console.error('%s %d: %s', set, pid, msg); + }; + } else { + debugs[set] = function() {}; + } + } + return debugs[set]; +}; diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/debuglog/package.json b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/debuglog/package.json new file mode 100644 index 0000000..b066e65 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/node_modules/debuglog/package.json @@ -0,0 +1,30 @@ +{ + "name": "debuglog", + "version": "0.0.2", + "description": "backport of util.debuglog from node v0.11", + "license": "MIT", + "main": "debuglog.js", + "repository": { + "type": "git", + "url": "https://github.com/sam-github/node-debuglog.git" + }, + "author": { + "name": "Sam Roberts", + "email": "sam@strongloop.com" + }, + "engines": { + "node": "*" + }, + "readme": "# debuglog - backport of util.debuglog() from node v0.11\n\nTo facilitate using the `util.debuglog()` function that will be available when\nnode v0.12 is released now, this is a copy extracted from the source.\n\n## require('debuglog')\n\nReturn `util.debuglog`, if it exists, otherwise it will return an internal copy\nof the implementation from node v0.11.\n\n## debuglog(section)\n\n* `section` {String} The section of the program to be debugged\n* Returns: {Function} The logging function\n\nThis is used to create a function which conditionally writes to stderr\nbased on the existence of a `NODE_DEBUG` environment variable. If the\n`section` name appears in that environment variable, then the returned\nfunction will be similar to `console.error()`. If not, then the\nreturned function is a no-op.\n\nFor example:\n\n```javascript\nvar debuglog = util.debuglog('foo');\n\nvar bar = 123;\ndebuglog('hello from foo [%d]', bar);\n```\n\nIf this program is run with `NODE_DEBUG=foo` in the environment, then\nit will output something like:\n\n FOO 3245: hello from foo [123]\n\nwhere `3245` is the process id. If it is not run with that\nenvironment variable set, then it will not print anything.\n\nYou may separate multiple `NODE_DEBUG` environment variables with a\ncomma. For example, `NODE_DEBUG=fs,net,tls`.\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/sam-github/node-debuglog/issues" + }, + "homepage": "https://github.com/sam-github/node-debuglog", + "_id": "debuglog@0.0.2", + "dist": { + "shasum": "e37a3ed5b502850c757895e5171ab592b47ae5ee" + }, + "_from": "debuglog@0.0.2", + "_resolved": "https://registry.npmjs.org/debuglog/-/debuglog-0.0.2.tgz" +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/package.json b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/package.json new file mode 100644 index 0000000..1185a4b --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/package.json @@ -0,0 +1,46 @@ +{ + "name": "readable-stream", + "version": "1.1.9", + "description": "An exploration of a new kind of readable streams for Node.js", + "main": "readable.js", + "dependencies": { + "core-util-is": "~1.0.0", + "debuglog": "0.0.2" + }, + "devDependencies": { + "tap": "~0.2.6" + }, + "scripts": { + "test": "tap test/simple/*.js" + }, + "repository": { + "type": "git", + "url": "git://github.com/isaacs/readable-stream" + }, + "keywords": [ + "readable", + "stream", + "pipe" + ], + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, + "license": "MIT", + "optionalDependencies": { + "debuglog": "0.0.2" + }, + "readme": "# readable-stream\n\nA new class of streams for Node.js\n\nThis module provides the new Stream base classes introduced in Node\nv0.10, for use in Node v0.8. You can use it to have programs that\nhave to work with node v0.8, while being forward-compatible for v0.10\nand beyond. When you drop support for v0.8, you can remove this\nmodule, and only use the native streams.\n\nThis is almost exactly the same codebase as appears in Node v0.10.\nHowever:\n\n1. The exported object is actually the Readable class. Decorating the\n native `stream` module would be global pollution.\n2. In v0.10, you can safely use `base64` as an argument to\n `setEncoding` in Readable streams. However, in v0.8, the\n StringDecoder class has no `end()` method, which is problematic for\n Base64. So, don't use that, because it'll break and be weird.\n\nOther than that, the API is the same as `require('stream')` in v0.10,\nso the API docs are reproduced below.\n\n----------\n\n Stability: 2 - Unstable\n\nA stream is an abstract interface implemented by various objects in\nNode. For example a request to an HTTP server is a stream, as is\nstdout. Streams are readable, writable, or both. All streams are\ninstances of [EventEmitter][]\n\nYou can load the Stream base classes by doing `require('stream')`.\nThere are base classes provided for Readable streams, Writable\nstreams, Duplex streams, and Transform streams.\n\n## Compatibility\n\nIn earlier versions of Node, the Readable stream interface was\nsimpler, but also less powerful and less useful.\n\n* Rather than waiting for you to call the `read()` method, `'data'`\n events would start emitting immediately. If you needed to do some\n I/O to decide how to handle data, then you had to store the chunks\n in some kind of buffer so that they would not be lost.\n* The `pause()` method was advisory, rather than guaranteed. This\n meant that you still had to be prepared to receive `'data'` events\n even when the stream was in a paused state.\n\nIn Node v0.10, the Readable class described below was added. For\nbackwards compatibility with older Node programs, Readable streams\nswitch into \"old mode\" when a `'data'` event handler is added, or when\nthe `pause()` or `resume()` methods are called. The effect is that,\neven if you are not using the new `read()` method and `'readable'`\nevent, you no longer have to worry about losing `'data'` chunks.\n\nMost programs will continue to function normally. However, this\nintroduces an edge case in the following conditions:\n\n* No `'data'` event handler is added.\n* The `pause()` and `resume()` methods are never called.\n\nFor example, consider the following code:\n\n```javascript\n// WARNING! BROKEN!\nnet.createServer(function(socket) {\n\n // we add an 'end' method, but never consume the data\n socket.on('end', function() {\n // It will never get here.\n socket.end('I got your message (but didnt read it)\\n');\n });\n\n}).listen(1337);\n```\n\nIn versions of node prior to v0.10, the incoming message data would be\nsimply discarded. However, in Node v0.10 and beyond, the socket will\nremain paused forever.\n\nThe workaround in this situation is to call the `resume()` method to\ntrigger \"old mode\" behavior:\n\n```javascript\n// Workaround\nnet.createServer(function(socket) {\n\n socket.on('end', function() {\n socket.end('I got your message (but didnt read it)\\n');\n });\n\n // start the flow of data, discarding it.\n socket.resume();\n\n}).listen(1337);\n```\n\nIn addition to new Readable streams switching into old-mode, pre-v0.10\nstyle streams can be wrapped in a Readable class using the `wrap()`\nmethod.\n\n## Class: stream.Readable\n\n\n\nA `Readable Stream` has the following methods, members, and events.\n\nNote that `stream.Readable` is an abstract class designed to be\nextended with an underlying implementation of the `_read(size)`\nmethod. (See below.)\n\n### new stream.Readable([options])\n\n* `options` {Object}\n * `highWaterMark` {Number} The maximum number of bytes to store in\n the internal buffer before ceasing to read from the underlying\n resource. Default=16kb\n * `encoding` {String} If specified, then buffers will be decoded to\n strings using the specified encoding. Default=null\n * `objectMode` {Boolean} Whether this stream should behave\n as a stream of objects. Meaning that stream.read(n) returns\n a single value instead of a Buffer of size n\n\nIn classes that extend the Readable class, make sure to call the\nconstructor so that the buffering settings can be properly\ninitialized.\n\n### readable.\\_read(size)\n\n* `size` {Number} Number of bytes to read asynchronously\n\nNote: **This function should NOT be called directly.** It should be\nimplemented by child classes, and called by the internal Readable\nclass methods only.\n\nAll Readable stream implementations must provide a `_read` method\nto fetch data from the underlying resource.\n\nThis method is prefixed with an underscore because it is internal to\nthe class that defines it, and should not be called directly by user\nprograms. However, you **are** expected to override this method in\nyour own extension classes.\n\nWhen data is available, put it into the read queue by calling\n`readable.push(chunk)`. If `push` returns false, then you should stop\nreading. When `_read` is called again, you should start pushing more\ndata.\n\nThe `size` argument is advisory. Implementations where a \"read\" is a\nsingle call that returns data can use this to know how much data to\nfetch. Implementations where that is not relevant, such as TCP or\nTLS, may ignore this argument, and simply provide data whenever it\nbecomes available. There is no need, for example to \"wait\" until\n`size` bytes are available before calling `stream.push(chunk)`.\n\n### readable.push(chunk)\n\n* `chunk` {Buffer | null | String} Chunk of data to push into the read queue\n* return {Boolean} Whether or not more pushes should be performed\n\nNote: **This function should be called by Readable implementors, NOT\nby consumers of Readable subclasses.** The `_read()` function will not\nbe called again until at least one `push(chunk)` call is made. If no\ndata is available, then you MAY call `push('')` (an empty string) to\nallow a future `_read` call, without adding any data to the queue.\n\nThe `Readable` class works by putting data into a read queue to be\npulled out later by calling the `read()` method when the `'readable'`\nevent fires.\n\nThe `push()` method will explicitly insert some data into the read\nqueue. If it is called with `null` then it will signal the end of the\ndata.\n\nIn some cases, you may be wrapping a lower-level source which has some\nsort of pause/resume mechanism, and a data callback. In those cases,\nyou could wrap the low-level source object by doing something like\nthis:\n\n```javascript\n// source is an object with readStop() and readStart() methods,\n// and an `ondata` member that gets called when it has data, and\n// an `onend` member that gets called when the data is over.\n\nvar stream = new Readable();\n\nsource.ondata = function(chunk) {\n // if push() returns false, then we need to stop reading from source\n if (!stream.push(chunk))\n source.readStop();\n};\n\nsource.onend = function() {\n stream.push(null);\n};\n\n// _read will be called when the stream wants to pull more data in\n// the advisory size argument is ignored in this case.\nstream._read = function(n) {\n source.readStart();\n};\n```\n\n### readable.unshift(chunk)\n\n* `chunk` {Buffer | null | String} Chunk of data to unshift onto the read queue\n* return {Boolean} Whether or not more pushes should be performed\n\nThis is the corollary of `readable.push(chunk)`. Rather than putting\nthe data at the *end* of the read queue, it puts it at the *front* of\nthe read queue.\n\nThis is useful in certain use-cases where a stream is being consumed\nby a parser, which needs to \"un-consume\" some data that it has\noptimistically pulled out of the source.\n\n```javascript\n// A parser for a simple data protocol.\n// The \"header\" is a JSON object, followed by 2 \\n characters, and\n// then a message body.\n//\n// Note: This can be done more simply as a Transform stream. See below.\n\nfunction SimpleProtocol(source, options) {\n if (!(this instanceof SimpleProtocol))\n return new SimpleProtocol(options);\n\n Readable.call(this, options);\n this._inBody = false;\n this._sawFirstCr = false;\n\n // source is a readable stream, such as a socket or file\n this._source = source;\n\n var self = this;\n source.on('end', function() {\n self.push(null);\n });\n\n // give it a kick whenever the source is readable\n // read(0) will not consume any bytes\n source.on('readable', function() {\n self.read(0);\n });\n\n this._rawHeader = [];\n this.header = null;\n}\n\nSimpleProtocol.prototype = Object.create(\n Readable.prototype, { constructor: { value: SimpleProtocol }});\n\nSimpleProtocol.prototype._read = function(n) {\n if (!this._inBody) {\n var chunk = this._source.read();\n\n // if the source doesn't have data, we don't have data yet.\n if (chunk === null)\n return this.push('');\n\n // check if the chunk has a \\n\\n\n var split = -1;\n for (var i = 0; i < chunk.length; i++) {\n if (chunk[i] === 10) { // '\\n'\n if (this._sawFirstCr) {\n split = i;\n break;\n } else {\n this._sawFirstCr = true;\n }\n } else {\n this._sawFirstCr = false;\n }\n }\n\n if (split === -1) {\n // still waiting for the \\n\\n\n // stash the chunk, and try again.\n this._rawHeader.push(chunk);\n this.push('');\n } else {\n this._inBody = true;\n var h = chunk.slice(0, split);\n this._rawHeader.push(h);\n var header = Buffer.concat(this._rawHeader).toString();\n try {\n this.header = JSON.parse(header);\n } catch (er) {\n this.emit('error', new Error('invalid simple protocol data'));\n return;\n }\n // now, because we got some extra data, unshift the rest\n // back into the read queue so that our consumer will see it.\n var b = chunk.slice(split);\n this.unshift(b);\n\n // and let them know that we are done parsing the header.\n this.emit('header', this.header);\n }\n } else {\n // from there on, just provide the data to our consumer.\n // careful not to push(null), since that would indicate EOF.\n var chunk = this._source.read();\n if (chunk) this.push(chunk);\n }\n};\n\n// Usage:\nvar parser = new SimpleProtocol(source);\n// Now parser is a readable stream that will emit 'header'\n// with the parsed header data.\n```\n\n### readable.wrap(stream)\n\n* `stream` {Stream} An \"old style\" readable stream\n\nIf you are using an older Node library that emits `'data'` events and\nhas a `pause()` method that is advisory only, then you can use the\n`wrap()` method to create a Readable stream that uses the old stream\nas its data source.\n\nFor example:\n\n```javascript\nvar OldReader = require('./old-api-module.js').OldReader;\nvar oreader = new OldReader;\nvar Readable = require('stream').Readable;\nvar myReader = new Readable().wrap(oreader);\n\nmyReader.on('readable', function() {\n myReader.read(); // etc.\n});\n```\n\n### Event: 'readable'\n\nWhen there is data ready to be consumed, this event will fire.\n\nWhen this event emits, call the `read()` method to consume the data.\n\n### Event: 'end'\n\nEmitted when the stream has received an EOF (FIN in TCP terminology).\nIndicates that no more `'data'` events will happen. If the stream is\nalso writable, it may be possible to continue writing.\n\n### Event: 'data'\n\nThe `'data'` event emits either a `Buffer` (by default) or a string if\n`setEncoding()` was used.\n\nNote that adding a `'data'` event listener will switch the Readable\nstream into \"old mode\", where data is emitted as soon as it is\navailable, rather than waiting for you to call `read()` to consume it.\n\n### Event: 'error'\n\nEmitted if there was an error receiving data.\n\n### Event: 'close'\n\nEmitted when the underlying resource (for example, the backing file\ndescriptor) has been closed. Not all streams will emit this.\n\n### readable.setEncoding(encoding)\n\nMakes the `'data'` event emit a string instead of a `Buffer`. `encoding`\ncan be `'utf8'`, `'utf16le'` (`'ucs2'`), `'ascii'`, or `'hex'`.\n\nThe encoding can also be set by specifying an `encoding` field to the\nconstructor.\n\n### readable.read([size])\n\n* `size` {Number | null} Optional number of bytes to read.\n* Return: {Buffer | String | null}\n\nNote: **This function SHOULD be called by Readable stream users.**\n\nCall this method to consume data once the `'readable'` event is\nemitted.\n\nThe `size` argument will set a minimum number of bytes that you are\ninterested in. If not set, then the entire content of the internal\nbuffer is returned.\n\nIf there is no data to consume, or if there are fewer bytes in the\ninternal buffer than the `size` argument, then `null` is returned, and\na future `'readable'` event will be emitted when more is available.\n\nCalling `stream.read(0)` will always return `null`, and will trigger a\nrefresh of the internal buffer, but otherwise be a no-op.\n\n### readable.pipe(destination, [options])\n\n* `destination` {Writable Stream}\n* `options` {Object} Optional\n * `end` {Boolean} Default=true\n\nConnects this readable stream to `destination` WriteStream. Incoming\ndata on this stream gets written to `destination`. Properly manages\nback-pressure so that a slow destination will not be overwhelmed by a\nfast readable stream.\n\nThis function returns the `destination` stream.\n\nFor example, emulating the Unix `cat` command:\n\n process.stdin.pipe(process.stdout);\n\nBy default `end()` is called on the destination when the source stream\nemits `end`, so that `destination` is no longer writable. Pass `{ end:\nfalse }` as `options` to keep the destination stream open.\n\nThis keeps `writer` open so that \"Goodbye\" can be written at the\nend.\n\n reader.pipe(writer, { end: false });\n reader.on(\"end\", function() {\n writer.end(\"Goodbye\\n\");\n });\n\nNote that `process.stderr` and `process.stdout` are never closed until\nthe process exits, regardless of the specified options.\n\n### readable.unpipe([destination])\n\n* `destination` {Writable Stream} Optional\n\nUndo a previously established `pipe()`. If no destination is\nprovided, then all previously established pipes are removed.\n\n### readable.pause()\n\nSwitches the readable stream into \"old mode\", where data is emitted\nusing a `'data'` event rather than being buffered for consumption via\nthe `read()` method.\n\nCeases the flow of data. No `'data'` events are emitted while the\nstream is in a paused state.\n\n### readable.resume()\n\nSwitches the readable stream into \"old mode\", where data is emitted\nusing a `'data'` event rather than being buffered for consumption via\nthe `read()` method.\n\nResumes the incoming `'data'` events after a `pause()`.\n\n\n## Class: stream.Writable\n\n\n\nA `Writable` Stream has the following methods, members, and events.\n\nNote that `stream.Writable` is an abstract class designed to be\nextended with an underlying implementation of the\n`_write(chunk, encoding, cb)` method. (See below.)\n\n### new stream.Writable([options])\n\n* `options` {Object}\n * `highWaterMark` {Number} Buffer level when `write()` starts\n returning false. Default=16kb\n * `decodeStrings` {Boolean} Whether or not to decode strings into\n Buffers before passing them to `_write()`. Default=true\n\nIn classes that extend the Writable class, make sure to call the\nconstructor so that the buffering settings can be properly\ninitialized.\n\n### writable.\\_write(chunk, encoding, callback)\n\n* `chunk` {Buffer | String} The chunk to be written. Will always\n be a buffer unless the `decodeStrings` option was set to `false`.\n* `encoding` {String} If the chunk is a string, then this is the\n encoding type. Ignore chunk is a buffer. Note that chunk will\n **always** be a buffer unless the `decodeStrings` option is\n explicitly set to `false`.\n* `callback` {Function} Call this function (optionally with an error\n argument) when you are done processing the supplied chunk.\n\nAll Writable stream implementations must provide a `_write` method to\nsend data to the underlying resource.\n\nNote: **This function MUST NOT be called directly.** It should be\nimplemented by child classes, and called by the internal Writable\nclass methods only.\n\nCall the callback using the standard `callback(error)` pattern to\nsignal that the write completed successfully or with an error.\n\nIf the `decodeStrings` flag is set in the constructor options, then\n`chunk` may be a string rather than a Buffer, and `encoding` will\nindicate the sort of string that it is. This is to support\nimplementations that have an optimized handling for certain string\ndata encodings. If you do not explicitly set the `decodeStrings`\noption to `false`, then you can safely ignore the `encoding` argument,\nand assume that `chunk` will always be a Buffer.\n\nThis method is prefixed with an underscore because it is internal to\nthe class that defines it, and should not be called directly by user\nprograms. However, you **are** expected to override this method in\nyour own extension classes.\n\n\n### writable.write(chunk, [encoding], [callback])\n\n* `chunk` {Buffer | String} Data to be written\n* `encoding` {String} Optional. If `chunk` is a string, then encoding\n defaults to `'utf8'`\n* `callback` {Function} Optional. Called when this chunk is\n successfully written.\n* Returns {Boolean}\n\nWrites `chunk` to the stream. Returns `true` if the data has been\nflushed to the underlying resource. Returns `false` to indicate that\nthe buffer is full, and the data will be sent out in the future. The\n`'drain'` event will indicate when the buffer is empty again.\n\nThe specifics of when `write()` will return false, is determined by\nthe `highWaterMark` option provided to the constructor.\n\n### writable.end([chunk], [encoding], [callback])\n\n* `chunk` {Buffer | String} Optional final data to be written\n* `encoding` {String} Optional. If `chunk` is a string, then encoding\n defaults to `'utf8'`\n* `callback` {Function} Optional. Called when the final chunk is\n successfully written.\n\nCall this method to signal the end of the data being written to the\nstream.\n\n### Event: 'drain'\n\nEmitted when the stream's write queue empties and it's safe to write\nwithout buffering again. Listen for it when `stream.write()` returns\n`false`.\n\n### Event: 'close'\n\nEmitted when the underlying resource (for example, the backing file\ndescriptor) has been closed. Not all streams will emit this.\n\n### Event: 'finish'\n\nWhen `end()` is called and there are no more chunks to write, this\nevent is emitted.\n\n### Event: 'pipe'\n\n* `source` {Readable Stream}\n\nEmitted when the stream is passed to a readable stream's pipe method.\n\n### Event 'unpipe'\n\n* `source` {Readable Stream}\n\nEmitted when a previously established `pipe()` is removed using the\nsource Readable stream's `unpipe()` method.\n\n## Class: stream.Duplex\n\n\n\nA \"duplex\" stream is one that is both Readable and Writable, such as a\nTCP socket connection.\n\nNote that `stream.Duplex` is an abstract class designed to be\nextended with an underlying implementation of the `_read(size)`\nand `_write(chunk, encoding, callback)` methods as you would with a Readable or\nWritable stream class.\n\nSince JavaScript doesn't have multiple prototypal inheritance, this\nclass prototypally inherits from Readable, and then parasitically from\nWritable. It is thus up to the user to implement both the lowlevel\n`_read(n)` method as well as the lowlevel `_write(chunk, encoding, cb)` method\non extension duplex classes.\n\n### new stream.Duplex(options)\n\n* `options` {Object} Passed to both Writable and Readable\n constructors. Also has the following fields:\n * `allowHalfOpen` {Boolean} Default=true. If set to `false`, then\n the stream will automatically end the readable side when the\n writable side ends and vice versa.\n\nIn classes that extend the Duplex class, make sure to call the\nconstructor so that the buffering settings can be properly\ninitialized.\n\n## Class: stream.Transform\n\nA \"transform\" stream is a duplex stream where the output is causally\nconnected in some way to the input, such as a zlib stream or a crypto\nstream.\n\nThere is no requirement that the output be the same size as the input,\nthe same number of chunks, or arrive at the same time. For example, a\nHash stream will only ever have a single chunk of output which is\nprovided when the input is ended. A zlib stream will either produce\nmuch smaller or much larger than its input.\n\nRather than implement the `_read()` and `_write()` methods, Transform\nclasses must implement the `_transform()` method, and may optionally\nalso implement the `_flush()` method. (See below.)\n\n### new stream.Transform([options])\n\n* `options` {Object} Passed to both Writable and Readable\n constructors.\n\nIn classes that extend the Transform class, make sure to call the\nconstructor so that the buffering settings can be properly\ninitialized.\n\n### transform.\\_transform(chunk, encoding, callback)\n\n* `chunk` {Buffer | String} The chunk to be transformed. Will always\n be a buffer unless the `decodeStrings` option was set to `false`.\n* `encoding` {String} If the chunk is a string, then this is the\n encoding type. (Ignore if `decodeStrings` chunk is a buffer.)\n* `callback` {Function} Call this function (optionally with an error\n argument) when you are done processing the supplied chunk.\n\nNote: **This function MUST NOT be called directly.** It should be\nimplemented by child classes, and called by the internal Transform\nclass methods only.\n\nAll Transform stream implementations must provide a `_transform`\nmethod to accept input and produce output.\n\n`_transform` should do whatever has to be done in this specific\nTransform class, to handle the bytes being written, and pass them off\nto the readable portion of the interface. Do asynchronous I/O,\nprocess things, and so on.\n\nCall `transform.push(outputChunk)` 0 or more times to generate output\nfrom this input chunk, depending on how much data you want to output\nas a result of this chunk.\n\nCall the callback function only when the current chunk is completely\nconsumed. Note that there may or may not be output as a result of any\nparticular input chunk.\n\nThis method is prefixed with an underscore because it is internal to\nthe class that defines it, and should not be called directly by user\nprograms. However, you **are** expected to override this method in\nyour own extension classes.\n\n### transform.\\_flush(callback)\n\n* `callback` {Function} Call this function (optionally with an error\n argument) when you are done flushing any remaining data.\n\nNote: **This function MUST NOT be called directly.** It MAY be implemented\nby child classes, and if so, will be called by the internal Transform\nclass methods only.\n\nIn some cases, your transform operation may need to emit a bit more\ndata at the end of the stream. For example, a `Zlib` compression\nstream will store up some internal state so that it can optimally\ncompress the output. At the end, however, it needs to do the best it\ncan with what is left, so that the data will be complete.\n\nIn those cases, you can implement a `_flush` method, which will be\ncalled at the very end, after all the written data is consumed, but\nbefore emitting `end` to signal the end of the readable side. Just\nlike with `_transform`, call `transform.push(chunk)` zero or more\ntimes, as appropriate, and call `callback` when the flush operation is\ncomplete.\n\nThis method is prefixed with an underscore because it is internal to\nthe class that defines it, and should not be called directly by user\nprograms. However, you **are** expected to override this method in\nyour own extension classes.\n\n### Example: `SimpleProtocol` parser\n\nThe example above of a simple protocol parser can be implemented much\nmore simply by using the higher level `Transform` stream class.\n\nIn this example, rather than providing the input as an argument, it\nwould be piped into the parser, which is a more idiomatic Node stream\napproach.\n\n```javascript\nfunction SimpleProtocol(options) {\n if (!(this instanceof SimpleProtocol))\n return new SimpleProtocol(options);\n\n Transform.call(this, options);\n this._inBody = false;\n this._sawFirstCr = false;\n this._rawHeader = [];\n this.header = null;\n}\n\nSimpleProtocol.prototype = Object.create(\n Transform.prototype, { constructor: { value: SimpleProtocol }});\n\nSimpleProtocol.prototype._transform = function(chunk, encoding, done) {\n if (!this._inBody) {\n // check if the chunk has a \\n\\n\n var split = -1;\n for (var i = 0; i < chunk.length; i++) {\n if (chunk[i] === 10) { // '\\n'\n if (this._sawFirstCr) {\n split = i;\n break;\n } else {\n this._sawFirstCr = true;\n }\n } else {\n this._sawFirstCr = false;\n }\n }\n\n if (split === -1) {\n // still waiting for the \\n\\n\n // stash the chunk, and try again.\n this._rawHeader.push(chunk);\n } else {\n this._inBody = true;\n var h = chunk.slice(0, split);\n this._rawHeader.push(h);\n var header = Buffer.concat(this._rawHeader).toString();\n try {\n this.header = JSON.parse(header);\n } catch (er) {\n this.emit('error', new Error('invalid simple protocol data'));\n return;\n }\n // and let them know that we are done parsing the header.\n this.emit('header', this.header);\n\n // now, because we got some extra data, emit this first.\n this.push(b);\n }\n } else {\n // from there on, just provide the data to our consumer as-is.\n this.push(b);\n }\n done();\n};\n\nvar parser = new SimpleProtocol();\nsource.pipe(parser)\n\n// Now parser is a readable stream that will emit 'header'\n// with the parsed header data.\n```\n\n\n## Class: stream.PassThrough\n\nThis is a trivial implementation of a `Transform` stream that simply\npasses the input bytes across to the output. Its purpose is mainly\nfor examples and testing, but there are occasionally use cases where\nit can come in handy.\n\n\n[EventEmitter]: events.html#events_class_events_eventemitter\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/isaacs/readable-stream/issues" + }, + "homepage": "https://github.com/isaacs/readable-stream", + "_id": "readable-stream@1.1.9", + "dist": { + "shasum": "cec339ce46a20482b10228bdda3c8621eb724678" + }, + "_from": "readable-stream@~1.1.9", + "_resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.9.tgz" +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/passthrough.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/passthrough.js new file mode 100644 index 0000000..27e8d8a --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/passthrough.js @@ -0,0 +1 @@ +module.exports = require("./lib/_stream_passthrough.js") diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/readable.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/readable.js new file mode 100644 index 0000000..09b8bf5 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/readable.js @@ -0,0 +1,7 @@ +exports = module.exports = require('./lib/_stream_readable.js'); +exports.Stream = require('stream'); +exports.Readable = exports; +exports.Writable = require('./lib/_stream_writable.js'); +exports.Duplex = require('./lib/_stream_duplex.js'); +exports.Transform = require('./lib/_stream_transform.js'); +exports.PassThrough = require('./lib/_stream_passthrough.js'); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/common.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/common.js new file mode 100644 index 0000000..28faaf9 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/common.js @@ -0,0 +1,200 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var path = require('path'); +var assert = require('assert'); + +exports.testDir = path.dirname(__filename); +exports.fixturesDir = path.join(exports.testDir, 'fixtures'); +exports.libDir = path.join(exports.testDir, '../lib'); +exports.tmpDir = path.join(exports.testDir, 'tmp'); +exports.PORT = +process.env.NODE_COMMON_PORT || 12346; + +if (process.platform === 'win32') { + exports.PIPE = '\\\\.\\pipe\\libuv-test'; +} else { + exports.PIPE = exports.tmpDir + '/test.sock'; +} + +var util = require('util'); +for (var i in util) exports[i] = util[i]; +//for (var i in exports) global[i] = exports[i]; + +function protoCtrChain(o) { + var result = []; + for (; o; o = o.__proto__) { result.push(o.constructor); } + return result.join(); +} + +exports.indirectInstanceOf = function(obj, cls) { + if (obj instanceof cls) { return true; } + var clsChain = protoCtrChain(cls.prototype); + var objChain = protoCtrChain(obj); + return objChain.slice(-clsChain.length) === clsChain; +}; + + +exports.ddCommand = function(filename, kilobytes) { + if (process.platform === 'win32') { + var p = path.resolve(exports.fixturesDir, 'create-file.js'); + return '"' + process.argv[0] + '" "' + p + '" "' + + filename + '" ' + (kilobytes * 1024); + } else { + return 'dd if=/dev/zero of="' + filename + '" bs=1024 count=' + kilobytes; + } +}; + + +exports.spawnCat = function(options) { + var spawn = require('child_process').spawn; + + if (process.platform === 'win32') { + return spawn('more', [], options); + } else { + return spawn('cat', [], options); + } +}; + + +exports.spawnPwd = function(options) { + var spawn = require('child_process').spawn; + + if (process.platform === 'win32') { + return spawn('cmd.exe', ['/c', 'cd'], options); + } else { + return spawn('pwd', [], options); + } +}; + + +// Turn this off if the test should not check for global leaks. +exports.globalCheck = true; + +process.on('exit', function() { + if (!exports.globalCheck) return; + var knownGlobals = [setTimeout, + setInterval, + setImmediate, + clearTimeout, + clearInterval, + clearImmediate, + console, + Buffer, + process, + global]; + + if (global.gc) { + knownGlobals.push(gc); + } + + if (global.DTRACE_HTTP_SERVER_RESPONSE) { + knownGlobals.push(DTRACE_HTTP_SERVER_RESPONSE); + knownGlobals.push(DTRACE_HTTP_SERVER_REQUEST); + knownGlobals.push(DTRACE_HTTP_CLIENT_RESPONSE); + knownGlobals.push(DTRACE_HTTP_CLIENT_REQUEST); + knownGlobals.push(DTRACE_NET_STREAM_END); + knownGlobals.push(DTRACE_NET_SERVER_CONNECTION); + knownGlobals.push(DTRACE_NET_SOCKET_READ); + knownGlobals.push(DTRACE_NET_SOCKET_WRITE); + } + if (global.COUNTER_NET_SERVER_CONNECTION) { + knownGlobals.push(COUNTER_NET_SERVER_CONNECTION); + knownGlobals.push(COUNTER_NET_SERVER_CONNECTION_CLOSE); + knownGlobals.push(COUNTER_HTTP_SERVER_REQUEST); + knownGlobals.push(COUNTER_HTTP_SERVER_RESPONSE); + knownGlobals.push(COUNTER_HTTP_CLIENT_REQUEST); + knownGlobals.push(COUNTER_HTTP_CLIENT_RESPONSE); + } + + if (global.ArrayBuffer) { + knownGlobals.push(ArrayBuffer); + knownGlobals.push(Int8Array); + knownGlobals.push(Uint8Array); + knownGlobals.push(Uint8ClampedArray); + knownGlobals.push(Int16Array); + knownGlobals.push(Uint16Array); + knownGlobals.push(Int32Array); + knownGlobals.push(Uint32Array); + knownGlobals.push(Float32Array); + knownGlobals.push(Float64Array); + knownGlobals.push(DataView); + } + + for (var x in global) { + var found = false; + + for (var y in knownGlobals) { + if (global[x] === knownGlobals[y]) { + found = true; + break; + } + } + + if (!found) { + console.error('Unknown global: %s', x); + assert.ok(false, 'Unknown global found'); + } + } +}); + + +var mustCallChecks = []; + + +function runCallChecks(exitCode) { + if (exitCode !== 0) return; + + var failed = mustCallChecks.filter(function(context) { + return context.actual !== context.expected; + }); + + failed.forEach(function(context) { + console.log('Mismatched %s function calls. Expected %d, actual %d.', + context.name, + context.expected, + context.actual); + console.log(context.stack.split('\n').slice(2).join('\n')); + }); + + if (failed.length) process.exit(1); +} + + +exports.mustCall = function(fn, expected) { + if (typeof expected !== 'number') expected = 1; + + var context = { + expected: expected, + actual: 0, + stack: (new Error).stack, + name: fn.name || '' + }; + + // add the exit listener only once to avoid listener leak warnings + if (mustCallChecks.length === 0) process.on('exit', runCallChecks); + + mustCallChecks.push(context); + + return function() { + context.actual++; + return fn.apply(this, arguments); + }; +}; diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/fixtures/x1024.txt b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/fixtures/x1024.txt new file mode 100644 index 0000000..c6a9d2f --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/fixtures/x1024.txt @@ -0,0 +1 @@ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \ No newline at end of file diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-GH-64.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-GH-64.js new file mode 100644 index 0000000..a51567f --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-GH-64.js @@ -0,0 +1,18 @@ +var assert = require('assert'); +var http = require('http'); +var net = require('net'); +var stream = require('readable-stream'); +var PORT = require('../common.js').PORT; + +var server = http.createServer(function (req, res) { + res.end('ok'); + server.close(function() { + console.log('ok'); + }); +}).listen(PORT, 'localhost', function () { + var client = net.connect(PORT); + client.write( + "GET / HTTP/1.1\r\n" + + "Host: localhost\r\n\r\n") + client.end(); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-GH-66.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-GH-66.js new file mode 100644 index 0000000..a51567f --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-GH-66.js @@ -0,0 +1,18 @@ +var assert = require('assert'); +var http = require('http'); +var net = require('net'); +var stream = require('readable-stream'); +var PORT = require('../common.js').PORT; + +var server = http.createServer(function (req, res) { + res.end('ok'); + server.close(function() { + console.log('ok'); + }); +}).listen(PORT, 'localhost', function () { + var client = net.connect(PORT); + client.write( + "GET / HTTP/1.1\r\n" + + "Host: localhost\r\n\r\n") + client.end(); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-big-push.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-big-push.js new file mode 100644 index 0000000..8cd2127 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-big-push.js @@ -0,0 +1,84 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var stream = require('../../'); +var str = 'asdfasdfasdfasdfasdf'; + +var r = new stream.Readable({ + highWaterMark: 5, + encoding: 'utf8' +}); + +var reads = 0; +var eofed = false; +var ended = false; + +r._read = function(n) { + if (reads === 0) { + setTimeout(function() { + r.push(str); + }); + reads++; + } else if (reads === 1) { + var ret = r.push(str); + assert.equal(ret, false); + reads++; + } else { + assert(!eofed); + eofed = true; + r.push(null); + } +}; + +r.on('end', function() { + ended = true; +}); + +// push some data in to start. +// we've never gotten any read event at this point. +var ret = r.push(str); +// should be false. > hwm +assert(!ret); +var chunk = r.read(); +assert.equal(chunk, str); +chunk = r.read(); +assert.equal(chunk, null); + +r.once('readable', function() { + // this time, we'll get *all* the remaining data, because + // it's been added synchronously, as the read WOULD take + // us below the hwm, and so it triggered a _read() again, + // which synchronously added more, which we then return. + chunk = r.read(); + assert.equal(chunk, str + str); + + chunk = r.read(); + assert.equal(chunk, null); +}); + +process.on('exit', function() { + assert(eofed); + assert(ended); + assert.equal(reads, 2); + console.log('ok'); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-end-paused.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-end-paused.js new file mode 100644 index 0000000..d40efc7 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-end-paused.js @@ -0,0 +1,53 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var gotEnd = false; + +// Make sure we don't miss the end event for paused 0-length streams + +var Readable = require('../../').Readable; +var stream = new Readable(); +var calledRead = false; +stream._read = function() { + assert(!calledRead); + calledRead = true; + this.push(null); +}; + +stream.on('data', function() { + throw new Error('should not ever get data'); +}); +stream.pause(); + +setTimeout(function() { + stream.on('end', function() { + gotEnd = true; + }); + stream.resume(); +}); + +process.on('exit', function() { + assert(gotEnd); + assert(calledRead); + console.log('ok'); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-pipe-after-end.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-pipe-after-end.js new file mode 100644 index 0000000..0be8366 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-pipe-after-end.js @@ -0,0 +1,86 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +var Readable = require('../../lib/_stream_readable'); +var Writable = require('../../lib/_stream_writable'); +var util = require('util'); + +util.inherits(TestReadable, Readable); +function TestReadable(opt) { + if (!(this instanceof TestReadable)) + return new TestReadable(opt); + Readable.call(this, opt); + this._ended = false; +} + +TestReadable.prototype._read = function(n) { + if (this._ended) + this.emit('error', new Error('_read called twice')); + this._ended = true; + this.push(null); +}; + +util.inherits(TestWritable, Writable); +function TestWritable(opt) { + if (!(this instanceof TestWritable)) + return new TestWritable(opt); + Writable.call(this, opt); + this._written = []; +} + +TestWritable.prototype._write = function(chunk, encoding, cb) { + this._written.push(chunk); + cb(); +}; + +// this one should not emit 'end' until we read() from it later. +var ender = new TestReadable(); +var enderEnded = false; + +// what happens when you pipe() a Readable that's already ended? +var piper = new TestReadable(); +// pushes EOF null, and length=0, so this will trigger 'end' +piper.read(); + +setTimeout(function() { + ender.on('end', function() { + enderEnded = true; + }); + assert(!enderEnded); + var c = ender.read(); + assert.equal(c, null); + + var w = new TestWritable(); + var writableFinished = false; + w.on('finish', function() { + writableFinished = true; + }); + piper.pipe(w); + + process.on('exit', function() { + assert(enderEnded); + assert(writableFinished); + console.log('ok'); + }); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-pipe-error-handling.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-pipe-error-handling.js new file mode 100644 index 0000000..c7d6b7d --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-pipe-error-handling.js @@ -0,0 +1,131 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var Stream = require('../../').Stream; + +(function testErrorListenerCatches() { + var source = new Stream(); + var dest = new Stream(); + + source.pipe(dest); + + var gotErr = null; + source.on('error', function(err) { + gotErr = err; + }); + + var err = new Error('This stream turned into bacon.'); + source.emit('error', err); + assert.strictEqual(gotErr, err); +})(); + +(function testErrorWithoutListenerThrows() { + var source = new Stream(); + var dest = new Stream(); + + source.pipe(dest); + + var err = new Error('This stream turned into bacon.'); + + var gotErr = null; + try { + source.emit('error', err); + } catch (e) { + gotErr = e; + } + + assert.strictEqual(gotErr, err); +})(); + +(function testErrorWithRemovedListenerThrows() { + var EE = require('events').EventEmitter; + var R = Stream.Readable; + var W = Stream.Writable; + + var r = new R; + var w = new W; + var removed = false; + var didTest = false; + + process.on('exit', function() { + assert(didTest); + console.log('ok'); + }); + + r._read = function() { + setTimeout(function() { + assert(removed); + assert.throws(function() { + w.emit('error', new Error('fail')); + }); + didTest = true; + }); + }; + + w.on('error', myOnError); + r.pipe(w); + w.removeListener('error', myOnError); + removed = true; + + function myOnError(er) { + throw new Error('this should not happen'); + } +})(); + +(function testErrorWithRemovedListenerThrows() { + var EE = require('events').EventEmitter; + var R = Stream.Readable; + var W = Stream.Writable; + + var r = new R; + var w = new W; + var removed = false; + var didTest = false; + var caught = false; + + process.on('exit', function() { + assert(didTest); + console.log('ok'); + }); + + r._read = function() { + setTimeout(function() { + assert(removed); + w.emit('error', new Error('fail')); + didTest = true; + }); + }; + + w.on('error', myOnError); + w._write = function() {}; + + r.pipe(w); + // Removing some OTHER random listener should not do anything + w.removeListener('error', function() {}); + removed = true; + + function myOnError(er) { + assert(!caught); + caught = true; + } +})(); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-pipe-event.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-pipe-event.js new file mode 100644 index 0000000..56f8d61 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-pipe-event.js @@ -0,0 +1,49 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var stream = require('../../'); +var assert = require('assert'); +var util = require('util'); + +function Writable() { + this.writable = true; + stream.Stream.call(this); +} +util.inherits(Writable, stream.Stream); + +function Readable() { + this.readable = true; + stream.Stream.call(this); +} +util.inherits(Readable, stream.Stream); + +var passed = false; + +var w = new Writable(); +w.on('pipe', function(src) { + passed = true; +}); + +var r = new Readable(); +r.pipe(w); + +assert.ok(passed); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-push-order.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-push-order.js new file mode 100644 index 0000000..a5c9bf9 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-push-order.js @@ -0,0 +1,52 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common.js'); +var Readable = require('../../').Readable; +var assert = require('assert'); + +var s = new Readable({ + highWaterMark: 20, + encoding: 'ascii' +}); + +var list = ['1', '2', '3', '4', '5', '6']; + +s._read = function (n) { + var one = list.shift(); + if (!one) { + s.push(null); + } else { + var two = list.shift(); + s.push(one); + s.push(two); + } +}; + +var v = s.read(0); + +// ACTUALLY [1, 3, 5, 6, 4, 2] + +process.on("exit", function () { + assert.deepEqual(s._readableState.buffer, + ['1', '2', '3', '4', '5', '6']); + console.log("ok"); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-push-strings.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-push-strings.js new file mode 100644 index 0000000..1701a9a --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-push-strings.js @@ -0,0 +1,66 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +var Readable = require('../../').Readable; +var util = require('util'); + +util.inherits(MyStream, Readable); +function MyStream(options) { + Readable.call(this, options); + this._chunks = 3; +} + +MyStream.prototype._read = function(n) { + switch (this._chunks--) { + case 0: + return this.push(null); + case 1: + return setTimeout(function() { + this.push('last chunk'); + }.bind(this), 100); + case 2: + return this.push('second to last chunk'); + case 3: + return process.nextTick(function() { + this.push('first chunk'); + }.bind(this)); + default: + throw new Error('?'); + } +}; + +var ms = new MyStream(); +var results = []; +ms.on('readable', function() { + var chunk; + while (null !== (chunk = ms.read())) + results.push(chunk + ''); +}); + +var expect = [ 'first chunksecond to last chunk', 'last chunk' ]; +process.on('exit', function() { + assert.equal(ms._chunks, -1); + assert.deepEqual(results, expect); + console.log('ok'); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-readable-event.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-readable-event.js new file mode 100644 index 0000000..a8e6f7b --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-readable-event.js @@ -0,0 +1,126 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +var Readable = require('../../').Readable; + +(function first() { + // First test, not reading when the readable is added. + // make sure that on('readable', ...) triggers a readable event. + var r = new Readable({ + highWaterMark: 3 + }); + + var _readCalled = false; + r._read = function(n) { + _readCalled = true; + }; + + // This triggers a 'readable' event, which is lost. + r.push(new Buffer('blerg')); + + var caughtReadable = false; + setTimeout(function() { + // we're testing what we think we are + assert(!r._readableState.reading); + r.on('readable', function() { + caughtReadable = true; + }); + }); + + process.on('exit', function() { + // we're testing what we think we are + assert(!_readCalled); + + assert(caughtReadable); + console.log('ok 1'); + }); +})(); + +(function second() { + // second test, make sure that readable is re-emitted if there's + // already a length, while it IS reading. + + var r = new Readable({ + highWaterMark: 3 + }); + + var _readCalled = false; + r._read = function(n) { + _readCalled = true; + }; + + // This triggers a 'readable' event, which is lost. + r.push(new Buffer('bl')); + + var caughtReadable = false; + setTimeout(function() { + // assert we're testing what we think we are + assert(r._readableState.reading); + r.on('readable', function() { + caughtReadable = true; + }); + }); + + process.on('exit', function() { + // we're testing what we think we are + assert(_readCalled); + + assert(caughtReadable); + console.log('ok 2'); + }); +})(); + +(function third() { + // Third test, not reading when the stream has not passed + // the highWaterMark but *has* reached EOF. + var r = new Readable({ + highWaterMark: 30 + }); + + var _readCalled = false; + r._read = function(n) { + _readCalled = true; + }; + + // This triggers a 'readable' event, which is lost. + r.push(new Buffer('blerg')); + r.push(null); + + var caughtReadable = false; + setTimeout(function() { + // assert we're testing what we think we are + assert(!r._readableState.reading); + r.on('readable', function() { + caughtReadable = true; + }); + }); + + process.on('exit', function() { + // we're testing what we think we are + assert(!_readCalled); + + assert(caughtReadable); + console.log('ok 3'); + }); +})(); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-readable-flow-recursion.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-readable-flow-recursion.js new file mode 100644 index 0000000..11689ba --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-readable-flow-recursion.js @@ -0,0 +1,76 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +// this test verifies that passing a huge number to read(size) +// will push up the highWaterMark, and cause the stream to read +// more data continuously, but without triggering a nextTick +// warning or RangeError. + +var Readable = require('../../').Readable; + +// throw an error if we trigger a nextTick warning. +process.throwDeprecation = true; + +var stream = new Readable({ highWaterMark: 2 }); +var reads = 0; +var total = 5000; +stream._read = function(size) { + reads++; + size = Math.min(size, total); + total -= size; + if (size === 0) + stream.push(null); + else + stream.push(new Buffer(size)); +}; + +var depth = 0; + +function flow(stream, size, callback) { + depth += 1; + var chunk = stream.read(size); + + if (!chunk) + stream.once('readable', flow.bind(null, stream, size, callback)); + else + callback(chunk); + + depth -= 1; + console.log('flow(' + depth + '): exit'); +} + +flow(stream, 5000, function() { + console.log('complete (' + depth + ')'); +}); + +process.on('exit', function(code) { + assert.equal(reads, 2); + // we pushed up the high water mark + assert.equal(stream._readableState.highWaterMark, 8192); + // length is 0 right now, because we pulled it all out. + assert.equal(stream._readableState.length, 0); + assert(!code); + assert.equal(depth, 0); + console.log('ok'); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-unshift-empty-chunk.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-unshift-empty-chunk.js new file mode 100644 index 0000000..7827538 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-unshift-empty-chunk.js @@ -0,0 +1,81 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +// This test verifies that stream.unshift(Buffer(0)) or +// stream.unshift('') does not set state.reading=false. +var Readable = require('../../').Readable; + +var r = new Readable(); +var nChunks = 10; +var chunk = new Buffer(10); +chunk.fill('x'); + +r._read = function(n) { + setTimeout(function() { + r.push(--nChunks === 0 ? null : chunk); + }); +}; + +var readAll = false; +var seen = []; +r.on('readable', function() { + var chunk; + while (chunk = r.read()) { + seen.push(chunk.toString()); + // simulate only reading a certain amount of the data, + // and then putting the rest of the chunk back into the + // stream, like a parser might do. We just fill it with + // 'y' so that it's easy to see which bits were touched, + // and which were not. + var putBack = new Buffer(readAll ? 0 : 5); + putBack.fill('y'); + readAll = !readAll; + r.unshift(putBack); + } +}); + +var expect = + [ 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy', + 'xxxxxxxxxx', + 'yyyyy' ]; + +r.on('end', function() { + assert.deepEqual(seen, expect); + console.log('ok'); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-unshift-read-race.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-unshift-read-race.js new file mode 100644 index 0000000..17c18aa --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-unshift-read-race.js @@ -0,0 +1,140 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +// This test verifies that: +// 1. unshift() does not cause colliding _read() calls. +// 2. unshift() after the 'end' event is an error, but after the EOF +// signalling null, it is ok, and just creates a new readable chunk. +// 3. push() after the EOF signaling null is an error. +// 4. _read() is not called after pushing the EOF null chunk. + +var stream = require('../../'); +var hwm = 10; +var r = stream.Readable({ highWaterMark: hwm }); +var chunks = 10; +var t = (chunks * 5); + +var data = new Buffer(chunks * hwm + Math.ceil(hwm / 2)); +for (var i = 0; i < data.length; i++) { + var c = 'asdf'.charCodeAt(i % 4); + data[i] = c; +} + +var pos = 0; +var pushedNull = false; +r._read = function(n) { + assert(!pushedNull, '_read after null push'); + + // every third chunk is fast + push(!(chunks % 3)); + + function push(fast) { + assert(!pushedNull, 'push() after null push'); + var c; + if (pos >= data.length) + c = null; + else { + if (n + pos > data.length) + n = data.length - pos; + c = data.slice(pos, pos + n); + } + pushedNull = c === null; + if (fast) { + pos += n; + r.push(c); + if (c === null) pushError(); + } else { + setTimeout(function() { + pos += n; + r.push(c); + if (c === null) pushError(); + }); + } + } +}; + +function pushError() { + assert.throws(function() { + r.push(new Buffer(1)); + }); +} + + +var w = stream.Writable(); +var written = []; +w._write = function(chunk, encoding, cb) { + written.push(chunk.toString()); + cb(); +}; + +var ended = false; +r.on('end', function() { + assert(!ended, 'end emitted more than once'); + assert.throws(function() { + r.unshift(new Buffer(1)); + }); + ended = true; + w.end(); +}); + +r.on('readable', function() { + var chunk; + while (null !== (chunk = r.read(10))) { + w.write(chunk); + if (chunk.length > 4) + r.unshift(new Buffer('1234')); + } +}); + +var finished = false; +w.on('finish', function() { + finished = true; + // each chunk should start with 1234, and then be asfdasdfasdf... + // The first got pulled out before the first unshift('1234'), so it's + // lacking that piece. + assert.equal(written[0], 'asdfasdfas'); + var asdf = 'd'; + console.error('0: %s', written[0]); + for (var i = 1; i < written.length; i++) { + console.error('%s: %s', i.toString(32), written[i]); + assert.equal(written[i].slice(0, 4), '1234'); + for (var j = 4; j < written[i].length; j++) { + var c = written[i].charAt(j); + assert.equal(c, asdf); + switch (asdf) { + case 'a': asdf = 's'; break; + case 's': asdf = 'd'; break; + case 'd': asdf = 'f'; break; + case 'f': asdf = 'a'; break; + } + } + } +}); + +process.on('exit', function() { + assert.equal(written.length, 18); + assert(ended, 'stream ended'); + assert(finished, 'stream finished'); + console.log('ok'); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-writev.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-writev.js new file mode 100644 index 0000000..b5321f3 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream-writev.js @@ -0,0 +1,121 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +var stream = require('../../'); + +var queue = []; +for (var decode = 0; decode < 2; decode++) { + for (var uncork = 0; uncork < 2; uncork++) { + for (var multi = 0; multi < 2; multi++) { + queue.push([!!decode, !!uncork, !!multi]); + } + } +} + +run(); + +function run() { + var t = queue.pop(); + if (t) + test(t[0], t[1], t[2], run); + else + console.log('ok'); +} + +function test(decode, uncork, multi, next) { + console.log('# decode=%j uncork=%j multi=%j', decode, uncork, multi); + var counter = 0; + var expectCount = 0; + function cnt(msg) { + expectCount++; + var expect = expectCount; + var called = false; + return function(er) { + if (er) + throw er; + called = true; + counter++; + assert.equal(counter, expect); + }; + } + + var w = new stream.Writable({ decodeStrings: decode }); + w._write = function(chunk, e, cb) { + assert(false, 'Should not call _write'); + }; + + var expectChunks = decode ? + [{ encoding: 'buffer', + chunk: [104, 101, 108, 108, 111, 44, 32] }, + { encoding: 'buffer', chunk: [119, 111, 114, 108, 100] }, + { encoding: 'buffer', chunk: [33] }, + { encoding: 'buffer', + chunk: [10, 97, 110, 100, 32, 116, 104, 101, 110, 46, 46, 46] }, + { encoding: 'buffer', + chunk: [250, 206, 190, 167, 222, 173, 190, 239, 222, 202, 251, 173] }] : + [{ encoding: 'ascii', chunk: 'hello, ' }, + { encoding: 'utf8', chunk: 'world' }, + { encoding: 'buffer', chunk: [33] }, + { encoding: 'binary', chunk: '\nand then...' }, + { encoding: 'hex', chunk: 'facebea7deadbeefdecafbad' }]; + + var actualChunks; + w._writev = function(chunks, cb) { + actualChunks = chunks.map(function(chunk) { + return { + encoding: chunk.encoding, + chunk: Buffer.isBuffer(chunk.chunk) ? + Array.prototype.slice.call(chunk.chunk) : chunk.chunk + }; + }); + cb(); + }; + + w.cork(); + w.write('hello, ', 'ascii', cnt('hello')); + w.write('world', 'utf8', cnt('world')); + + if (multi) + w.cork(); + + w.write(new Buffer('!'), 'buffer', cnt('!')); + w.write('\nand then...', 'binary', cnt('and then')); + + if (multi) + w.uncork(); + + w.write('facebea7deadbeefdecafbad', 'hex', cnt('hex')); + + if (uncork) + w.uncork(); + + w.end(cnt('end')); + + w.on('finish', function() { + // make sure finish comes after all the write cb + cnt('finish')(); + assert.deepEqual(expectChunks, actualChunks); + next(); + }); +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-basic.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-basic.js new file mode 100644 index 0000000..248c1be --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-basic.js @@ -0,0 +1,475 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + +var common = require('../common.js'); +var R = require('../../lib/_stream_readable'); +var assert = require('assert'); + +var util = require('util'); +var EE = require('events').EventEmitter; + +function TestReader(n) { + R.apply(this); + this._buffer = new Buffer(n || 100); + this._buffer.fill('x'); + this._pos = 0; + this._bufs = 10; +} + +util.inherits(TestReader, R); + +TestReader.prototype._read = function(n) { + var max = this._buffer.length - this._pos; + n = Math.max(n, 0); + var toRead = Math.min(n, max); + if (toRead === 0) { + // simulate the read buffer filling up with some more bytes some time + // in the future. + setTimeout(function() { + this._pos = 0; + this._bufs -= 1; + if (this._bufs <= 0) { + // read them all! + if (!this.ended) + this.push(null); + } else { + // now we have more. + // kinda cheating by calling _read, but whatever, + // it's just fake anyway. + this._read(n); + } + }.bind(this), 10); + return; + } + + var ret = this._buffer.slice(this._pos, this._pos + toRead); + this._pos += toRead; + this.push(ret); +}; + +///// + +function TestWriter() { + EE.apply(this); + this.received = []; + this.flush = false; +} + +util.inherits(TestWriter, EE); + +TestWriter.prototype.write = function(c) { + this.received.push(c.toString()); + this.emit('write', c); + return true; +}; + +TestWriter.prototype.end = function(c) { + if (c) this.write(c); + this.emit('end', this.received); +}; + +//////// + +// tiny node-tap lookalike. +var tests = []; +var count = 0; + +function test(name, fn) { + count++; + tests.push([name, fn]); +} + +function run() { + var next = tests.shift(); + if (!next) + return console.error('ok'); + + var name = next[0]; + var fn = next[1]; + console.log('# %s', name); + fn({ + same: assert.deepEqual, + ok: assert, + equal: assert.equal, + end: function () { + count--; + run(); + } + }); +} + +// ensure all tests have run +process.on("exit", function () { + assert.equal(count, 0); +}); + +process.nextTick(run); + + +test('a most basic test', function(t) { + var r = new TestReader(20); + + var reads = []; + var expect = [ 'x', + 'xx', + 'xxx', + 'xxxx', + 'xxxxx', + 'xxxxxxxxx', + 'xxxxxxxxxx', + 'xxxxxxxxxxxx', + 'xxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxxxxxxxxxx', + 'xxxxxxxxxxxxxxxxxxxxx' ]; + + r.on('end', function() { + t.same(reads, expect); + t.end(); + }); + + var readSize = 1; + function flow() { + var res; + while (null !== (res = r.read(readSize++))) { + reads.push(res.toString()); + } + r.once('readable', flow); + } + + flow(); +}); + +test('pipe', function(t) { + var r = new TestReader(5); + + var expect = [ 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx' ] + + var w = new TestWriter; + var flush = true; + + w.on('end', function(received) { + t.same(received, expect); + t.end(); + }); + + r.pipe(w); +}); + + + +[1,2,3,4,5,6,7,8,9].forEach(function(SPLIT) { + test('unpipe', function(t) { + var r = new TestReader(5); + + // unpipe after 3 writes, then write to another stream instead. + var expect = [ 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx' ]; + expect = [ expect.slice(0, SPLIT), expect.slice(SPLIT) ]; + + var w = [ new TestWriter(), new TestWriter() ]; + + var writes = SPLIT; + w[0].on('write', function() { + if (--writes === 0) { + r.unpipe(); + t.equal(r._readableState.pipes, null); + w[0].end(); + r.pipe(w[1]); + t.equal(r._readableState.pipes, w[1]); + } + }); + + var ended = 0; + + var ended0 = false; + var ended1 = false; + w[0].on('end', function(results) { + t.equal(ended0, false); + ended0 = true; + ended++; + t.same(results, expect[0]); + }); + + w[1].on('end', function(results) { + t.equal(ended1, false); + ended1 = true; + ended++; + t.equal(ended, 2); + t.same(results, expect[1]); + t.end(); + }); + + r.pipe(w[0]); + }); +}); + + +// both writers should get the same exact data. +test('multipipe', function(t) { + var r = new TestReader(5); + var w = [ new TestWriter, new TestWriter ]; + + var expect = [ 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx' ]; + + var c = 2; + w[0].on('end', function(received) { + t.same(received, expect, 'first'); + if (--c === 0) t.end(); + }); + w[1].on('end', function(received) { + t.same(received, expect, 'second'); + if (--c === 0) t.end(); + }); + + r.pipe(w[0]); + r.pipe(w[1]); +}); + + +[1,2,3,4,5,6,7,8,9].forEach(function(SPLIT) { + test('multi-unpipe', function(t) { + var r = new TestReader(5); + + // unpipe after 3 writes, then write to another stream instead. + var expect = [ 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx', + 'xxxxx' ]; + expect = [ expect.slice(0, SPLIT), expect.slice(SPLIT) ]; + + var w = [ new TestWriter(), new TestWriter(), new TestWriter() ]; + + var writes = SPLIT; + w[0].on('write', function() { + if (--writes === 0) { + r.unpipe(); + w[0].end(); + r.pipe(w[1]); + } + }); + + var ended = 0; + + w[0].on('end', function(results) { + ended++; + t.same(results, expect[0]); + }); + + w[1].on('end', function(results) { + ended++; + t.equal(ended, 2); + t.same(results, expect[1]); + t.end(); + }); + + r.pipe(w[0]); + r.pipe(w[2]); + }); +}); + +test('back pressure respected', function (t) { + function noop() {} + + var r = new R({ objectMode: true }); + r._read = noop; + var counter = 0; + r.push(["one"]); + r.push(["two"]); + r.push(["three"]); + r.push(["four"]); + r.push(null); + + var w1 = new R(); + w1.write = function (chunk) { + console.error('w1.emit("close")'); + assert.equal(chunk[0], "one"); + w1.emit("close"); + process.nextTick(function () { + r.pipe(w2); + r.pipe(w3); + }) + }; + w1.end = noop; + + r.pipe(w1); + + var expected = ["two", "two", "three", "three", "four", "four"]; + + var w2 = new R(); + w2.write = function (chunk) { + console.error('w2 write', chunk, counter); + assert.equal(chunk[0], expected.shift()); + assert.equal(counter, 0); + + counter++; + + if (chunk[0] === "four") { + return true; + } + + setTimeout(function () { + counter--; + console.error("w2 drain"); + w2.emit("drain"); + }, 10); + + return false; + } + w2.end = noop; + + var w3 = new R(); + w3.write = function (chunk) { + console.error('w3 write', chunk, counter); + assert.equal(chunk[0], expected.shift()); + assert.equal(counter, 1); + + counter++; + + if (chunk[0] === "four") { + return true; + } + + setTimeout(function () { + counter--; + console.error("w3 drain"); + w3.emit("drain"); + }, 50); + + return false; + }; + w3.end = function () { + assert.equal(counter, 2); + assert.equal(expected.length, 0); + t.end(); + }; +}); + +test('read(0) for ended streams', function (t) { + var r = new R(); + var written = false; + var ended = false; + r._read = function (n) {}; + + r.push(new Buffer("foo")); + r.push(null); + + var v = r.read(0); + + assert.equal(v, null); + + var w = new R(); + + w.write = function (buffer) { + written = true; + assert.equal(ended, false); + assert.equal(buffer.toString(), "foo") + }; + + w.end = function () { + ended = true; + assert.equal(written, true); + t.end(); + }; + + r.pipe(w); +}) + +test('sync _read ending', function (t) { + var r = new R(); + var called = false; + r._read = function (n) { + r.push(null); + }; + + r.once('end', function () { + called = true; + }) + + r.read(); + + process.nextTick(function () { + assert.equal(called, true); + t.end(); + }) +}); + +test('adding readable triggers data flow', function(t) { + var r = new R({ highWaterMark: 5 }); + var onReadable = false; + var readCalled = 0; + + r._read = function(n) { + if (readCalled++ === 2) + r.push(null); + else + r.push(new Buffer('asdf')); + }; + + var called = false; + r.on('readable', function() { + onReadable = true; + r.read(); + }); + + r.on('end', function() { + t.equal(readCalled, 3); + t.ok(onReadable); + t.end(); + }); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-compatibility.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-compatibility.js new file mode 100644 index 0000000..f0fa84b --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-compatibility.js @@ -0,0 +1,53 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + +var common = require('../common.js'); +var R = require('../../lib/_stream_readable'); +var assert = require('assert'); + +var util = require('util'); +var EE = require('events').EventEmitter; + +var ondataCalled = 0; + +function TestReader() { + R.apply(this); + this._buffer = new Buffer(100); + this._buffer.fill('x'); + + this.on('data', function() { + ondataCalled++; + }); +} + +util.inherits(TestReader, R); + +TestReader.prototype._read = function(n) { + this.push(this._buffer); + this._buffer = new Buffer(0); +}; + +var reader = new TestReader(); +setImmediate(function() { + assert.equal(ondataCalled, 1); + console.log('ok'); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-finish-pipe.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-finish-pipe.js new file mode 100644 index 0000000..006a19b --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-finish-pipe.js @@ -0,0 +1,41 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common.js'); +var stream = require('../../'); +var Buffer = require('buffer').Buffer; + +var r = new stream.Readable(); +r._read = function(size) { + r.push(new Buffer(size)); +}; + +var w = new stream.Writable(); +w._write = function(data, encoding, cb) { + cb(null); +}; + +r.pipe(w); + +// This might sound unrealistic, but it happens in net.js. When +// `socket.allowHalfOpen === false`, EOF will cause `.destroySoon()` call which +// ends the writable side of net.Socket. +w.end(); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-large-read-stall.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-large-read-stall.js new file mode 100644 index 0000000..667985b --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-large-read-stall.js @@ -0,0 +1,82 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common.js'); +var assert = require('assert'); + +// If everything aligns so that you do a read(n) of exactly the +// remaining buffer, then make sure that 'end' still emits. + +var READSIZE = 100; +var PUSHSIZE = 20; +var PUSHCOUNT = 1000; +var HWM = 50; + +var Readable = require('../../').Readable; +var r = new Readable({ + highWaterMark: HWM +}); +var rs = r._readableState; + +r._read = push; + +r.on('readable', function() { + //console.error('>> readable'); + do { + //console.error(' > read(%d)', READSIZE); + var ret = r.read(READSIZE); + //console.error(' < %j (%d remain)', ret && ret.length, rs.length); + } while (ret && ret.length === READSIZE); + + //console.error('<< after read()', + // ret && ret.length, + // rs.needReadable, + // rs.length); +}); + +var endEmitted = false; +r.on('end', function() { + endEmitted = true; + //console.error('end'); +}); + +var pushes = 0; +function push() { + if (pushes > PUSHCOUNT) + return; + + if (pushes++ === PUSHCOUNT) { + //console.error(' push(EOF)'); + return r.push(null); + } + + //console.error(' push #%d', pushes); + if (r.push(new Buffer(PUSHSIZE))) + setTimeout(push); +} + +// start the flow +var ret = r.read(0); + +process.on('exit', function() { + assert.equal(pushes, PUSHCOUNT + 1); + assert(endEmitted); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-objects.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-objects.js new file mode 100644 index 0000000..ff47d89 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-objects.js @@ -0,0 +1,351 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + +var common = require('../common.js'); +var Readable = require('../../lib/_stream_readable'); +var Writable = require('../../lib/_stream_writable'); +var assert = require('assert'); + +// tiny node-tap lookalike. +var tests = []; +var count = 0; + +function test(name, fn) { + count++; + tests.push([name, fn]); +} + +function run() { + var next = tests.shift(); + if (!next) + return console.error('ok'); + + var name = next[0]; + var fn = next[1]; + console.log('# %s', name); + fn({ + same: assert.deepEqual, + equal: assert.equal, + end: function() { + count--; + run(); + } + }); +} + +// ensure all tests have run +process.on('exit', function() { + assert.equal(count, 0); +}); + +process.nextTick(run); + +function toArray(callback) { + var stream = new Writable({ objectMode: true }); + var list = []; + stream.write = function(chunk) { + list.push(chunk); + }; + + stream.end = function() { + callback(list); + }; + + return stream; +} + +function fromArray(list) { + var r = new Readable({ objectMode: true }); + r._read = noop; + list.forEach(function(chunk) { + r.push(chunk); + }); + r.push(null); + + return r; +} + +function noop() {} + +test('can read objects from stream', function(t) { + var r = fromArray([{ one: '1'}, { two: '2' }]); + + var v1 = r.read(); + var v2 = r.read(); + var v3 = r.read(); + + assert.deepEqual(v1, { one: '1' }); + assert.deepEqual(v2, { two: '2' }); + assert.deepEqual(v3, null); + + t.end(); +}); + +test('can pipe objects into stream', function(t) { + var r = fromArray([{ one: '1'}, { two: '2' }]); + + r.pipe(toArray(function(list) { + assert.deepEqual(list, [ + { one: '1' }, + { two: '2' } + ]); + + t.end(); + })); +}); + +test('read(n) is ignored', function(t) { + var r = fromArray([{ one: '1'}, { two: '2' }]); + + var value = r.read(2); + + assert.deepEqual(value, { one: '1' }); + + t.end(); +}); + +test('can read objects from _read (sync)', function(t) { + var r = new Readable({ objectMode: true }); + var list = [{ one: '1'}, { two: '2' }]; + r._read = function(n) { + var item = list.shift(); + r.push(item || null); + }; + + r.pipe(toArray(function(list) { + assert.deepEqual(list, [ + { one: '1' }, + { two: '2' } + ]); + + t.end(); + })); +}); + +test('can read objects from _read (async)', function(t) { + var r = new Readable({ objectMode: true }); + var list = [{ one: '1'}, { two: '2' }]; + r._read = function(n) { + var item = list.shift(); + process.nextTick(function() { + r.push(item || null); + }); + }; + + r.pipe(toArray(function(list) { + assert.deepEqual(list, [ + { one: '1' }, + { two: '2' } + ]); + + t.end(); + })); +}); + +test('can read strings as objects', function(t) { + var r = new Readable({ + objectMode: true + }); + r._read = noop; + var list = ['one', 'two', 'three']; + list.forEach(function(str) { + r.push(str); + }); + r.push(null); + + r.pipe(toArray(function(array) { + assert.deepEqual(array, list); + + t.end(); + })); +}); + +test('read(0) for object streams', function(t) { + var r = new Readable({ + objectMode: true + }); + r._read = noop; + + r.push('foobar'); + r.push(null); + + var v = r.read(0); + + r.pipe(toArray(function(array) { + assert.deepEqual(array, ['foobar']); + + t.end(); + })); +}); + +test('falsey values', function(t) { + var r = new Readable({ + objectMode: true + }); + r._read = noop; + + r.push(false); + r.push(0); + r.push(''); + r.push(null); + + r.pipe(toArray(function(array) { + assert.deepEqual(array, [false, 0, '']); + + t.end(); + })); +}); + +test('high watermark _read', function(t) { + var r = new Readable({ + highWaterMark: 6, + objectMode: true + }); + var calls = 0; + var list = ['1', '2', '3', '4', '5', '6', '7', '8']; + + r._read = function(n) { + calls++; + }; + + list.forEach(function(c) { + r.push(c); + }); + + var v = r.read(); + + assert.equal(calls, 0); + assert.equal(v, '1'); + + var v2 = r.read(); + assert.equal(v2, '2'); + + var v3 = r.read(); + assert.equal(v3, '3'); + + assert.equal(calls, 1); + + t.end(); +}); + +test('high watermark push', function(t) { + var r = new Readable({ + highWaterMark: 6, + objectMode: true + }); + r._read = function(n) {}; + for (var i = 0; i < 6; i++) { + var bool = r.push(i); + assert.equal(bool, i === 5 ? false : true); + } + + t.end(); +}); + +test('can write objects to stream', function(t) { + var w = new Writable({ objectMode: true }); + + w._write = function(chunk, encoding, cb) { + assert.deepEqual(chunk, { foo: 'bar' }); + cb(); + }; + + w.on('finish', function() { + t.end(); + }); + + w.write({ foo: 'bar' }); + w.end(); +}); + +test('can write multiple objects to stream', function(t) { + var w = new Writable({ objectMode: true }); + var list = []; + + w._write = function(chunk, encoding, cb) { + list.push(chunk); + cb(); + }; + + w.on('finish', function() { + assert.deepEqual(list, [0, 1, 2, 3, 4]); + + t.end(); + }); + + w.write(0); + w.write(1); + w.write(2); + w.write(3); + w.write(4); + w.end(); +}); + +test('can write strings as objects', function(t) { + var w = new Writable({ + objectMode: true + }); + var list = []; + + w._write = function(chunk, encoding, cb) { + list.push(chunk); + process.nextTick(cb); + }; + + w.on('finish', function() { + assert.deepEqual(list, ['0', '1', '2', '3', '4']); + + t.end(); + }); + + w.write('0'); + w.write('1'); + w.write('2'); + w.write('3'); + w.write('4'); + w.end(); +}); + +test('buffers finish until cb is called', function(t) { + var w = new Writable({ + objectMode: true + }); + var called = false; + + w._write = function(chunk, encoding, cb) { + assert.equal(chunk, 'foo'); + + process.nextTick(function() { + called = true; + cb(); + }); + }; + + w.on('finish', function() { + assert.equal(called, true); + + t.end(); + }); + + w.write('foo'); + w.end(); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-pipe-error-handling.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-pipe-error-handling.js new file mode 100644 index 0000000..e3f3e4e --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-pipe-error-handling.js @@ -0,0 +1,105 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var stream = require('../../'); + +(function testErrorListenerCatches() { + var count = 1000; + + var source = new stream.Readable(); + source._read = function(n) { + n = Math.min(count, n); + count -= n; + source.push(new Buffer(n)); + }; + + var unpipedDest; + source.unpipe = function(dest) { + unpipedDest = dest; + stream.Readable.prototype.unpipe.call(this, dest); + }; + + var dest = new stream.Writable(); + dest._write = function(chunk, encoding, cb) { + cb(); + }; + + source.pipe(dest); + + var gotErr = null; + dest.on('error', function(err) { + gotErr = err; + }); + + var unpipedSource; + dest.on('unpipe', function(src) { + unpipedSource = src; + }); + + var err = new Error('This stream turned into bacon.'); + dest.emit('error', err); + assert.strictEqual(gotErr, err); + assert.strictEqual(unpipedSource, source); + assert.strictEqual(unpipedDest, dest); +})(); + +(function testErrorWithoutListenerThrows() { + var count = 1000; + + var source = new stream.Readable(); + source._read = function(n) { + n = Math.min(count, n); + count -= n; + source.push(new Buffer(n)); + }; + + var unpipedDest; + source.unpipe = function(dest) { + unpipedDest = dest; + stream.Readable.prototype.unpipe.call(this, dest); + }; + + var dest = new stream.Writable(); + dest._write = function(chunk, encoding, cb) { + cb(); + }; + + source.pipe(dest); + + var unpipedSource; + dest.on('unpipe', function(src) { + unpipedSource = src; + }); + + var err = new Error('This stream turned into bacon.'); + + var gotErr = null; + try { + dest.emit('error', err); + } catch (e) { + gotErr = e; + } + assert.strictEqual(gotErr, err); + assert.strictEqual(unpipedSource, source); + assert.strictEqual(unpipedDest, dest); +})(); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-pipe-error-once-listener.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-pipe-error-once-listener.js new file mode 100644 index 0000000..53b2616 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-pipe-error-once-listener.js @@ -0,0 +1,64 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + +var common = require('../common.js'); +var assert = require('assert'); + +var util = require('util'); +var stream = require('../../'); + + +var Read = function() { + stream.Readable.call(this); +}; +util.inherits(Read, stream.Readable); + +Read.prototype._read = function(size) { + this.push('x'); + this.push(null); +}; + + +var Write = function() { + stream.Writable.call(this); +}; +util.inherits(Write, stream.Writable); + +Write.prototype._write = function(buffer, encoding, cb) { + this.emit('error', new Error('boom')); + this.emit('alldone'); +}; + +var read = new Read(); +var write = new Write(); + +write.once('error', function(err) {}); +write.once('alldone', function(err) { + console.log('ok'); +}); + +process.on('exit', function(c) { + console.error('error thrown even with listener'); +}); + +read.pipe(write); + diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-push.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-push.js new file mode 100644 index 0000000..eb2b0e9 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-push.js @@ -0,0 +1,138 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common.js'); +var stream = require('../../'); +var Readable = stream.Readable; +var Writable = stream.Writable; +var assert = require('assert'); + +var util = require('util'); +var EE = require('events').EventEmitter; + + +// a mock thing a bit like the net.Socket/tcp_wrap.handle interaction + +var stream = new Readable({ + highWaterMark: 16, + encoding: 'utf8' +}); + +var source = new EE; + +stream._read = function() { + console.error('stream._read'); + readStart(); +}; + +var ended = false; +stream.on('end', function() { + ended = true; +}); + +source.on('data', function(chunk) { + var ret = stream.push(chunk); + console.error('data', stream._readableState.length); + if (!ret) + readStop(); +}); + +source.on('end', function() { + stream.push(null); +}); + +var reading = false; + +function readStart() { + console.error('readStart'); + reading = true; +} + +function readStop() { + console.error('readStop'); + reading = false; + process.nextTick(function() { + var r = stream.read(); + if (r !== null) + writer.write(r); + }); +} + +var writer = new Writable({ + decodeStrings: false +}); + +var written = []; + +var expectWritten = + [ 'asdfgasdfgasdfgasdfg', + 'asdfgasdfgasdfgasdfg', + 'asdfgasdfgasdfgasdfg', + 'asdfgasdfgasdfgasdfg', + 'asdfgasdfgasdfgasdfg', + 'asdfgasdfgasdfgasdfg' ]; + +writer._write = function(chunk, encoding, cb) { + console.error('WRITE %s', chunk); + written.push(chunk); + process.nextTick(cb); +}; + +writer.on('finish', finish); + + +// now emit some chunks. + +var chunk = "asdfg"; + +var set = 0; +readStart(); +data(); +function data() { + assert(reading); + source.emit('data', chunk); + assert(reading); + source.emit('data', chunk); + assert(reading); + source.emit('data', chunk); + assert(reading); + source.emit('data', chunk); + assert(!reading); + if (set++ < 5) + setTimeout(data, 10); + else + end(); +} + +function finish() { + console.error('finish'); + assert.deepEqual(written, expectWritten); + console.log('ok'); +} + +function end() { + source.emit('end'); + assert(!reading); + writer.end(stream.read()); + setTimeout(function() { + assert(ended); + }); +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-read-sync-stack.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-read-sync-stack.js new file mode 100644 index 0000000..9740a47 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-read-sync-stack.js @@ -0,0 +1,54 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var Readable = require('../../').Readable; +var r = new Readable(); +var N = 256 * 1024; + +// Go ahead and allow the pathological case for this test. +// Yes, it's an infinite loop, that's the point. +process.maxTickDepth = N + 2; + +var reads = 0; +r._read = function(n) { + var chunk = reads++ === N ? null : new Buffer(1); + r.push(chunk); +}; + +r.on('readable', function onReadable() { + if (!(r._readableState.length % 256)) + console.error('readable', r._readableState.length); + r.read(N * 2); +}); + +var ended = false; +r.on('end', function onEnd() { + ended = true; +}); + +r.read(0); + +process.on('exit', function() { + assert(ended); + console.log('ok'); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-readable-empty-buffer-no-eof.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-readable-empty-buffer-no-eof.js new file mode 100644 index 0000000..4b1659d --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-readable-empty-buffer-no-eof.js @@ -0,0 +1,89 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +var Readable = require('../../').Readable; + +test1(); + +function test1() { + var r = new Readable(); + + // should not end when we get a Buffer(0) or '' as the _read result + // that just means that there is *temporarily* no data, but to go + // ahead and try again later. + // + // note that this is very unusual. it only works for crypto streams + // because the other side of the stream will call read(0) to cycle + // data through openssl. that's why we set the timeouts to call + // r.read(0) again later, otherwise there is no more work being done + // and the process just exits. + + var buf = new Buffer(5); + buf.fill('x'); + var reads = 5; + r._read = function(n) { + switch (reads--) { + case 0: + return r.push(null); // EOF + case 1: + return r.push(buf); + case 2: + setTimeout(r.read.bind(r, 0), 10); + return r.push(new Buffer(0)); // Not-EOF! + case 3: + setTimeout(r.read.bind(r, 0), 10); + return process.nextTick(function() { + return r.push(new Buffer(0)); + }); + case 4: + setTimeout(r.read.bind(r, 0), 10); + return setTimeout(function() { + return r.push(new Buffer(0)); + }); + case 5: + return setTimeout(function() { + return r.push(buf); + }); + default: + throw new Error('unreachable'); + } + }; + + var results = []; + function flow() { + var chunk; + while (null !== (chunk = r.read())) + results.push(chunk + ''); + } + r.on('readable', flow); + r.on('end', function() { + results.push('EOF'); + }); + flow(); + + process.on('exit', function() { + assert.deepEqual(results, [ 'xxxxx', 'xxxxx', 'EOF' ]); + console.log('ok'); + }); +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-readable-from-list.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-readable-from-list.js new file mode 100644 index 0000000..04a96f5 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-readable-from-list.js @@ -0,0 +1,120 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var assert = require('assert'); +var common = require('../common.js'); +var fromList = require('../../lib/_stream_readable')._fromList; + +// tiny node-tap lookalike. +var tests = []; +var count = 0; + +function test(name, fn) { + count++; + tests.push([name, fn]); +} + +function run() { + var next = tests.shift(); + if (!next) + return console.error('ok'); + + var name = next[0]; + var fn = next[1]; + console.log('# %s', name); + fn({ + same: assert.deepEqual, + equal: assert.equal, + end: function () { + count--; + run(); + } + }); +} + +// ensure all tests have run +process.on("exit", function () { + assert.equal(count, 0); +}); + +process.nextTick(run); + + + +test('buffers', function(t) { + // have a length + var len = 16; + var list = [ new Buffer('foog'), + new Buffer('bark'), + new Buffer('bazy'), + new Buffer('kuel') ]; + + // read more than the first element. + var ret = fromList(6, { buffer: list, length: 16 }); + t.equal(ret.toString(), 'foogba'); + + // read exactly the first element. + ret = fromList(2, { buffer: list, length: 10 }); + t.equal(ret.toString(), 'rk'); + + // read less than the first element. + ret = fromList(2, { buffer: list, length: 8 }); + t.equal(ret.toString(), 'ba'); + + // read more than we have. + ret = fromList(100, { buffer: list, length: 6 }); + t.equal(ret.toString(), 'zykuel'); + + // all consumed. + t.same(list, []); + + t.end(); +}); + +test('strings', function(t) { + // have a length + var len = 16; + var list = [ 'foog', + 'bark', + 'bazy', + 'kuel' ]; + + // read more than the first element. + var ret = fromList(6, { buffer: list, length: 16, decoder: true }); + t.equal(ret, 'foogba'); + + // read exactly the first element. + ret = fromList(2, { buffer: list, length: 10, decoder: true }); + t.equal(ret, 'rk'); + + // read less than the first element. + ret = fromList(2, { buffer: list, length: 8, decoder: true }); + t.equal(ret, 'ba'); + + // read more than we have. + ret = fromList(100, { buffer: list, length: 6, decoder: true }); + t.equal(ret, 'zykuel'); + + // all consumed. + t.same(list, []); + + t.end(); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-readable-legacy-drain.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-readable-legacy-drain.js new file mode 100644 index 0000000..51fd3d5 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-readable-legacy-drain.js @@ -0,0 +1,75 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +var Stream = require('../../'); +var Readable = Stream.Readable; + +var r = new Readable(); +var N = 256; +var reads = 0; +r._read = function(n) { + return r.push(++reads === N ? null : new Buffer(1)); +}; + +var rended = false; +r.on('end', function() { + rended = true; +}); + +var w = new Stream(); +w.writable = true; +var writes = 0; +var buffered = 0; +w.write = function(c) { + writes += c.length; + buffered += c.length; + process.nextTick(drain); + return false; +}; + +function drain() { + assert(buffered <= 2); + buffered = 0; + w.emit('drain'); +} + + +var wended = false; +w.end = function() { + wended = true; +}; + +// Just for kicks, let's mess with the drain count. +// This verifies that even if it gets negative in the +// pipe() cleanup function, we'll still function properly. +r.on('readable', function() { + w.emit('drain'); +}); + +r.pipe(w); +process.on('exit', function() { + assert(rended); + assert(wended); + console.error('ok'); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-readable-non-empty-end.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-readable-non-empty-end.js new file mode 100644 index 0000000..c971898 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-readable-non-empty-end.js @@ -0,0 +1,78 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var assert = require('assert'); +var common = require('../common.js'); +var Readable = require('../../lib/_stream_readable'); + +var len = 0; +var chunks = new Array(10); +for (var i = 1; i <= 10; i++) { + chunks[i-1] = new Buffer(i); + len += i; +} + +var test = new Readable(); +var n = 0; +test._read = function(size) { + var chunk = chunks[n++]; + setTimeout(function() { + test.push(chunk); + }); +}; + +test.on('end', thrower); +function thrower() { + throw new Error('this should not happen!'); +} + +var bytesread = 0; +test.on('readable', function() { + var b = len - bytesread - 1; + var res = test.read(b); + if (res) { + bytesread += res.length; + console.error('br=%d len=%d', bytesread, len); + setTimeout(next); + } + test.read(0); +}); +test.read(0); + +function next() { + // now let's make 'end' happen + test.removeListener('end', thrower); + + var endEmitted = false; + process.on('exit', function() { + assert(endEmitted, 'end should be emitted by now'); + }); + test.on('end', function() { + endEmitted = true; + }); + + // one to get the last byte + var r = test.read(); + assert(r); + assert.equal(r.length, 1); + r = test.read(); + assert.equal(r, null); +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-readable-wrap-empty.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-readable-wrap-empty.js new file mode 100644 index 0000000..fd8a3dc --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-readable-wrap-empty.js @@ -0,0 +1,43 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +var Readable = require('../../lib/_stream_readable'); +var EE = require('events').EventEmitter; + +var oldStream = new EE(); +oldStream.pause = function(){}; +oldStream.resume = function(){}; + +var newStream = new Readable().wrap(oldStream); + +var ended = false; +newStream + .on('readable', function(){}) + .on('end', function(){ ended = true; }); + +oldStream.emit('end'); + +process.on('exit', function(){ + assert.ok(ended); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-readable-wrap.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-readable-wrap.js new file mode 100644 index 0000000..6b177f7 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-readable-wrap.js @@ -0,0 +1,105 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +var Readable = require('../../lib/_stream_readable'); +var Writable = require('../../lib/_stream_writable'); +var EE = require('events').EventEmitter; + +var testRuns = 0, completedRuns = 0; +function runTest(highWaterMark, objectMode, produce) { + testRuns++; + + var old = new EE; + var r = new Readable({ highWaterMark: highWaterMark, objectMode: objectMode }); + assert.equal(r, r.wrap(old)); + + var ended = false; + r.on('end', function() { + ended = true; + }); + + old.pause = function() { + console.error('old.pause()'); + old.emit('pause'); + flowing = false; + }; + + old.resume = function() { + console.error('old.resume()'); + old.emit('resume'); + flow(); + }; + + var flowing; + var chunks = 10; + var oldEnded = false; + var expected = []; + function flow() { + flowing = true; + while (flowing && chunks-- > 0) { + var item = produce(); + expected.push(item); + console.log('old.emit', chunks, flowing); + old.emit('data', item); + console.log('after emit', chunks, flowing); + } + if (chunks <= 0) { + oldEnded = true; + console.log('old end', chunks, flowing); + old.emit('end'); + } + } + + var w = new Writable({ highWaterMark: highWaterMark * 2, objectMode: objectMode }); + var written = []; + w._write = function(chunk, encoding, cb) { + console.log('_write', chunk); + written.push(chunk); + setTimeout(cb); + }; + + w.on('finish', function() { + completedRuns++; + performAsserts(); + }); + + r.pipe(w); + + flow(); + + function performAsserts() { + assert(ended); + assert(oldEnded); + assert.deepEqual(written, expected); + } +} + +runTest(100, false, function(){ return new Buffer(100); }); +runTest(10, false, function(){ return new Buffer('xxxxxxxxxx'); }); +runTest(1, true, function(){ return { foo: 'bar' }; }); + +process.on('exit', function() { + assert.equal(testRuns, completedRuns); + console.log('ok'); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-set-encoding.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-set-encoding.js new file mode 100644 index 0000000..685531b --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-set-encoding.js @@ -0,0 +1,361 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + +var common = require('../common.js'); +var assert = require('assert'); +var R = require('../../lib/_stream_readable'); +var util = require('util'); + +// tiny node-tap lookalike. +var tests = []; +var count = 0; + +function test(name, fn) { + count++; + tests.push([name, fn]); +} + +function run() { + var next = tests.shift(); + if (!next) + return console.error('ok'); + + var name = next[0]; + var fn = next[1]; + console.log('# %s', name); + fn({ + same: assert.deepEqual, + equal: assert.equal, + end: function () { + count--; + run(); + } + }); +} + +// ensure all tests have run +process.on("exit", function () { + assert.equal(count, 0); +}); + +process.nextTick(run); + +///// + +util.inherits(TestReader, R); + +function TestReader(n, opts) { + R.call(this, opts); + + this.pos = 0; + this.len = n || 100; +} + +TestReader.prototype._read = function(n) { + setTimeout(function() { + + if (this.pos >= this.len) { + // double push(null) to test eos handling + this.push(null); + return this.push(null); + } + + n = Math.min(n, this.len - this.pos); + if (n <= 0) { + // double push(null) to test eos handling + this.push(null); + return this.push(null); + } + + this.pos += n; + var ret = new Buffer(n); + ret.fill('a'); + + console.log("this.push(ret)", ret) + + return this.push(ret); + }.bind(this), 1); +}; + +test('setEncoding utf8', function(t) { + var tr = new TestReader(100); + tr.setEncoding('utf8'); + var out = []; + var expect = + [ 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa' ]; + + tr.on('readable', function flow() { + var chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', function() { + t.same(out, expect); + t.end(); + }); +}); + + +test('setEncoding hex', function(t) { + var tr = new TestReader(100); + tr.setEncoding('hex'); + var out = []; + var expect = + [ '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161' ]; + + tr.on('readable', function flow() { + var chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', function() { + t.same(out, expect); + t.end(); + }); +}); + +test('setEncoding hex with read(13)', function(t) { + var tr = new TestReader(100); + tr.setEncoding('hex'); + var out = []; + var expect = + [ "6161616161616", + "1616161616161", + "6161616161616", + "1616161616161", + "6161616161616", + "1616161616161", + "6161616161616", + "1616161616161", + "6161616161616", + "1616161616161", + "6161616161616", + "1616161616161", + "6161616161616", + "1616161616161", + "6161616161616", + "16161" ]; + + tr.on('readable', function flow() { + console.log("readable once") + var chunk; + while (null !== (chunk = tr.read(13))) + out.push(chunk); + }); + + tr.on('end', function() { + console.log("END") + t.same(out, expect); + t.end(); + }); +}); + +test('setEncoding base64', function(t) { + var tr = new TestReader(100); + tr.setEncoding('base64'); + var out = []; + var expect = + [ 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYQ==' ]; + + tr.on('readable', function flow() { + var chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', function() { + t.same(out, expect); + t.end(); + }); +}); + +test('encoding: utf8', function(t) { + var tr = new TestReader(100, { encoding: 'utf8' }); + var out = []; + var expect = + [ 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa', + 'aaaaaaaaaa' ]; + + tr.on('readable', function flow() { + var chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', function() { + t.same(out, expect); + t.end(); + }); +}); + + +test('encoding: hex', function(t) { + var tr = new TestReader(100, { encoding: 'hex' }); + var out = []; + var expect = + [ '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161', + '6161616161' ]; + + tr.on('readable', function flow() { + var chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', function() { + t.same(out, expect); + t.end(); + }); +}); + +test('encoding: hex with read(13)', function(t) { + var tr = new TestReader(100, { encoding: 'hex' }); + var out = []; + var expect = + [ "6161616161616", + "1616161616161", + "6161616161616", + "1616161616161", + "6161616161616", + "1616161616161", + "6161616161616", + "1616161616161", + "6161616161616", + "1616161616161", + "6161616161616", + "1616161616161", + "6161616161616", + "1616161616161", + "6161616161616", + "16161" ]; + + tr.on('readable', function flow() { + var chunk; + while (null !== (chunk = tr.read(13))) + out.push(chunk); + }); + + tr.on('end', function() { + t.same(out, expect); + t.end(); + }); +}); + +test('encoding: base64', function(t) { + var tr = new TestReader(100, { encoding: 'base64' }); + var out = []; + var expect = + [ 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYQ==' ]; + + tr.on('readable', function flow() { + var chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', function() { + t.same(out, expect); + t.end(); + }); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-transform.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-transform.js new file mode 100644 index 0000000..a0cacc6 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-transform.js @@ -0,0 +1,522 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var assert = require('assert'); +var common = require('../common.js'); +var PassThrough = require('../../').PassThrough; +var Transform = require('../../').Transform; + +// tiny node-tap lookalike. +var tests = []; +var count = 0; + +function test(name, fn) { + count++; + tests.push([name, fn]); +} + +function run() { + var next = tests.shift(); + if (!next) + return console.error('ok'); + + var name = next[0]; + var fn = next[1]; + console.log('# %s', name); + fn({ + same: assert.deepEqual, + equal: assert.equal, + ok: assert, + end: function () { + count--; + run(); + } + }); +} + +// ensure all tests have run +process.on("exit", function () { + assert.equal(count, 0); +}); + +process.nextTick(run); + +///// + +test('writable side consumption', function(t) { + var tx = new Transform({ + highWaterMark: 10 + }); + + var transformed = 0; + tx._transform = function(chunk, encoding, cb) { + transformed += chunk.length; + tx.push(chunk); + cb(); + }; + + for (var i = 1; i <= 10; i++) { + tx.write(new Buffer(i)); + } + tx.end(); + + t.equal(tx._readableState.length, 10); + t.equal(transformed, 10); + t.equal(tx._transformState.writechunk.length, 5); + t.same(tx._writableState.buffer.map(function(c) { + return c.chunk.length; + }), [6, 7, 8, 9, 10]); + + t.end(); +}); + +test('passthrough', function(t) { + var pt = new PassThrough(); + + pt.write(new Buffer('foog')); + pt.write(new Buffer('bark')); + pt.write(new Buffer('bazy')); + pt.write(new Buffer('kuel')); + pt.end(); + + t.equal(pt.read(5).toString(), 'foogb'); + t.equal(pt.read(5).toString(), 'arkba'); + t.equal(pt.read(5).toString(), 'zykue'); + t.equal(pt.read(5).toString(), 'l'); + t.end(); +}); + +test('object passthrough', function (t) { + var pt = new PassThrough({ objectMode: true }); + + pt.write(1); + pt.write(true); + pt.write(false); + pt.write(0); + pt.write('foo'); + pt.write(''); + pt.write({ a: 'b'}); + pt.end(); + + t.equal(pt.read(), 1); + t.equal(pt.read(), true); + t.equal(pt.read(), false); + t.equal(pt.read(), 0); + t.equal(pt.read(), 'foo'); + t.equal(pt.read(), ''); + t.same(pt.read(), { a: 'b'}); + t.end(); +}); + +test('simple transform', function(t) { + var pt = new Transform; + pt._transform = function(c, e, cb) { + var ret = new Buffer(c.length); + ret.fill('x'); + pt.push(ret); + cb(); + }; + + pt.write(new Buffer('foog')); + pt.write(new Buffer('bark')); + pt.write(new Buffer('bazy')); + pt.write(new Buffer('kuel')); + pt.end(); + + t.equal(pt.read(5).toString(), 'xxxxx'); + t.equal(pt.read(5).toString(), 'xxxxx'); + t.equal(pt.read(5).toString(), 'xxxxx'); + t.equal(pt.read(5).toString(), 'x'); + t.end(); +}); + +test('simple object transform', function(t) { + var pt = new Transform({ objectMode: true }); + pt._transform = function(c, e, cb) { + pt.push(JSON.stringify(c)); + cb(); + }; + + pt.write(1); + pt.write(true); + pt.write(false); + pt.write(0); + pt.write('foo'); + pt.write(''); + pt.write({ a: 'b'}); + pt.end(); + + t.equal(pt.read(), '1'); + t.equal(pt.read(), 'true'); + t.equal(pt.read(), 'false'); + t.equal(pt.read(), '0'); + t.equal(pt.read(), '"foo"'); + t.equal(pt.read(), '""'); + t.equal(pt.read(), '{"a":"b"}'); + t.end(); +}); + +test('async passthrough', function(t) { + var pt = new Transform; + pt._transform = function(chunk, encoding, cb) { + setTimeout(function() { + pt.push(chunk); + cb(); + }, 10); + }; + + pt.write(new Buffer('foog')); + pt.write(new Buffer('bark')); + pt.write(new Buffer('bazy')); + pt.write(new Buffer('kuel')); + pt.end(); + + pt.on('finish', function() { + t.equal(pt.read(5).toString(), 'foogb'); + t.equal(pt.read(5).toString(), 'arkba'); + t.equal(pt.read(5).toString(), 'zykue'); + t.equal(pt.read(5).toString(), 'l'); + t.end(); + }); +}); + +test('assymetric transform (expand)', function(t) { + var pt = new Transform; + + // emit each chunk 2 times. + pt._transform = function(chunk, encoding, cb) { + setTimeout(function() { + pt.push(chunk); + setTimeout(function() { + pt.push(chunk); + cb(); + }, 10) + }, 10); + }; + + pt.write(new Buffer('foog')); + pt.write(new Buffer('bark')); + pt.write(new Buffer('bazy')); + pt.write(new Buffer('kuel')); + pt.end(); + + pt.on('finish', function() { + t.equal(pt.read(5).toString(), 'foogf'); + t.equal(pt.read(5).toString(), 'oogba'); + t.equal(pt.read(5).toString(), 'rkbar'); + t.equal(pt.read(5).toString(), 'kbazy'); + t.equal(pt.read(5).toString(), 'bazyk'); + t.equal(pt.read(5).toString(), 'uelku'); + t.equal(pt.read(5).toString(), 'el'); + t.end(); + }); +}); + +test('assymetric transform (compress)', function(t) { + var pt = new Transform; + + // each output is the first char of 3 consecutive chunks, + // or whatever's left. + pt.state = ''; + + pt._transform = function(chunk, encoding, cb) { + if (!chunk) + chunk = ''; + var s = chunk.toString(); + setTimeout(function() { + this.state += s.charAt(0); + if (this.state.length === 3) { + pt.push(new Buffer(this.state)); + this.state = ''; + } + cb(); + }.bind(this), 10); + }; + + pt._flush = function(cb) { + // just output whatever we have. + pt.push(new Buffer(this.state)); + this.state = ''; + cb(); + }; + + pt.write(new Buffer('aaaa')); + pt.write(new Buffer('bbbb')); + pt.write(new Buffer('cccc')); + pt.write(new Buffer('dddd')); + pt.write(new Buffer('eeee')); + pt.write(new Buffer('aaaa')); + pt.write(new Buffer('bbbb')); + pt.write(new Buffer('cccc')); + pt.write(new Buffer('dddd')); + pt.write(new Buffer('eeee')); + pt.write(new Buffer('aaaa')); + pt.write(new Buffer('bbbb')); + pt.write(new Buffer('cccc')); + pt.write(new Buffer('dddd')); + pt.end(); + + // 'abcdeabcdeabcd' + pt.on('finish', function() { + t.equal(pt.read(5).toString(), 'abcde'); + t.equal(pt.read(5).toString(), 'abcde'); + t.equal(pt.read(5).toString(), 'abcd'); + t.end(); + }); +}); + +// this tests for a stall when data is written to a full stream +// that has empty transforms. +test('complex transform', function(t) { + var count = 0; + var saved = null; + var pt = new Transform({highWaterMark:3}); + pt._transform = function(c, e, cb) { + if (count++ === 1) + saved = c; + else { + if (saved) { + pt.push(saved); + saved = null; + } + pt.push(c); + } + + cb(); + }; + + pt.once('readable', function() { + process.nextTick(function() { + pt.write(new Buffer('d')); + pt.write(new Buffer('ef'), function() { + pt.end(); + t.end(); + }); + t.equal(pt.read().toString(), 'abcdef'); + t.equal(pt.read(), null); + }); + }); + + pt.write(new Buffer('abc')); +}); + + +test('passthrough event emission', function(t) { + var pt = new PassThrough(); + var emits = 0; + pt.on('readable', function() { + var state = pt._readableState; + console.error('>>> emit readable %d', emits); + emits++; + }); + + var i = 0; + + pt.write(new Buffer('foog')); + + console.error('need emit 0'); + pt.write(new Buffer('bark')); + + console.error('should have emitted readable now 1 === %d', emits); + t.equal(emits, 1); + + t.equal(pt.read(5).toString(), 'foogb'); + t.equal(pt.read(5) + '', 'null'); + + console.error('need emit 1'); + + pt.write(new Buffer('bazy')); + console.error('should have emitted, but not again'); + pt.write(new Buffer('kuel')); + + console.error('should have emitted readable now 2 === %d', emits); + t.equal(emits, 2); + + t.equal(pt.read(5).toString(), 'arkba'); + t.equal(pt.read(5).toString(), 'zykue'); + t.equal(pt.read(5), null); + + console.error('need emit 2'); + + pt.end(); + + t.equal(emits, 3); + + t.equal(pt.read(5).toString(), 'l'); + t.equal(pt.read(5), null); + + console.error('should not have emitted again'); + t.equal(emits, 3); + t.end(); +}); + +test('passthrough event emission reordered', function(t) { + var pt = new PassThrough; + var emits = 0; + pt.on('readable', function() { + console.error('emit readable', emits) + emits++; + }); + + pt.write(new Buffer('foog')); + console.error('need emit 0'); + pt.write(new Buffer('bark')); + console.error('should have emitted readable now 1 === %d', emits); + t.equal(emits, 1); + + t.equal(pt.read(5).toString(), 'foogb'); + t.equal(pt.read(5), null); + + console.error('need emit 1'); + pt.once('readable', function() { + t.equal(pt.read(5).toString(), 'arkba'); + + t.equal(pt.read(5), null); + + console.error('need emit 2'); + pt.once('readable', function() { + t.equal(pt.read(5).toString(), 'zykue'); + t.equal(pt.read(5), null); + pt.once('readable', function() { + t.equal(pt.read(5).toString(), 'l'); + t.equal(pt.read(5), null); + t.equal(emits, 4); + t.end(); + }); + pt.end(); + }); + pt.write(new Buffer('kuel')); + }); + + pt.write(new Buffer('bazy')); +}); + +test('passthrough facaded', function(t) { + console.error('passthrough facaded'); + var pt = new PassThrough; + var datas = []; + pt.on('data', function(chunk) { + datas.push(chunk.toString()); + }); + + pt.on('end', function() { + t.same(datas, ['foog', 'bark', 'bazy', 'kuel']); + t.end(); + }); + + pt.write(new Buffer('foog')); + setTimeout(function() { + pt.write(new Buffer('bark')); + setTimeout(function() { + pt.write(new Buffer('bazy')); + setTimeout(function() { + pt.write(new Buffer('kuel')); + setTimeout(function() { + pt.end(); + }, 10); + }, 10); + }, 10); + }, 10); +}); + +test('object transform (json parse)', function(t) { + console.error('json parse stream'); + var jp = new Transform({ objectMode: true }); + jp._transform = function(data, encoding, cb) { + try { + jp.push(JSON.parse(data)); + cb(); + } catch (er) { + cb(er); + } + }; + + // anything except null/undefined is fine. + // those are "magic" in the stream API, because they signal EOF. + var objects = [ + { foo: 'bar' }, + 100, + "string", + { nested: { things: [ { foo: 'bar' }, 100, "string" ] } } + ]; + + var ended = false; + jp.on('end', function() { + ended = true; + }); + + objects.forEach(function(obj) { + jp.write(JSON.stringify(obj)); + var res = jp.read(); + t.same(res, obj); + }); + + jp.end(); + // read one more time to get the 'end' event + jp.read(); + + process.nextTick(function() { + t.ok(ended); + t.end(); + }) +}); + +test('object transform (json stringify)', function(t) { + console.error('json parse stream'); + var js = new Transform({ objectMode: true }); + js._transform = function(data, encoding, cb) { + try { + js.push(JSON.stringify(data)); + cb(); + } catch (er) { + cb(er); + } + }; + + // anything except null/undefined is fine. + // those are "magic" in the stream API, because they signal EOF. + var objects = [ + { foo: 'bar' }, + 100, + "string", + { nested: { things: [ { foo: 'bar' }, 100, "string" ] } } + ]; + + var ended = false; + js.on('end', function() { + ended = true; + }); + + objects.forEach(function(obj) { + js.write(obj); + var res = js.read(); + t.equal(res, JSON.stringify(obj)); + }); + + js.end(); + // read one more time to get the 'end' event + js.read(); + + process.nextTick(function() { + t.ok(ended); + t.end(); + }) +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-unpipe-drain.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-unpipe-drain.js new file mode 100644 index 0000000..365b327 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-unpipe-drain.js @@ -0,0 +1,76 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + +var common = require('../common.js'); +var assert = require('assert'); +var stream = require('../../'); +var crypto = require('crypto'); + +var util = require('util'); + +function TestWriter() { + stream.Writable.call(this); +} +util.inherits(TestWriter, stream.Writable); + +TestWriter.prototype._write = function (buffer, encoding, callback) { + console.log('write called'); + // super slow write stream (callback never called) +}; + +var dest = new TestWriter(); + +function TestReader(id) { + stream.Readable.call(this); + this.reads = 0; +} +util.inherits(TestReader, stream.Readable); + +TestReader.prototype._read = function (size) { + this.reads += 1; + this.push(crypto.randomBytes(size)); +}; + +var src1 = new TestReader(); +var src2 = new TestReader(); + +src1.pipe(dest); + +src1.once('readable', function () { + process.nextTick(function () { + + src2.pipe(dest); + + src2.once('readable', function () { + process.nextTick(function () { + + src1.unpipe(dest); + }); + }); + }); +}); + + +process.on('exit', function () { + assert.equal(src1.reads, 2); + assert.equal(src2.reads, 2); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-unpipe-leak.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-unpipe-leak.js new file mode 100644 index 0000000..17c92ae --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-unpipe-leak.js @@ -0,0 +1,75 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + +var common = require('../common.js'); +var assert = require('assert'); +var stream = require('../../'); + +var chunk = new Buffer('hallo'); + +var util = require('util'); + +function TestWriter() { + stream.Writable.call(this); +} +util.inherits(TestWriter, stream.Writable); + +TestWriter.prototype._write = function(buffer, encoding, callback) { + callback(null); +}; + +var dest = new TestWriter(); + +// Set this high so that we'd trigger a nextTick warning +// and/or RangeError if we do maybeReadMore wrong. +function TestReader() { + stream.Readable.call(this, { highWaterMark: 0x10000 }); +} +util.inherits(TestReader, stream.Readable); + +TestReader.prototype._read = function(size) { + this.push(chunk); +}; + +var src = new TestReader(); + +for (var i = 0; i < 10; i++) { + src.pipe(dest); + src.unpipe(dest); +} + +assert.equal(src.listeners('end').length, 0); +assert.equal(src.listeners('readable').length, 0); + +assert.equal(dest.listeners('unpipe').length, 0); +assert.equal(dest.listeners('drain').length, 0); +assert.equal(dest.listeners('error').length, 0); +assert.equal(dest.listeners('close').length, 0); +assert.equal(dest.listeners('finish').length, 0); + +console.error(src._readableState); +process.on('exit', function() { + src._readableState.buffer.length = 0; + console.error(src._readableState); + assert(src._readableState.length >= src._readableState.highWaterMark); + console.log('ok'); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-writable.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-writable.js new file mode 100644 index 0000000..209c3a6 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream2-writable.js @@ -0,0 +1,393 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common.js'); +var W = require('../../').Writable; +var D = require('../../').Duplex; +var assert = require('assert'); + +var util = require('util'); +util.inherits(TestWriter, W); + +function TestWriter() { + W.apply(this, arguments); + this.buffer = []; + this.written = 0; +} + +TestWriter.prototype._write = function(chunk, encoding, cb) { + // simulate a small unpredictable latency + setTimeout(function() { + this.buffer.push(chunk.toString()); + this.written += chunk.length; + cb(); + }.bind(this), Math.floor(Math.random() * 10)); +}; + +var chunks = new Array(50); +for (var i = 0; i < chunks.length; i++) { + chunks[i] = new Array(i + 1).join('x'); +} + +// tiny node-tap lookalike. +var tests = []; +var count = 0; + +function test(name, fn) { + count++; + tests.push([name, fn]); +} + +function run() { + var next = tests.shift(); + if (!next) + return console.error('ok'); + + var name = next[0]; + var fn = next[1]; + console.log('# %s', name); + fn({ + same: assert.deepEqual, + equal: assert.equal, + end: function () { + count--; + run(); + } + }); +} + +// ensure all tests have run +process.on("exit", function () { + assert.equal(count, 0); +}); + +process.nextTick(run); + +test('write fast', function(t) { + var tw = new TestWriter({ + highWaterMark: 100 + }); + + tw.on('finish', function() { + t.same(tw.buffer, chunks, 'got chunks in the right order'); + t.end(); + }); + + chunks.forEach(function(chunk) { + // screw backpressure. Just buffer it all up. + tw.write(chunk); + }); + tw.end(); +}); + +test('write slow', function(t) { + var tw = new TestWriter({ + highWaterMark: 100 + }); + + tw.on('finish', function() { + t.same(tw.buffer, chunks, 'got chunks in the right order'); + t.end(); + }); + + var i = 0; + (function W() { + tw.write(chunks[i++]); + if (i < chunks.length) + setTimeout(W, 10); + else + tw.end(); + })(); +}); + +test('write backpressure', function(t) { + var tw = new TestWriter({ + highWaterMark: 50 + }); + + var drains = 0; + + tw.on('finish', function() { + t.same(tw.buffer, chunks, 'got chunks in the right order'); + t.equal(drains, 17); + t.end(); + }); + + tw.on('drain', function() { + drains++; + }); + + var i = 0; + (function W() { + do { + var ret = tw.write(chunks[i++]); + } while (ret !== false && i < chunks.length); + + if (i < chunks.length) { + assert(tw._writableState.length >= 50); + tw.once('drain', W); + } else { + tw.end(); + } + })(); +}); + +test('write bufferize', function(t) { + var tw = new TestWriter({ + highWaterMark: 100 + }); + + var encodings = + [ 'hex', + 'utf8', + 'utf-8', + 'ascii', + 'binary', + 'base64', + 'ucs2', + 'ucs-2', + 'utf16le', + 'utf-16le', + undefined ]; + + tw.on('finish', function() { + t.same(tw.buffer, chunks, 'got the expected chunks'); + }); + + chunks.forEach(function(chunk, i) { + var enc = encodings[ i % encodings.length ]; + chunk = new Buffer(chunk); + tw.write(chunk.toString(enc), enc); + }); + t.end(); +}); + +test('write no bufferize', function(t) { + var tw = new TestWriter({ + highWaterMark: 100, + decodeStrings: false + }); + + tw._write = function(chunk, encoding, cb) { + assert(typeof chunk === 'string'); + chunk = new Buffer(chunk, encoding); + return TestWriter.prototype._write.call(this, chunk, encoding, cb); + }; + + var encodings = + [ 'hex', + 'utf8', + 'utf-8', + 'ascii', + 'binary', + 'base64', + 'ucs2', + 'ucs-2', + 'utf16le', + 'utf-16le', + undefined ]; + + tw.on('finish', function() { + t.same(tw.buffer, chunks, 'got the expected chunks'); + }); + + chunks.forEach(function(chunk, i) { + var enc = encodings[ i % encodings.length ]; + chunk = new Buffer(chunk); + tw.write(chunk.toString(enc), enc); + }); + t.end(); +}); + +test('write callbacks', function (t) { + var callbacks = chunks.map(function(chunk, i) { + return [i, function(er) { + callbacks._called[i] = chunk; + }]; + }).reduce(function(set, x) { + set['callback-' + x[0]] = x[1]; + return set; + }, {}); + callbacks._called = []; + + var tw = new TestWriter({ + highWaterMark: 100 + }); + + tw.on('finish', function() { + process.nextTick(function() { + t.same(tw.buffer, chunks, 'got chunks in the right order'); + t.same(callbacks._called, chunks, 'called all callbacks'); + t.end(); + }); + }); + + chunks.forEach(function(chunk, i) { + tw.write(chunk, callbacks['callback-' + i]); + }); + tw.end(); +}); + +test('end callback', function (t) { + var tw = new TestWriter(); + tw.end(function () { + t.end(); + }); +}); + +test('end callback with chunk', function (t) { + var tw = new TestWriter(); + tw.end(new Buffer('hello world'), function () { + t.end(); + }); +}); + +test('end callback with chunk and encoding', function (t) { + var tw = new TestWriter(); + tw.end('hello world', 'ascii', function () { + t.end(); + }); +}); + +test('end callback after .write() call', function (t) { + var tw = new TestWriter(); + tw.write(new Buffer('hello world')); + tw.end(function () { + t.end(); + }); +}); + +test('end callback called after write callback', function (t) { + var tw = new TestWriter(); + var writeCalledback = false; + tw.write(new Buffer('hello world'), function() { + writeCalledback = true; + }); + tw.end(function () { + t.equal(writeCalledback, true); + t.end(); + }); +}); + +test('encoding should be ignored for buffers', function(t) { + var tw = new W(); + var hex = '018b5e9a8f6236ffe30e31baf80d2cf6eb'; + tw._write = function(chunk, encoding, cb) { + t.equal(chunk.toString('hex'), hex); + t.end(); + }; + var buf = new Buffer(hex, 'hex'); + tw.write(buf, 'binary'); +}); + +test('writables are not pipable', function(t) { + var w = new W(); + w._write = function() {}; + var gotError = false; + w.on('error', function(er) { + gotError = true; + }); + w.pipe(process.stdout); + assert(gotError); + t.end(); +}); + +test('duplexes are pipable', function(t) { + var d = new D(); + d._read = function() {}; + d._write = function() {}; + var gotError = false; + d.on('error', function(er) { + gotError = true; + }); + d.pipe(process.stdout); + assert(!gotError); + t.end(); +}); + +test('end(chunk) two times is an error', function(t) { + var w = new W(); + w._write = function() {}; + var gotError = false; + w.on('error', function(er) { + gotError = true; + t.equal(er.message, 'write after end'); + }); + w.end('this is the end'); + w.end('and so is this'); + process.nextTick(function() { + assert(gotError); + t.end(); + }); +}); + +test('dont end while writing', function(t) { + var w = new W(); + var wrote = false; + w._write = function(chunk, e, cb) { + assert(!this.writing); + wrote = true; + this.writing = true; + setTimeout(function() { + this.writing = false; + cb(); + }); + }; + w.on('finish', function() { + assert(wrote); + t.end(); + }); + w.write(Buffer(0)); + w.end(); +}); + +test('finish does not come before write cb', function(t) { + var w = new W(); + var writeCb = false; + w._write = function(chunk, e, cb) { + setTimeout(function() { + writeCb = true; + cb(); + }, 10); + }; + w.on('finish', function() { + assert(writeCb); + t.end(); + }); + w.write(Buffer(0)); + w.end(); +}); + +test('finish does not come before sync _write cb', function(t) { + var w = new W(); + var writeCb = false; + w._write = function(chunk, e, cb) { + cb(); + }; + w.on('finish', function() { + assert(writeCb); + t.end(); + }); + w.write(Buffer(0), function(er) { + writeCb = true; + }); + w.end(); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream3-pause-then-read.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream3-pause-then-read.js new file mode 100644 index 0000000..2f72c15 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/test/simple/test-stream3-pause-then-read.js @@ -0,0 +1,167 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +var stream = require('../../'); +var Readable = stream.Readable; +var Writable = stream.Writable; + +var totalChunks = 100; +var chunkSize = 99; +var expectTotalData = totalChunks * chunkSize; +var expectEndingData = expectTotalData; + +var r = new Readable({ highWaterMark: 1000 }); +var chunks = totalChunks; +r._read = function(n) { + if (!(chunks % 2)) + setImmediate(push); + else if (!(chunks % 3)) + process.nextTick(push); + else + push(); +}; + +var totalPushed = 0; +function push() { + var chunk = chunks-- > 0 ? new Buffer(chunkSize) : null; + if (chunk) { + totalPushed += chunk.length; + chunk.fill('x'); + } + r.push(chunk); +} + +read100(); + +// first we read 100 bytes +function read100() { + readn(100, onData); +} + +function readn(n, then) { + console.error('read %d', n); + expectEndingData -= n; + ;(function read() { + var c = r.read(n); + if (!c) + r.once('readable', read); + else { + assert.equal(c.length, n); + assert(!r._readableState.flowing); + then(); + } + })(); +} + +// then we listen to some data events +function onData() { + expectEndingData -= 100; + console.error('onData'); + var seen = 0; + r.on('data', function od(c) { + seen += c.length; + if (seen >= 100) { + // seen enough + r.removeListener('data', od); + r.pause(); + if (seen > 100) { + // oh no, seen too much! + // put the extra back. + var diff = seen - 100; + r.unshift(c.slice(c.length - diff)); + console.error('seen too much', seen, diff); + } + + // Nothing should be lost in between + setImmediate(pipeLittle); + } + }); +} + +// Just pipe 200 bytes, then unshift the extra and unpipe +function pipeLittle() { + expectEndingData -= 200; + console.error('pipe a little'); + var w = new Writable(); + var written = 0; + w.on('finish', function() { + assert.equal(written, 200); + setImmediate(read1234); + }); + w._write = function(chunk, encoding, cb) { + written += chunk.length; + if (written >= 200) { + r.unpipe(w); + w.end(); + cb(); + if (written > 200) { + var diff = written - 200; + written -= diff; + r.unshift(chunk.slice(chunk.length - diff)); + } + } else { + setImmediate(cb); + } + }; + r.pipe(w); +} + +// now read 1234 more bytes +function read1234() { + readn(1234, resumePause); +} + +function resumePause() { + console.error('resumePause'); + // don't read anything, just resume and re-pause a whole bunch + r.resume(); + r.pause(); + r.resume(); + r.pause(); + r.resume(); + r.pause(); + r.resume(); + r.pause(); + r.resume(); + r.pause(); + setImmediate(pipe); +} + + +function pipe() { + console.error('pipe the rest'); + var w = new Writable(); + var written = 0; + w._write = function(chunk, encoding, cb) { + written += chunk.length; + cb(); + }; + w.on('finish', function() { + console.error('written', written, totalPushed); + assert.equal(written, expectEndingData); + assert.equal(totalPushed, expectTotalData); + console.log('ok'); + }); + r.pipe(w); +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/transform.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/transform.js new file mode 100644 index 0000000..5d482f0 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/transform.js @@ -0,0 +1 @@ +module.exports = require("./lib/_stream_transform.js") diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/writable.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/writable.js new file mode 100644 index 0000000..e1e9efd --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/writable.js @@ -0,0 +1 @@ +module.exports = require("./lib/_stream_writable.js") diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/zlib.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/zlib.js new file mode 100644 index 0000000..a30ca20 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/zlib.js @@ -0,0 +1,452 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var Transform = require('./lib/_stream_transform.js'); + +var binding = process.binding('zlib'); +var util = require('util'); +var assert = require('assert').ok; + +// zlib doesn't provide these, so kludge them in following the same +// const naming scheme zlib uses. +binding.Z_MIN_WINDOWBITS = 8; +binding.Z_MAX_WINDOWBITS = 15; +binding.Z_DEFAULT_WINDOWBITS = 15; + +// fewer than 64 bytes per chunk is stupid. +// technically it could work with as few as 8, but even 64 bytes +// is absurdly low. Usually a MB or more is best. +binding.Z_MIN_CHUNK = 64; +binding.Z_MAX_CHUNK = Infinity; +binding.Z_DEFAULT_CHUNK = (16 * 1024); + +binding.Z_MIN_MEMLEVEL = 1; +binding.Z_MAX_MEMLEVEL = 9; +binding.Z_DEFAULT_MEMLEVEL = 8; + +binding.Z_MIN_LEVEL = -1; +binding.Z_MAX_LEVEL = 9; +binding.Z_DEFAULT_LEVEL = binding.Z_DEFAULT_COMPRESSION; + +// expose all the zlib constants +Object.keys(binding).forEach(function(k) { + if (k.match(/^Z/)) exports[k] = binding[k]; +}); + +// translation table for return codes. +exports.codes = { + Z_OK: binding.Z_OK, + Z_STREAM_END: binding.Z_STREAM_END, + Z_NEED_DICT: binding.Z_NEED_DICT, + Z_ERRNO: binding.Z_ERRNO, + Z_STREAM_ERROR: binding.Z_STREAM_ERROR, + Z_DATA_ERROR: binding.Z_DATA_ERROR, + Z_MEM_ERROR: binding.Z_MEM_ERROR, + Z_BUF_ERROR: binding.Z_BUF_ERROR, + Z_VERSION_ERROR: binding.Z_VERSION_ERROR +}; + +Object.keys(exports.codes).forEach(function(k) { + exports.codes[exports.codes[k]] = k; +}); + +exports.Deflate = Deflate; +exports.Inflate = Inflate; +exports.Gzip = Gzip; +exports.Gunzip = Gunzip; +exports.DeflateRaw = DeflateRaw; +exports.InflateRaw = InflateRaw; +exports.Unzip = Unzip; + +exports.createDeflate = function(o) { + return new Deflate(o); +}; + +exports.createInflate = function(o) { + return new Inflate(o); +}; + +exports.createDeflateRaw = function(o) { + return new DeflateRaw(o); +}; + +exports.createInflateRaw = function(o) { + return new InflateRaw(o); +}; + +exports.createGzip = function(o) { + return new Gzip(o); +}; + +exports.createGunzip = function(o) { + return new Gunzip(o); +}; + +exports.createUnzip = function(o) { + return new Unzip(o); +}; + + +// Convenience methods. +// compress/decompress a string or buffer in one step. +exports.deflate = function(buffer, callback) { + zlibBuffer(new Deflate(), buffer, callback); +}; + +exports.gzip = function(buffer, callback) { + zlibBuffer(new Gzip(), buffer, callback); +}; + +exports.deflateRaw = function(buffer, callback) { + zlibBuffer(new DeflateRaw(), buffer, callback); +}; + +exports.unzip = function(buffer, callback) { + zlibBuffer(new Unzip(), buffer, callback); +}; + +exports.inflate = function(buffer, callback) { + zlibBuffer(new Inflate(), buffer, callback); +}; + +exports.gunzip = function(buffer, callback) { + zlibBuffer(new Gunzip(), buffer, callback); +}; + +exports.inflateRaw = function(buffer, callback) { + zlibBuffer(new InflateRaw(), buffer, callback); +}; + +function zlibBuffer(engine, buffer, callback) { + var buffers = []; + var nread = 0; + + engine.on('error', onError); + engine.on('end', onEnd); + + engine.end(buffer); + flow(); + + function flow() { + var chunk; + while (null !== (chunk = engine.read())) { + buffers.push(chunk); + nread += chunk.length; + } + engine.once('readable', flow); + } + + function onError(err) { + engine.removeListener('end', onEnd); + engine.removeListener('readable', flow); + callback(err); + } + + function onEnd() { + var buf = Buffer.concat(buffers, nread); + buffers = []; + callback(null, buf); + } +} + + +// generic zlib +// minimal 2-byte header +function Deflate(opts) { + if (!(this instanceof Deflate)) return new Deflate(opts); + Zlib.call(this, opts, binding.DEFLATE); +} + +function Inflate(opts) { + if (!(this instanceof Inflate)) return new Inflate(opts); + Zlib.call(this, opts, binding.INFLATE); +} + + + +// gzip - bigger header, same deflate compression +function Gzip(opts) { + if (!(this instanceof Gzip)) return new Gzip(opts); + Zlib.call(this, opts, binding.GZIP); +} + +function Gunzip(opts) { + if (!(this instanceof Gunzip)) return new Gunzip(opts); + Zlib.call(this, opts, binding.GUNZIP); +} + + + +// raw - no header +function DeflateRaw(opts) { + if (!(this instanceof DeflateRaw)) return new DeflateRaw(opts); + Zlib.call(this, opts, binding.DEFLATERAW); +} + +function InflateRaw(opts) { + if (!(this instanceof InflateRaw)) return new InflateRaw(opts); + Zlib.call(this, opts, binding.INFLATERAW); +} + + +// auto-detect header. +function Unzip(opts) { + if (!(this instanceof Unzip)) return new Unzip(opts); + Zlib.call(this, opts, binding.UNZIP); +} + + +// the Zlib class they all inherit from +// This thing manages the queue of requests, and returns +// true or false if there is anything in the queue when +// you call the .write() method. + +function Zlib(opts, mode) { + this._opts = opts = opts || {}; + this._chunkSize = opts.chunkSize || exports.Z_DEFAULT_CHUNK; + + Transform.call(this, opts); + + // means a different thing there. + this._readableState.chunkSize = null; + + if (opts.chunkSize) { + if (opts.chunkSize < exports.Z_MIN_CHUNK || + opts.chunkSize > exports.Z_MAX_CHUNK) { + throw new Error('Invalid chunk size: ' + opts.chunkSize); + } + } + + if (opts.windowBits) { + if (opts.windowBits < exports.Z_MIN_WINDOWBITS || + opts.windowBits > exports.Z_MAX_WINDOWBITS) { + throw new Error('Invalid windowBits: ' + opts.windowBits); + } + } + + if (opts.level) { + if (opts.level < exports.Z_MIN_LEVEL || + opts.level > exports.Z_MAX_LEVEL) { + throw new Error('Invalid compression level: ' + opts.level); + } + } + + if (opts.memLevel) { + if (opts.memLevel < exports.Z_MIN_MEMLEVEL || + opts.memLevel > exports.Z_MAX_MEMLEVEL) { + throw new Error('Invalid memLevel: ' + opts.memLevel); + } + } + + if (opts.strategy) { + if (opts.strategy != exports.Z_FILTERED && + opts.strategy != exports.Z_HUFFMAN_ONLY && + opts.strategy != exports.Z_RLE && + opts.strategy != exports.Z_FIXED && + opts.strategy != exports.Z_DEFAULT_STRATEGY) { + throw new Error('Invalid strategy: ' + opts.strategy); + } + } + + if (opts.dictionary) { + if (!Buffer.isBuffer(opts.dictionary)) { + throw new Error('Invalid dictionary: it should be a Buffer instance'); + } + } + + this._binding = new binding.Zlib(mode); + + var self = this; + this._hadError = false; + this._binding.onerror = function(message, errno) { + // there is no way to cleanly recover. + // continuing only obscures problems. + self._binding = null; + self._hadError = true; + + var error = new Error(message); + error.errno = errno; + error.code = exports.codes[errno]; + self.emit('error', error); + }; + + this._binding.init(opts.windowBits || exports.Z_DEFAULT_WINDOWBITS, + opts.level || exports.Z_DEFAULT_COMPRESSION, + opts.memLevel || exports.Z_DEFAULT_MEMLEVEL, + opts.strategy || exports.Z_DEFAULT_STRATEGY, + opts.dictionary); + + this._buffer = new Buffer(this._chunkSize); + this._offset = 0; + this._closed = false; + + this.once('end', this.close); +} + +util.inherits(Zlib, Transform); + +Zlib.prototype.reset = function reset() { + return this._binding.reset(); +}; + +Zlib.prototype._flush = function(output, callback) { + var rs = this._readableState; + var self = this; + this._transform(null, output, function(er) { + if (er) + return callback(er); + + // now a weird thing happens... it could be that you called flush + // but everything had already actually been consumed, but it wasn't + // enough to get over the Readable class's lowWaterMark. + // In that case, we emit 'readable' now to make sure it's consumed. + if (rs.length && + rs.length < rs.lowWaterMark && + !rs.ended && + rs.needReadable) + self.emit('readable'); + + callback(); + }); +}; + +Zlib.prototype.flush = function(callback) { + var ws = this._writableState; + var ts = this._transformState; + + if (ws.writing) { + ws.needDrain = true; + var self = this; + this.once('drain', function() { + self._flush(ts.output, callback); + }); + return; + } + + this._flush(ts.output, callback || function() {}); +}; + +Zlib.prototype.close = function(callback) { + if (callback) + process.nextTick(callback); + + if (this._closed) + return; + + this._closed = true; + + this._binding.close(); + + var self = this; + process.nextTick(function() { + self.emit('close'); + }); +}; + +Zlib.prototype._transform = function(chunk, output, cb) { + var flushFlag; + var ws = this._writableState; + var ending = ws.ending || ws.ended; + var last = ending && (!chunk || ws.length === chunk.length); + + if (chunk !== null && !Buffer.isBuffer(chunk)) + return cb(new Error('invalid input')); + + // If it's the last chunk, or a final flush, we use the Z_FINISH flush flag. + // If it's explicitly flushing at some other time, then we use + // Z_FULL_FLUSH. Otherwise, use Z_NO_FLUSH for maximum compression + // goodness. + if (last) + flushFlag = binding.Z_FINISH; + else if (chunk === null) + flushFlag = binding.Z_FULL_FLUSH; + else + flushFlag = binding.Z_NO_FLUSH; + + var availInBefore = chunk && chunk.length; + var availOutBefore = this._chunkSize - this._offset; + var inOff = 0; + + var req = this._binding.write(flushFlag, + chunk, // in + inOff, // in_off + availInBefore, // in_len + this._buffer, // out + this._offset, //out_off + availOutBefore); // out_len + + req.buffer = chunk; + req.callback = callback; + + var self = this; + function callback(availInAfter, availOutAfter, buffer) { + if (self._hadError) + return; + + var have = availOutBefore - availOutAfter; + assert(have >= 0, 'have should not go down'); + + if (have > 0) { + var out = self._buffer.slice(self._offset, self._offset + have); + self._offset += have; + // serve some output to the consumer. + output(out); + } + + // exhausted the output buffer, or used all the input create a new one. + if (availOutAfter === 0 || self._offset >= self._chunkSize) { + availOutBefore = self._chunkSize; + self._offset = 0; + self._buffer = new Buffer(self._chunkSize); + } + + if (availOutAfter === 0) { + // Not actually done. Need to reprocess. + // Also, update the availInBefore to the availInAfter value, + // so that if we have to hit it a third (fourth, etc.) time, + // it'll have the correct byte counts. + inOff += (availInBefore - availInAfter); + availInBefore = availInAfter; + + var newReq = self._binding.write(flushFlag, + chunk, + inOff, + availInBefore, + self._buffer, + self._offset, + self._chunkSize); + newReq.callback = callback; // this same function + newReq.buffer = chunk; + return; + } + + // finished with the chunk. + cb(); + } +}; + +util.inherits(Deflate, Zlib); +util.inherits(Inflate, Zlib); +util.inherits(Gzip, Zlib); +util.inherits(Gunzip, Zlib); +util.inherits(DeflateRaw, Zlib); +util.inherits(InflateRaw, Zlib); +util.inherits(Unzip, Zlib); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/stream-counter/.npmignore b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/stream-counter/.npmignore new file mode 100644 index 0000000..07e6e47 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/stream-counter/.npmignore @@ -0,0 +1 @@ +/node_modules diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/stream-counter/README.md b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/stream-counter/README.md new file mode 100644 index 0000000..8d6a192 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/stream-counter/README.md @@ -0,0 +1,14 @@ +# stream-counter + +Keep track of how many bytes have been written to a stream. + +## Usage + +```js +var StreamCounter = require('stream-counter'); +var counter = new StreamCounter(); +counter.on('progress', function() { + console.log("progress", counter.bytes); +}); +fs.createReadStream('foo.txt').pipe(counter); +``` diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/stream-counter/index.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/stream-counter/index.js new file mode 100644 index 0000000..c490c2d --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/stream-counter/index.js @@ -0,0 +1,16 @@ +module.exports = ByteCounter; + +var Writable = require('readable-stream').Writable; +var util = require('util'); + +util.inherits(ByteCounter, Writable); +function ByteCounter(options) { + Writable.call(this, options); + this.bytes = 0; +} + +ByteCounter.prototype._write = function(chunk, encoding, cb) { + this.bytes += chunk.length; + this.emit('progress'); + cb(); +}; diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/stream-counter/package.json b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/stream-counter/package.json new file mode 100644 index 0000000..75da272 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/stream-counter/package.json @@ -0,0 +1,36 @@ +{ + "name": "stream-counter", + "version": "0.2.0", + "description": "keeps track of how many bytes have been written to a stream", + "main": "index.js", + "scripts": { + "test": "node test/test.js" + }, + "repository": { + "type": "git", + "url": "git://github.com/superjoe30/node-stream-counter.git" + }, + "author": { + "name": "Andrew Kelley", + "email": "superjoe30@gmail.com" + }, + "license": "BSD", + "engines": { + "node": ">=0.8.0" + }, + "dependencies": { + "readable-stream": "~1.1.8" + }, + "readme": "# stream-counter\n\nKeep track of how many bytes have been written to a stream.\n\n## Usage\n\n```js\nvar StreamCounter = require('stream-counter');\nvar counter = new StreamCounter();\ncounter.on('progress', function() {\n console.log(\"progress\", counter.bytes);\n});\nfs.createReadStream('foo.txt').pipe(counter);\n```\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/superjoe30/node-stream-counter/issues" + }, + "homepage": "https://github.com/superjoe30/node-stream-counter", + "_id": "stream-counter@0.2.0", + "dist": { + "shasum": "ded266556319c8b0e222812b9cf3b26fa7d947de" + }, + "_from": "stream-counter@~0.2.0", + "_resolved": "https://registry.npmjs.org/stream-counter/-/stream-counter-0.2.0.tgz" +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/stream-counter/test/test.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/stream-counter/test/test.js new file mode 100644 index 0000000..0da9566 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/stream-counter/test/test.js @@ -0,0 +1,20 @@ +var ByteCounter = require('../'); +var fs = require('fs'); +var path = require('path'); +var assert = require('assert'); + +var counter = new ByteCounter(); +var remainingTests = 2; +counter.once('progress', function() { + assert.strictEqual(counter.bytes, 5); + remainingTests -= 1; +}); +var is = fs.createReadStream(path.join(__dirname, 'test.txt')); +is.pipe(counter); +is.on('end', function() { + remainingTests -= 1; + assert.strictEqual(counter.bytes, 5); +}); +process.on('exit', function() { + assert.strictEqual(remainingTests, 0); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/stream-counter/test/test.txt b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/stream-counter/test/test.txt new file mode 100644 index 0000000..81c545e --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/stream-counter/test/test.txt @@ -0,0 +1 @@ +1234 diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/package.json b/server/node_modules/express/node_modules/connect/node_modules/multiparty/package.json new file mode 100644 index 0000000..dc68a4b --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/package.json @@ -0,0 +1,47 @@ +{ + "name": "multiparty", + "version": "2.2.0", + "description": "multipart/form-data parser which supports streaming", + "repository": { + "type": "git", + "url": "git@github.com:superjoe30/node-multiparty.git" + }, + "keywords": [ + "file", + "upload", + "formidable", + "stream", + "s3" + ], + "devDependencies": { + "findit": "0.1.1", + "hashish": "0.0.4", + "mocha": "~1.8.2", + "request": "~2.16.6", + "mkdirp": "~0.3.5", + "superagent": "~0.14.1" + }, + "scripts": { + "test": "ulimit -n 500 && mocha --timeout 4000 --reporter spec --recursive test/test.js" + }, + "engines": { + "node": ">=0.8.0" + }, + "license": "MIT", + "dependencies": { + "readable-stream": "~1.1.9", + "stream-counter": "~0.2.0" + }, + "readme": "[![Build Status](https://travis-ci.org/superjoe30/node-multiparty.png?branch=master)](https://travis-ci.org/superjoe30/node-multiparty)\n# multiparty\n\nParse http requests with content-type `multipart/form-data`, also known as file uploads.\n\nSee also [busboy](https://github.com/mscdex/busboy) - a\n[faster](https://github.com/mscdex/dicer/wiki/Benchmarks) alternative\nwhich may be worth looking into.\n\n### Why the fork?\n\n * This module uses the Node.js v0.10 streams properly, *even in Node.js v0.8*\n * It will not create a temp file for you unless you want it to.\n * Counts bytes and does math to help you figure out the `Content-Length` of\n each part.\n * You can easily stream uploads to s3 with\n [knox](https://github.com/LearnBoost/knox), for [example](examples/s3.js).\n * Less bugs. This code is simpler, has all deprecated functionality removed,\n has cleaner tests, and does not try to do anything beyond multipart stream\n parsing.\n\n## Installation\n\n```\nnpm install multiparty\n```\n\n## Usage\n\n * See [examples](examples).\n\nParse an incoming `multipart/form-data` request.\n\n```js\nvar multiparty = require('multiparty')\n , http = require('http')\n , util = require('util')\n\nhttp.createServer(function(req, res) {\n if (req.url === '/upload' && req.method === 'POST') {\n // parse a file upload\n var form = new multiparty.Form();\n\n form.parse(req, function(err, fields, files) {\n res.writeHead(200, {'content-type': 'text/plain'});\n res.write('received upload:\\n\\n');\n res.end(util.inspect({fields: fields, files: files}));\n });\n\n return;\n }\n\n // show a file upload form\n res.writeHead(200, {'content-type': 'text/html'});\n res.end(\n '
    '+\n '
    '+\n '
    '+\n ''+\n '
    '\n );\n}).listen(8080);\n```\n\n## API\n\n### multiparty.Form\n```js\nvar form = new multiparty.Form(options)\n```\nCreates a new form. Options:\n\n * `encoding` - sets encoding for the incoming form fields. Defaults to `utf8`.\n * `maxFieldSize` - Limits the amount of memory a field (not a file) can\n allocate in bytes. If this value is exceeded, an `error` event is emitted.\n The default size is 2MB.\n * `maxFields` - Limits the number of fields that will be parsed before\n emitting an `error` event. A file counts as a field in this case.\n Defaults to 1000.\n * `autoFields` - Enables `field` events. This is automatically set to `true`\n if you add a `field` listener.\n * `autoFiles` - Enables `file` events. This is automatically set to `true`\n if you add a `file` listener.\n * `uploadDir` - Only relevant when `autoFiles` is `true`. The directory for\n placing file uploads in. You can move them later using `fs.rename()`.\n Defaults to `os.tmpDir()`.\n * `hash` - Only relevant when `autoFiles` is `true`. If you want checksums\n calculated for incoming files, set this to either `sha1` or `md5`.\n Defaults to off.\n\n#### form.parse(request, [cb])\n\nParses an incoming node.js `request` containing form data. If `cb` is\nprovided, `autoFields` and `autoFiles` are set to `true` and all fields and\nfiles are collected and passed to the callback:\n\n```js\nform.parse(req, function(err, fieldsObject, filesObject, fieldsList, filesList) {\n // ...\n});\n```\n\nIt is often convenient to access a field or file by name. In this situation,\nuse `fieldsObject` or `filesObject`. However sometimes, as in the case of a\n`` the multipart stream will contain\nmultiple files of the same input name, and you are interested in all of them.\nIn this case, use `filesList`.\n\nAnother example is when you do not care what the field name of a file is; you\nare merely interested in a single upload. In this case, set `maxFields` to 1\n(assuming no other fields expected besides the file) and use `filesList[0]`.\n\n#### form.bytesReceived\n\nThe amount of bytes received for this form so far.\n\n#### form.bytesExpected\n\nThe expected number of bytes in this form.\n\n### Events\n\n#### 'error' (err)\n\nYou definitely want to handle this event. If not your server *will* crash when\nusers submit bogus multipart requests!\n\n#### 'part' (part)\n\nEmitted when a part is encountered in the request. `part` is a\n`ReadableStream`. It also has the following properties:\n\n * `headers` - the headers for this part. For example, you may be interested\n in `content-type`.\n * `name` - the field name for this part\n * `filename` - only if the part is an incoming file\n * `byteOffset` - the byte offset of this part in the request body\n * `byteCount` - assuming that this is the last part in the request,\n this is the size of this part in bytes. You could use this, for\n example, to set the `Content-Length` header if uploading to S3.\n If the part had a `Content-Length` header then that value is used\n here instead.\n\n#### 'aborted'\n\nEmitted when the request is aborted. This event will be followed shortly\nby an `error` event. In practice you do not need to handle this event.\n\n#### 'progress' (bytesReceived, bytesExpected)\n\n#### 'close'\n\nEmitted after all parts have been parsed and emitted. Not emitted if an `error`\nevent is emitted. This is typically when you would send your response.\n\n#### 'file' (name, file)\n\n**By default multiparty will not touch your hard drive.** But if you add this\nlistener, multiparty automatically sets `form.autoFiles` to `true` and will\nstream uploads to disk for you. \n\n * `name` - the field name for this file\n * `file` - an object with these properties:\n - `fieldName` - same as `name` - the field name for this file\n - `originalFilename` - the filename that the user reports for the file\n - `path` - the absolute path of the uploaded file on disk\n - `headers` - the HTTP headers that were sent along with this file\n - `size` - size of the file in bytes\n\nIf you set the `form.hash` option, then `file` will also contain a `hash`\nproperty which is the checksum of the file.\n\n#### 'field' (name, value)\n\n * `name` - field name\n * `value` - string field value\n\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/superjoe30/node-multiparty/issues" + }, + "homepage": "https://github.com/superjoe30/node-multiparty", + "_id": "multiparty@2.2.0", + "dist": { + "shasum": "1f821261d954aec67c80421b12af93710fb316ba" + }, + "_from": "multiparty@2.2.0", + "_resolved": "https://registry.npmjs.org/multiparty/-/multiparty-2.2.0.tgz" +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/bench-multipart-parser.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/bench-multipart-parser.js new file mode 100644 index 0000000..ee5dbad --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/bench-multipart-parser.js @@ -0,0 +1,76 @@ +var assert = require('assert') + , Form = require('../').Form + , boundary = '-----------------------------168072824752491622650073' + , mb = 100 + , buffer = createMultipartBuffer(boundary, mb * 1024 * 1024) + +var callbacks = { + partBegin: -1, + partEnd: -1, + headerField: -1, + headerValue: -1, + partData: -1, + end: -1, +}; + +var form = new Form({ boundary: boundary }); + +hijack('onParseHeaderField', function() { + callbacks.headerField++; +}); + +hijack('onParseHeaderValue', function() { + callbacks.headerValue++; +}); + +hijack('onParsePartBegin', function() { + callbacks.partBegin++; +}); + +hijack('onParsePartData', function() { + callbacks.partData++; +}); + +hijack('onParsePartEnd', function() { + callbacks.partEnd++; +}); + +form.on('finish', function() { + callbacks.end++; +}); + +var start = new Date(); +form.write(buffer, function(err) { + var duration = new Date() - start; + assert.ifError(err); + var mbPerSec = (mb / (duration / 1000)).toFixed(2); + console.log(mbPerSec+' mb/sec'); +}); + +process.on('exit', function() { + for (var k in callbacks) { + assert.equal(0, callbacks[k], k+' count off by '+callbacks[k]); + } +}); + +function createMultipartBuffer(boundary, size) { + var head = + '--'+boundary+'\r\n' + + 'content-disposition: form-data; name="field1"\r\n' + + '\r\n' + , tail = '\r\n--'+boundary+'--\r\n' + , buffer = new Buffer(size); + + buffer.write(head, 'ascii', 0); + buffer.write(tail, 'ascii', buffer.length - tail.length); + return buffer; +} + +function hijack(name, fn) { + var oldFn = form[name]; + form[name] = function() { + fn(); + return oldFn.apply(this, arguments); + }; +} + diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/file/beta-sticker-1.png b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/file/beta-sticker-1.png new file mode 100644 index 0000000..20b1a7f Binary files /dev/null and b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/file/beta-sticker-1.png differ diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/file/binaryfile.tar.gz b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/file/binaryfile.tar.gz new file mode 100644 index 0000000..4a85af7 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/file/binaryfile.tar.gz differ diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/file/blank.gif b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/file/blank.gif new file mode 100644 index 0000000..75b945d Binary files /dev/null and b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/file/blank.gif differ diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/file/funkyfilename.txt b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/file/funkyfilename.txt new file mode 100644 index 0000000..e7a4785 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/file/funkyfilename.txt @@ -0,0 +1 @@ +I am a text file with a funky name! diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/file/menu_separator.png b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/file/menu_separator.png new file mode 100644 index 0000000..1c16a71 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/file/menu_separator.png differ diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/file/pf1y5.png b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/file/pf1y5.png new file mode 100644 index 0000000..44d6017 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/file/pf1y5.png differ diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/file/plain.txt b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/file/plain.txt new file mode 100644 index 0000000..9b6903e --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/file/plain.txt @@ -0,0 +1 @@ +I am a plain text file diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/encoding/beta-sticker-1.png.http b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/encoding/beta-sticker-1.png.http new file mode 100644 index 0000000..833b83c --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/encoding/beta-sticker-1.png.http @@ -0,0 +1,12 @@ +POST /upload HTTP/1.1 +Host: localhost:8080 +Content-Type: multipart/form-data; boundary=\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ +Content-Length: 2483 + +--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ +Content-Disposition: form-data; name="sticker"; filename="beta-sticker-1.png" +Content-Type: image/png +Content-Transfer-Encoding: base64 + +iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABh5JREFUeNrMmHtIHEcYwGfv5SNwaovxEanEiJKqlYCCTRo1f0SvDeof1legEcE/YttQaNOiaQjYFFtpKaJILZU8SCRUWqlJGpoWepGLTXqUEnzFxCrnK9DEelbvvPOe/WacuY7r7HmGFjrwsbNzt7u//V7zfYvQ/2xI/9K1/NyvMP9PgCTuGmmL6/0ckD9UOGmbIExUsqMkAPHJjv5QwKRtgKioqDlh5+w/7IFeCuLlxCeA2zQ0IcCwh2qoaLH09fUdTElJ2e/1elU+n0/y+9fvPz4+fvfYsWN3YOoBcXPiocLghD4mBYHhQTCErqWlZU9FRcXJqKiowyqVSk/uSEH4o8fjWVlYWDB2d3e3d3R0WGB5jYqLg/NyGgsKxMNgkDB4451NTU3vxcXF1SlBKB0tFsuVxsbGjlu3bj2GJQeIk8K5RVBqBTMxrYRfuHAh9/jx4+ejo6MPS9I6f6hHPOC6rOLi4vyVlZXf7t27Z5c5/iZfkgMxxyUwFy9ezC0tLe3V6XRJ/MOCAYjWwsLCni0oKCh98uSJaWhoyMZFn0/uT2qBqYi/1NbWxjc0NJwPFUYExc/B53R5eXk5ZrN5YH5+3slFn5+D2uBDzG90IJETExOtzGdC9RelNf78wYMH3xQWFn4Ep0sgyyCr1NmJP6kEIa5tbW3dEx8fXxeKRoJpT76OR3p6enllZWUKTCOwNalFAglWDkTCvLq6+uR2YYKZSw4GQVKNfZQCafjkqhKYTBsTE3NY/uYi2Q4MP5KTkw9QGB3VEMv6G/YioqFLly5lazQavfytxobnUW+PWTGisIyNPEL3QYLB4PPIyMi4EydO7JUBbTIZ0RDYOFPkE8t/OdHczCK6Y/qdzP8BfUTW8Tj/uQndvT1F5vOzVvTLz1PwX4cQbt++fekURsNpSNLIw16v1z/HLsRRgecsSnovm8nxs5bvUe+NN1Bz47fkfBaAXj2aA2BWEsM/3hhFX1/5Fe3NTEAfvn8NXTO+tSH68IiNjU2Qw/AmCzg2XCQp+YyhJAu9c+pl9GJ+KmhiEt38bhjpoyJQRtYudA60k3dwD6o4mouKjmSiolcy0ArRqnXz3rT+knwFEShhNKLNlmmFP7Kf8XxuehHpj0QQmLdPGch/ioYyCSAe57pMaHnJgcprctDdwUkRjKi8CUTWhipvbm7uvlJo3zFNoHJDOznPeGEXqn+9EBUf+AQZXvqU+BEG/KCpHz2flYh+ALO9++ZX5L/Mj3gfevjw4ZRoP+PzD/b4HadPn844c+aMkb0F1DqIz9byzBvquXytvr6+7vr16+Ow9CfN2njjdfFAWpo9o2FnNmm12kQMw24gcvSnhbHb7Y+huHsNlhapLNHSxK3idlq287qhhrkKlSByOBzIZrPhGyCn04ncbjfRGAMV5ZlQxvDw8E+yYi1Q3qpleYjUQlNTU5aysrJqgNBhIAwGVSDCkFj48BVFULA1eCl7XV3dx1CKYK3YqKnY7u9Ti2royclJ76FDh1YhxefgsoFpCIOtra0RuGBQwYbRaLzc1dVlpjA2ZiqmKbWsDAmEYU9Pz8Tg4OCNoqKixNTU1BQostDq6iqBcrlcRBiYfEff1KBR+OnpabPBYOikWlnhtOOWm0zUffpnZ2ednZ2dJtCYMTs7+xkA2x0eHk6gsMYwFPYr/EC1Wo2LMEWzWa1WC1QRZ8FUVgpj42ohD3umWqHjRFxf5RkZGVkCNQ9CcTWQn5+flpSUtBOiMKAt7Fek/FSAmpmZMVdVVZ0dGxv7g4PhteMVlbBIofv0sh4Lbmhtb2+/Cbv1eFpaWmJCQsJODMO0hGGgUghAAay9v7//i5KSki9lmmG+4+Jg/MHaIH6f0dCkqaNFFc5VkViam5v319TUNEDdvRubEGsNYHGqsAwMDFxta2u7DdpdpA+3c+LgWiHfVkCiFnpDw0iLqwgqO6BVKoPo00K6WIDsOzE6OrpE395FzeLgxMn5jVe0dYTa26s5jfFg4VR0nAuwNtrFda1rgmToD6VzVWq3eTPyYAxOwwH5gvT2PiWY7X4fUgJTywp1fivyyL6E+Lb6XvQ0X9AkBeeXZED+p/k+9LcAAwAXm3hBLzoZPAAAAABJRU5ErkJggg== +--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/-- diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/encoding/binaryfile.tar.gz.http b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/encoding/binaryfile.tar.gz.http new file mode 100644 index 0000000..4f4fadb --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/encoding/binaryfile.tar.gz.http @@ -0,0 +1,12 @@ +POST /upload HTTP/1.1 +Host: localhost:8080 +Content-Type: multipart/form-data; boundary=\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ +Content-Length: 676 + +--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ +Content-Disposition: form-data; name="file"; filename="binaryfile.tar.gz" +Content-Type: application/x-gzip +Content-Transfer-Encoding: base64 + +H4sIAGiNIU8AA+3R0W6CMBQGYK59iobLZantRDG73osUOGqnFNJWM2N897UghG1ZdmWWLf93U/jP4bRAq8q92hJ/dY1J7kQEqyyLq8yXYrp2ltkqkTKXYiEykYc++ZTLVcLEvQ40dXReWcYSV1pdnL/v+6n+R11mjKVG1ZQ+s3TT2FpXqjhQ+hjzE1mnGxNLkgu+7tOKWjIVmVKTC6XL9ZaeXj4VQhwKWzL+cI4zwgQuuhkh3mhTad/Hkssh3im3027X54JnQ360R/M19OT8kC7SEN7Ooi2VvrEfznHQRWzl83gxttZKmzGehzPRW/+W8X+3fvL8sFet9sS6m3EIma02071MU3Uf9KHrmV1/+y8DAAAAAAAAAAAAAAAAAAAAAMB/9A6txIuJACgAAA== +--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/-- diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/encoding/blank.gif.http b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/encoding/blank.gif.http new file mode 100644 index 0000000..7426f5b --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/encoding/blank.gif.http @@ -0,0 +1,12 @@ +POST /upload HTTP/1.1 +Host: localhost:8080 +Content-Type: multipart/form-data; boundary=\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ +Content-Length: 323 + +--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ +Content-Disposition: form-data; name="file"; filename="blank.gif" +Content-Type: image/gif +Content-Transfer-Encoding: base64 + +R0lGODlhAQABAJH/AP///wAAAMDAwAAAACH5BAEAAAIALAAAAAABAAEAAAICVAEAOw== +--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/-- diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/encoding/menu_seperator.png.http b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/encoding/menu_seperator.png.http new file mode 100644 index 0000000..d08fd37 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/encoding/menu_seperator.png.http @@ -0,0 +1,12 @@ +POST /upload HTTP/1.1 +Host: localhost:8080 +Content-Type: multipart/form-data; boundary=\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ +Content-Length: 1509 + +--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ +Content-Disposition: form-data; name="image"; filename="menu_separator.png" +Content-Type: image/png +Content-Transfer-Encoding: base64 + +iVBORw0KGgoAAAANSUhEUgAAAAIAAAAYCAIAAABfmbuOAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MDcxODNBNzJERDcyMTFFMUFBOEVFNDQzOTA0MDJDMjQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MDcxODNBNzNERDcyMTFFMUFBOEVFNDQzOTA0MDJDMjQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowNzE4M0E3MERENzIxMUUxQUE4RUU0NDM5MDQwMkMyNCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowNzE4M0E3MURENzIxMUUxQUE4RUU0NDM5MDQwMkMyNCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pmvhbb8AAAAXSURBVHjaYnHk9PON8WJiAIPBSwEEGAAPrgG+VozFWgAAAABJRU5ErkJggg== +--\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/-- diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/encoding/pf1y5.png.http b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/encoding/pf1y5.png.http new file mode 100644 index 0000000..20c2c2d Binary files /dev/null and b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/encoding/pf1y5.png.http differ diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/encoding/plain.txt.http b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/encoding/plain.txt.http new file mode 100644 index 0000000..5e85ad6 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/encoding/plain.txt.http @@ -0,0 +1,13 @@ +POST /upload HTTP/1.1 +Host: localhost:8080 +Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ +Content-Length: 221 + +------TLV0SrKD4z1TRxRhAPUvZ +Content-Disposition: form-data; name="file"; filename="plain.txt" +Content-Type: text/plain +Content-Transfer-Encoding: 7bit + +I am a plain text file + +------TLV0SrKD4z1TRxRhAPUvZ-- diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/no-filename/filename-name.http b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/no-filename/filename-name.http new file mode 100644 index 0000000..43672a3 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/no-filename/filename-name.http @@ -0,0 +1,13 @@ +POST /upload HTTP/1.1 +Host: localhost:8080 +Content-Type: multipart/form-data; boundary=----WebKitFormBoundarytyE4wkKlZ5CQJVTG +Content-Length: 1000 + +------WebKitFormBoundarytyE4wkKlZ5CQJVTG +Content-Disposition: form-data; filename="plain.txt"; name="upload" +Content-Type: text/plain + +I am a plain text file + +------WebKitFormBoundarytyE4wkKlZ5CQJVTG-- + diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/no-filename/generic.http b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/no-filename/generic.http new file mode 100644 index 0000000..e0dee27 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/no-filename/generic.http @@ -0,0 +1,13 @@ +POST /upload HTTP/1.1 +Host: localhost:8080 +Content-Type: multipart/form-data; boundary=----WebKitFormBoundarytyE4wkKlZ5CQJVTG +Content-Length: 1000 + +------WebKitFormBoundarytyE4wkKlZ5CQJVTG +Content-Disposition: form-data; name="upload"; filename="" +Content-Type: text/plain + +I am a plain text file + +------WebKitFormBoundarytyE4wkKlZ5CQJVTG-- + diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/preamble/crlf.http b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/preamble/crlf.http new file mode 100644 index 0000000..1d5f709 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/preamble/crlf.http @@ -0,0 +1,13 @@ +POST /upload HTTP/1.1 +Host: localhost:8080 +Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ +Content-Length: 184 + + +------TLV0SrKD4z1TRxRhAPUvZ +Content-Disposition: form-data; name="upload"; filename="plain.txt" +Content-Type: text/plain + +I am a plain text file + +------TLV0SrKD4z1TRxRhAPUvZ-- diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/preamble/preamble.http b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/preamble/preamble.http new file mode 100644 index 0000000..d14d433 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/preamble/preamble.http @@ -0,0 +1,13 @@ +POST /upload HTTP/1.1 +Host: localhost:8080 +Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ +Content-Length: 226 + +This is a preamble which should be ignored +------TLV0SrKD4z1TRxRhAPUvZ +Content-Disposition: form-data; name="upload"; filename="plain.txt" +Content-Type: text/plain + +I am a plain text file + +------TLV0SrKD4z1TRxRhAPUvZ-- diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/info.md b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/info.md new file mode 100644 index 0000000..3c9dbe3 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/info.md @@ -0,0 +1,3 @@ +* Opera does not allow submitting this file, it shows a warning to the + user that the file could not be found instead. Tested in 9.8, 11.51 on OSX. + Reported to Opera on 08.09.2011 (tracking email DSK-346009@bugs.opera.com). diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/osx-chrome-13.http b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/osx-chrome-13.http new file mode 100644 index 0000000..4ef3917 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/osx-chrome-13.http @@ -0,0 +1,26 @@ +POST /upload HTTP/1.1 +Host: localhost:8080 +Connection: keep-alive +Referer: http://localhost:8080/ +Content-Length: 383 +Cache-Control: max-age=0 +Origin: http://localhost:8080 +User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.220 Safari/535.1 +Content-Type: multipart/form-data; boundary=----WebKitFormBoundarytyE4wkKlZ5CQJVTG +Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 +Accept-Encoding: gzip,deflate,sdch +Accept-Language: en-US,en;q=0.8 +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 +Cookie: jqCookieJar_tablesorter=%7B%22showListTable%22%3A%5B%5B5%2C1%5D%2C%5B1%2C0%5D%5D%7D + +------WebKitFormBoundarytyE4wkKlZ5CQJVTG +Content-Disposition: form-data; name="title" + +Weird filename +------WebKitFormBoundarytyE4wkKlZ5CQJVTG +Content-Disposition: form-data; name="upload"; filename=": \ ? % * | %22 < > . ? ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt" +Content-Type: text/plain + +I am a text file with a funky name! + +------WebKitFormBoundarytyE4wkKlZ5CQJVTG-- diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/osx-firefox-3.6.http b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/osx-firefox-3.6.http new file mode 100644 index 0000000..bf49f85 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/osx-firefox-3.6.http @@ -0,0 +1,24 @@ +POST /upload HTTP/1.1 +Host: localhost:8080 +User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.22) Gecko/20110902 Firefox/3.6.22 +Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 +Accept-Language: en-us,en;q=0.5 +Accept-Encoding: gzip,deflate +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Keep-Alive: 115 +Connection: keep-alive +Referer: http://localhost:8080/ +Content-Type: multipart/form-data; boundary=---------------------------9849436581144108930470211272 +Content-Length: 438 + +-----------------------------9849436581144108930470211272 +Content-Disposition: form-data; name="title" + +Weird filename +-----------------------------9849436581144108930470211272 +Content-Disposition: form-data; name="upload"; filename=": \ ? % * | " < > . ☃ ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt" +Content-Type: text/plain + +I am a text file with a funky name! + +-----------------------------9849436581144108930470211272-- diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/osx-safari-5.http b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/osx-safari-5.http new file mode 100644 index 0000000..ff158a4 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/osx-safari-5.http @@ -0,0 +1,23 @@ +POST /upload HTTP/1.1 +Host: localhost:8080 +Origin: http://localhost:8080 +Content-Length: 383 +User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1 +Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryQJZ1gvhvdgfisJPJ +Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 +Referer: http://localhost:8080/ +Accept-Language: en-us +Accept-Encoding: gzip, deflate +Connection: keep-alive + +------WebKitFormBoundaryQJZ1gvhvdgfisJPJ +Content-Disposition: form-data; name="title" + +Weird filename +------WebKitFormBoundaryQJZ1gvhvdgfisJPJ +Content-Disposition: form-data; name="upload"; filename=": \ ? % * | %22 < > . ? ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt" +Content-Type: text/plain + +I am a text file with a funky name! + +------WebKitFormBoundaryQJZ1gvhvdgfisJPJ-- diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/xp-chrome-12.http b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/xp-chrome-12.http new file mode 100644 index 0000000..f0fc533 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/xp-chrome-12.http @@ -0,0 +1,24 @@ +POST /upload HTTP/1.1 +Host: 192.168.56.1:8080 +Connection: keep-alive +Referer: http://192.168.56.1:8080/ +Content-Length: 344 +Cache-Control: max-age=0 +Origin: http://192.168.56.1:8080 +User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.122 Safari/534.30 +Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryEvqBNplR3ByrwQPa +Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 +Accept-Encoding: gzip,deflate,sdch +Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4 +Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 + +------WebKitFormBoundaryEvqBNplR3ByrwQPa +Content-Disposition: form-data; name="title" + +Weird filename +------WebKitFormBoundaryEvqBNplR3ByrwQPa +Content-Disposition: form-data; name="upload"; filename=" ? % * | %22 < > . ? ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt" +Content-Type: text/plain + + +------WebKitFormBoundaryEvqBNplR3ByrwQPa-- diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/xp-ie-7.http b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/xp-ie-7.http new file mode 100644 index 0000000..2e2c61c --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/xp-ie-7.http @@ -0,0 +1,22 @@ +POST /upload HTTP/1.1 +Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, */* +Referer: http://192.168.56.1:8080/ +Accept-Language: de +User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1) +Content-Type: multipart/form-data; boundary=---------------------------7db1fe232017c +Accept-Encoding: gzip, deflate +Host: 192.168.56.1:8080 +Content-Length: 368 +Connection: Keep-Alive +Cache-Control: no-cache + +-----------------------------7db1fe232017c +Content-Disposition: form-data; name="title" + +Weird filename +-----------------------------7db1fe232017c +Content-Disposition: form-data; name="upload"; filename=" ? % * | " < > . ☃ ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt" +Content-Type: application/octet-stream + + +-----------------------------7db1fe232017c-- diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/xp-ie-8.http b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/xp-ie-8.http new file mode 100644 index 0000000..e2b94fa --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/xp-ie-8.http @@ -0,0 +1,22 @@ +POST /upload HTTP/1.1 +Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, */* +Referer: http://192.168.56.1:8080/ +Accept-Language: de +User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0) +Content-Type: multipart/form-data; boundary=---------------------------7db3a8372017c +Accept-Encoding: gzip, deflate +Host: 192.168.56.1:8080 +Content-Length: 368 +Connection: Keep-Alive +Cache-Control: no-cache + +-----------------------------7db3a8372017c +Content-Disposition: form-data; name="title" + +Weird filename +-----------------------------7db3a8372017c +Content-Disposition: form-data; name="upload"; filename=" ? % * | " < > . ☃ ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt" +Content-Type: application/octet-stream + + +-----------------------------7db3a8372017c-- diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/xp-safari-5.http b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/xp-safari-5.http new file mode 100644 index 0000000..6379ac0 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/special-chars-in-filename/xp-safari-5.http @@ -0,0 +1,22 @@ +POST /upload HTTP/1.1 +Host: 192.168.56.1:8080 +Referer: http://192.168.56.1:8080/ +Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 +Accept-Language: en-US +Origin: http://192.168.56.1:8080 +User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4 +Accept-Encoding: gzip, deflate +Content-Type: multipart/form-data; boundary=----WebKitFormBoundarykmaWSUbu697WN9TM +Content-Length: 344 +Connection: keep-alive + +------WebKitFormBoundarykmaWSUbu697WN9TM +Content-Disposition: form-data; name="title" + +Weird filename +------WebKitFormBoundarykmaWSUbu697WN9TM +Content-Disposition: form-data; name="upload"; filename=" ? % * | %22 < > . ? ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt" +Content-Type: text/plain + + +------WebKitFormBoundarykmaWSUbu697WN9TM-- diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/workarounds/missing-hyphens1.http b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/workarounds/missing-hyphens1.http new file mode 100644 index 0000000..2826890 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/workarounds/missing-hyphens1.http @@ -0,0 +1,12 @@ +POST /upload HTTP/1.1 +Host: localhost:8080 +Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ +Content-Length: 178 + +------TLV0SrKD4z1TRxRhAPUvZ +Content-Disposition: form-data; name="upload"; filename="plain.txt" +Content-Type: text/plain + +I am a plain text file + +------TLV0SrKD4z1TRxRhAPUvZ diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/workarounds/missing-hyphens2.http b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/workarounds/missing-hyphens2.http new file mode 100644 index 0000000..8e18194 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/http/workarounds/missing-hyphens2.http @@ -0,0 +1,12 @@ +POST /upload HTTP/1.1 +Host: localhost:8080 +Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ +Content-Length: 180 + +------TLV0SrKD4z1TRxRhAPUvZ +Content-Disposition: form-data; name="upload"; filename="plain.txt" +Content-Type: text/plain + +I am a plain text file + +------TLV0SrKD4z1TRxRhAPUvZ diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/js/encoding.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/js/encoding.js new file mode 100644 index 0000000..1ade965 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/js/encoding.js @@ -0,0 +1,69 @@ +module.exports['menu_seperator.png.http'] = [ + { + type: 'file', + name: 'image', + filename: 'menu_separator.png', + fixture: 'menu_separator.png', + sha1: 'c845ca3ea794be298f2a1b79769b71939eaf4e54', + size: 931, + } +]; + +module.exports['beta-sticker-1.png.http'] = [ + { + type: 'file', + name: 'sticker', + filename: 'beta-sticker-1.png', + fixture: 'beta-sticker-1.png', + sha1: '6abbcffd12b4ada5a6a084fe9e4584f846331bc4', + size: 1660, + } +]; + +module.exports['blank.gif.http'] = [ + { + type: 'file', + name: 'file', + filename: 'blank.gif', + fixture: 'blank.gif', + sha1: 'a1fdee122b95748d81cee426d717c05b5174fe96', + size: 49, + } +]; + +module.exports['binaryfile.tar.gz.http'] = [ + { + type: 'file', + name: 'file', + filename: 'binaryfile.tar.gz', + fixture: 'binaryfile.tar.gz', + sha1: 'cfabe13b348e5e69287d677860880c52a69d2155', + size: 301, + } +]; + +module.exports['plain.txt.http'] = [ + { + type: 'file', + name: 'file', + filename: 'plain.txt', + fixture: 'plain.txt', + sha1: 'b31d07bac24ac32734de88b3687dddb10e976872', + size: 23, + } +]; + +module.exports['pf1y5.png.http'] = [ + { + type: 'field', + name: 'path', + }, + { + type: 'file', + name: 'upload', + filename: 'pf1y5.png', + fixture: 'pf1y5.png', + sha1: '805cc640c5b182e86f2b5c8ebf34ecf063cd34fd', + size: 768323, + } +]; diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/js/no-filename.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/js/no-filename.js new file mode 100644 index 0000000..f03b4f0 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/js/no-filename.js @@ -0,0 +1,9 @@ +module.exports['generic.http'] = [ + {type: 'file', name: 'upload', filename: '', fixture: 'plain.txt', + sha1: 'b31d07bac24ac32734de88b3687dddb10e976872'}, +]; + +module.exports['filename-name.http'] = [ + {type: 'file', name: 'upload', filename: 'plain.txt', fixture: 'plain.txt', + sha1: 'b31d07bac24ac32734de88b3687dddb10e976872'}, +]; diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/js/preamble.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/js/preamble.js new file mode 100644 index 0000000..d2e4cfd --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/js/preamble.js @@ -0,0 +1,9 @@ +module.exports['crlf.http'] = [ + {type: 'file', name: 'upload', filename: 'plain.txt', fixture: 'plain.txt', + sha1: 'b31d07bac24ac32734de88b3687dddb10e976872'}, +]; + +module.exports['preamble.http'] = [ + {type: 'file', name: 'upload', filename: 'plain.txt', fixture: 'plain.txt', + sha1: 'b31d07bac24ac32734de88b3687dddb10e976872'}, +]; diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/js/special-chars-in-filename.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/js/special-chars-in-filename.js new file mode 100644 index 0000000..aa0b79f --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/js/special-chars-in-filename.js @@ -0,0 +1,30 @@ +var properFilename = 'funkyfilename.txt'; + +function expect(filename) { + return [ + { + type: 'field', + name: 'title', + value: 'Weird filename', + }, + { + type: 'file', + name: 'upload', + filename: filename, + fixture: properFilename, + }, + ]; +} + +var webkit = " ? % * | \" < > . ? ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt"; +var ffOrIe = " ? % * | \" < > . ☃ ; ' @ # $ ^ & ( ) - _ = + { } [ ] ` ~.txt"; + +module.exports = { + 'osx-chrome-13.http' : expect(webkit), + 'osx-firefox-3.6.http' : expect(ffOrIe), + 'osx-safari-5.http' : expect(webkit), + 'xp-chrome-12.http' : expect(webkit), + 'xp-ie-7.http' : expect(ffOrIe), + 'xp-ie-8.http' : expect(ffOrIe), + 'xp-safari-5.http' : expect(webkit), +}; diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/js/workarounds.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/js/workarounds.js new file mode 100644 index 0000000..e59c5b2 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/js/workarounds.js @@ -0,0 +1,8 @@ +module.exports['missing-hyphens1.http'] = [ + {type: 'file', name: 'upload', filename: 'plain.txt', fixture: 'plain.txt', + sha1: 'b31d07bac24ac32734de88b3687dddb10e976872'}, +]; +module.exports['missing-hyphens2.http'] = [ + {type: 'file', name: 'upload', filename: 'plain.txt', fixture: 'plain.txt', + sha1: 'b31d07bac24ac32734de88b3687dddb10e976872'}, +]; diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/multi_video.upload b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/multi_video.upload new file mode 100644 index 0000000..9c82ba3 Binary files /dev/null and b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/multi_video.upload differ diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/multipart.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/multipart.js new file mode 100644 index 0000000..a476169 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/fixture/multipart.js @@ -0,0 +1,72 @@ +exports['rfc1867'] = + { boundary: 'AaB03x', + raw: + '--AaB03x\r\n'+ + 'content-disposition: form-data; name="field1"\r\n'+ + '\r\n'+ + 'Joe Blow\r\nalmost tricked you!\r\n'+ + '--AaB03x\r\n'+ + 'content-disposition: form-data; name="pics"; filename="file1.txt"\r\n'+ + 'Content-Type: text/plain\r\n'+ + '\r\n'+ + '... contents of file1.txt ...\r\r\n'+ + '--AaB03x--\r\n', + parts: + [ { headers: { + 'content-disposition': 'form-data; name="field1"', + }, + data: 'Joe Blow\r\nalmost tricked you!', + }, + { headers: { + 'content-disposition': 'form-data; name="pics"; filename="file1.txt"', + 'Content-Type': 'text/plain', + }, + data: '... contents of file1.txt ...\r', + } + ] + }; + +exports['noTrailing\r\n'] = + { boundary: 'AaB03x', + raw: + '--AaB03x\r\n'+ + 'content-disposition: form-data; name="field1"\r\n'+ + '\r\n'+ + 'Joe Blow\r\nalmost tricked you!\r\n'+ + '--AaB03x\r\n'+ + 'content-disposition: form-data; name="pics"; filename="file1.txt"\r\n'+ + 'Content-Type: text/plain\r\n'+ + '\r\n'+ + '... contents of file1.txt ...\r\r\n'+ + '--AaB03x--', + parts: + [ { headers: { + 'content-disposition': 'form-data; name="field1"', + }, + data: 'Joe Blow\r\nalmost tricked you!', + }, + { headers: { + 'content-disposition': 'form-data; name="pics"; filename="file1.txt"', + 'Content-Type': 'text/plain', + }, + data: '... contents of file1.txt ...\r', + } + ] + }; + +exports['emptyHeader'] = + { boundary: 'AaB03x', + raw: + '--AaB03x\r\n'+ + 'content-disposition: form-data; name="field1"\r\n'+ + ': foo\r\n'+ + '\r\n'+ + 'Joe Blow\r\nalmost tricked you!\r\n'+ + '--AaB03x\r\n'+ + 'content-disposition: form-data; name="pics"; filename="file1.txt"\r\n'+ + 'Content-Type: text/plain\r\n'+ + '\r\n'+ + '... contents of file1.txt ...\r\r\n'+ + '--AaB03x--\r\n', + expectError: true, + }; diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/record.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/record.js new file mode 100644 index 0000000..9f1cef8 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/record.js @@ -0,0 +1,47 @@ +var http = require('http'); +var fs = require('fs'); +var connections = 0; + +var server = http.createServer(function(req, res) { + var socket = req.socket; + console.log('Request: %s %s -> %s', req.method, req.url, socket.filename); + + req.on('end', function() { + if (req.url !== '/') { + res.end(JSON.stringify({ + method: req.method, + url: req.url, + filename: socket.filename, + })); + return; + } + + res.writeHead(200, {'content-type': 'text/html'}); + res.end( + '
    '+ + '
    '+ + '
    '+ + ''+ + '
    ' + ); + }); +}); + +server.on('connection', function(socket) { + connections++; + + socket.id = connections; + socket.filename = 'connection-' + socket.id + '.http'; + socket.file = fs.createWriteStream(socket.filename); + socket.pipe(socket.file); + + console.log('--> %s', socket.filename); + socket.on('close', function() { + console.log('<-- %s', socket.filename); + }); +}); + +var port = process.env.PORT || 8080; +server.listen(port, function() { + console.log('Recording connections on port %s', port); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-connection-aborted.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-connection-aborted.js new file mode 100644 index 0000000..bd83e1d --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-connection-aborted.js @@ -0,0 +1,27 @@ +var assert = require('assert'); +var http = require('http'); +var net = require('net'); +var multiparty = require('../../'); + +var server = http.createServer(function (req, res) { + var form = new multiparty.Form(); + var aborted_received = false; + form.on('aborted', function () { + aborted_received = true; + }); + form.on('error', function () { + assert(aborted_received, 'Error event should follow aborted'); + server.close(); + }); + form.on('end', function () { + throw new Error('Unexpected "end" event'); + }); + form.parse(req); +}).listen(0, 'localhost', function () { + var client = net.connect(server.address().port); + client.write( + "POST / HTTP/1.1\r\n" + + "Content-Length: 70\r\n" + + "Content-Type: multipart/form-data; boundary=foo\r\n\r\n"); + client.end(); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-content-transfer-encoding.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-content-transfer-encoding.js new file mode 100644 index 0000000..35e5a1f --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-content-transfer-encoding.js @@ -0,0 +1,52 @@ +var assert = require('assert') + , multiparty = require('../../') + , http = require('http') + , path = require('path') + , TMP_PATH = path.join(__dirname, '..', 'tmp') + +var server = http.createServer(function(req, res) { + var form = new multiparty.Form(); + form.uploadDir = TMP_PATH; + form.on('close', function () { + throw new Error('Unexpected "close" event'); + }); + form.on('end', function () { + throw new Error('Unexpected "end" event'); + }); + form.on('error', function (e) { + res.writeHead(500); + res.end(e.message); + }); + form.parse(req); +}); + +server.listen(0, function() { + var body = + '--foo\r\n' + + 'Content-Disposition: form-data; name="file1"; filename="file1"\r\n' + + 'Content-Type: application/octet-stream\r\n' + + '\r\nThis is the first file\r\n' + + '--foo\r\n' + + 'Content-Type: application/octet-stream\r\n' + + 'Content-Disposition: form-data; name="file2"; filename="file2"\r\n' + + 'Content-Transfer-Encoding: unknown\r\n' + + '\r\nThis is the second file\r\n' + + '--foo--\r\n'; + + var req = http.request({ + method: 'POST', + port: server.address().port, + headers: { + 'Content-Length': body.length, + 'Content-Type': 'multipart/form-data; boundary=foo' + } + }); + req.on('response', function (res) { + assert.equal(res.statusCode, 500); + res.on('data', function () {}); + res.on('end', function () { + server.close(); + }); + }); + req.end(body); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-invalid.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-invalid.js new file mode 100644 index 0000000..ede541d --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-invalid.js @@ -0,0 +1,35 @@ +var superagent = require('superagent') + , multiparty = require('../../') + , http = require('http') + +var server = http.createServer(function(req, resp) { + var form = new multiparty.Form(); + + var errCount = 0; + form.on('error', function(err) { + errCount += 1; + resp.end(); + }); + form.on('file', function(name, file) { + }); + form.on('field', function(name, file) { + }); + + form.parse(req); +}); +server.listen(function() { + var url = 'http://localhost:' + server.address().port + '/' + var req = superagent.post(url) + req.set('Content-Type', 'multipart/form-data; boundary=foo') + req.write('--foo\r\n') + req.write('Content-filename="foo.txt"\r\n') + req.write('\r\n') + req.write('some text here') + req.write('Content-Disposition: form-data; name="text"; filename="bar.txt"\r\n') + req.write('\r\n') + req.write('some more text stuff') + req.write('\r\n--foo--') + req.end(function(err, resp) { + server.close(); + }); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-issue-15.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-issue-15.js new file mode 100644 index 0000000..43982fa --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-issue-15.js @@ -0,0 +1,88 @@ +var http = require('http') + , multiparty = require('../../') + , assert = require('assert') + , superagent = require('superagent') + , path = require('path') + +var server = http.createServer(function(req, res) { + assert.strictEqual(req.url, '/upload'); + assert.strictEqual(req.method, 'POST'); + + var form = new multiparty.Form({autoFields:true,autoFiles:true}); + + form.on('error', function(err) { + console.log(err); + }); + + form.on('close', function() { + }); + + var fileCount = 0; + form.on('file', function(name, file) { + fileCount += 1; + }); + + form.parse(req, function(err, fields, files) { + var objFileCount = 0; + for (var file in files) { + objFileCount += 1; + } + // multiparty does NOT try to do intelligent things based on + // the part name. + assert.strictEqual(fileCount, 2); + assert.strictEqual(objFileCount, 1); + res.end(); + }); +}); +server.listen(function() { + var url = 'http://localhost:' + server.address().port + '/upload'; + var req = superagent.post(url); + req.attach('files[]', fixture('pf1y5.png'), 'SOG2.JPG'); + req.attach('files[]', fixture('binaryfile.tar.gz'), 'BenF364_LIB353.zip'); + + // Get the existing boundary. + var contentType = req.get('content-type'); + var split = contentType.split(' '); + + // Set the content-type. + req.set('content-type', split.join('')); + + req.end(function(err, resp) { + assert.ifError(err); + resp.on('end', function() { + server.close(); + }); + }); + + // No space. + createRequest(''); + + // Single space. + createRequest(' '); + + // Multiple spaces. + createRequest(' '); +}); + +function createRequest(separator) { + var url = 'http://localhost:' + server.address().port + '/upload'; + var req = superagent.post(url); + req.attach('files[]', fixture('pf1y5.png'), 'SOG2.JPG'); + req.attach('files[]', fixture('binaryfile.tar.gz'), 'BenF364_LIB353.zip'); + + // Get the existing boundary. + var contentType = req.get('content-type'); + var split = contentType.split(' '); + + // Set the content-type. + req.set('content-type', split.join(separator)); + + req.end(function(err, resp) { + assert.ifError(err); + // We don't close the server, to allow other requests to pass. + }); +} + +function fixture(name) { + return path.join(__dirname, '..', 'fixture', 'file', name) +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-issue-19.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-issue-19.js new file mode 100644 index 0000000..d7da0cf --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-issue-19.js @@ -0,0 +1,44 @@ +var assert = require('assert'); +var http = require('http'); +var net = require('net'); +var multiparty = require('../../'); + +var client; +var server = http.createServer(function (req, res) { + var form = new multiparty.Form({maxFields: 1}); + form.on('aborted', function () { + throw new Error("did not expect aborted"); + }); + var first = true; + form.on('error', function (err) { + assert.ok(first); + first = false; + client.end(); + assert.ok(/maxFields/.test(err.message)); + server.close(); + }); + form.on('end', function () { + throw new Error('Unexpected "end" event'); + }); + form.parse(req); +}); +server.listen(function() { + client = net.connect(server.address().port); + + client.write("POST /upload HTTP/1.1\r\n" + + "Content-Length: 728\r\n" + + "Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryvfUZhxgsZDO7FXLF\r\n" + + "\r\n" + + "------WebKitFormBoundaryvfUZhxgsZDO7FXLF\r\n" + + "Content-Disposition: form-data; name=\"title\"\r\n" + + "\r\n" + + "foofoo" + + "\r\n" + + "------WebKitFormBoundaryvfUZhxgsZDO7FXLF\r\n" + + "Content-Disposition: form-data; name=\"upload\"; filename=\"blah1.txt\"\r\n" + + "Content-Type: text/plain\r\n" + + "\r\n" + + "hi1\r\n" + + "\r\n" + + "------WebKitFormBoundaryvfUZhxgsZDO7FXLF\r\n"); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-issue-21.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-issue-21.js new file mode 100644 index 0000000..155fba0 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-issue-21.js @@ -0,0 +1,66 @@ +var assert = require('assert'); +var http = require('http'); +var net = require('net'); +var multiparty = require('../../'); + +var client; +var server = http.createServer(function(req, res) { + var form = new multiparty.Form(); + + form.parse(req, function(err, fieldsTable, filesTable, fieldsList, filesList) { + if (err) { + console.error(err.stack); + return; + } + assert.strictEqual(fieldsList.length, 1); + assert.strictEqual(fieldsList[0].name, "title"); + assert.strictEqual(fieldsList[0].value, "foofoo"); + assert.strictEqual(filesList.length, 4); + assert.strictEqual(filesList[0].fieldName, "upload"); + assert.strictEqual(filesList[1].fieldName, "upload"); + assert.strictEqual(filesList[2].fieldName, "upload"); + assert.strictEqual(filesList[3].fieldName, "upload"); + res.end(); + client.end(); + server.close(); + }); +}); +server.listen(function() { + client = net.connect(server.address().port); + + client.write("POST /upload HTTP/1.1\r\n" + + "Content-Length: 728\r\n" + + "Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryvfUZhxgsZDO7FXLF\r\n" + + "\r\n" + + "------WebKitFormBoundaryvfUZhxgsZDO7FXLF\r\n" + + "Content-Disposition: form-data; name=\"title\"\r\n" + + "\r\n" + + "foofoo" + + "\r\n" + + "------WebKitFormBoundaryvfUZhxgsZDO7FXLF\r\n" + + "Content-Disposition: form-data; name=\"upload\"; filename=\"blah1.txt\"\r\n" + + "Content-Type: text/plain\r\n" + + "\r\n" + + "hi1\r\n" + + "\r\n" + + "------WebKitFormBoundaryvfUZhxgsZDO7FXLF\r\n" + + "Content-Disposition: form-data; name=\"upload\"; filename=\"blah2.txt\"\r\n" + + "Content-Type: text/plain\r\n" + + "\r\n" + + "hi2\r\n" + + "\r\n" + + "------WebKitFormBoundaryvfUZhxgsZDO7FXLF\r\n" + + "Content-Disposition: form-data; name=\"upload\"; filename=\"blah3.txt\"\r\n" + + "Content-Type: text/plain\r\n" + + "\r\n" + + "hi3\r\n" + + "\r\n" + + "------WebKitFormBoundaryvfUZhxgsZDO7FXLF\r\n" + + "Content-Disposition: form-data; name=\"upload\"; filename=\"blah4.txt\"\r\n" + + "Content-Type: text/plain\r\n" + + "\r\n" + + "hi4\r\n" + + "\r\n" + + "------WebKitFormBoundaryvfUZhxgsZDO7FXLF--\r\n" + ); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-issue-4.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-issue-4.js new file mode 100644 index 0000000..66b2a69 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-issue-4.js @@ -0,0 +1,51 @@ +var http = require('http') + , multiparty = require('../../') + , assert = require('assert') + , superagent = require('superagent') + , path = require('path') + +var server = http.createServer(function(req, res) { + assert.strictEqual(req.url, '/upload'); + assert.strictEqual(req.method, 'POST'); + + var form = new multiparty.Form({autoFields:true,autoFiles:true}); + + form.on('error', function(err) { + console.log(err); + }); + + form.on('close', function() { + }); + + var fileCount = 0; + form.on('file', function(name, file) { + fileCount += 1; + }); + + form.parse(req, function(err, fields, files) { + var objFileCount = 0; + for (var file in files) { + objFileCount += 1; + } + // multiparty does NOT try to do intelligent things based on + // the part name. + assert.strictEqual(fileCount, 2); + assert.strictEqual(objFileCount, 1); + res.end(); + }); +}); +server.listen(function() { + var url = 'http://localhost:' + server.address().port + '/upload'; + var req = superagent.post(url); + req.attach('files[]', fixture('pf1y5.png'), 'SOG2.JPG'); + req.attach('files[]', fixture('binaryfile.tar.gz'), 'BenF364_LIB353.zip'); + req.end(function(err, resp) { + assert.ifError(err); + resp.on('end', function() { + server.close(); + }); + }); +}); +function fixture(name) { + return path.join(__dirname, '..', 'fixture', 'file', name) +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-issue-46.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-issue-46.js new file mode 100644 index 0000000..676b870 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-issue-46.js @@ -0,0 +1,49 @@ +var http = require('http'), + multiparty = require('../../'), + request = require('request'), + assert = require('assert'); + +var host = 'localhost'; + +var index = [ + '
    ', + ' ', + ' ', + '
    ' +].join("\n"); + +var server = http.createServer(function(req, res) { + + // Show a form for testing purposes. + if (req.method === 'GET') { + res.writeHead(200, {'content-type': 'text/html'}); + res.end(index); + return; + } + + // Parse form and write results to response. + var form = new multiparty.Form(); + form.parse(req, function(err, fields, files) { + res.writeHead(200, {'content-type': 'text/plain'}); + res.write(JSON.stringify({err: err, fields: fields, files: files})); + res.end(); + }); + +}).listen(0, host, function() { + + //console.log("Server up and running..."); + + var server = this, + url = 'http://' + host + ':' + server.address().port; + + var parts = [ + {'Content-Disposition': 'form-data; name="foo"', 'body': 'bar'} + ] + + var req = request({method: 'POST', url: url, multipart: parts}, function(e, res, body) { + var obj = JSON.parse(body); + assert.equal("bar", obj.fields.foo); + server.close(); + }); + +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-issue-5.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-issue-5.js new file mode 100644 index 0000000..80eadf2 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/standalone/test-issue-5.js @@ -0,0 +1,39 @@ +var assert = require('assert'); +var http = require('http'); +var net = require('net'); +var multiparty = require('../../'); + +var client; +var attachmentCount = 510; +var server = http.createServer(function(req, res) { + var form = new multiparty.Form({maxFields: 10000}); + + form.parse(req, function(err, fieldsTable, filesTable, fieldsList, filesList) { + assert.strictEqual(err.code, "EMFILE"); + res.end(); + client.end(); + server.close(); + }); +}); +server.listen(function() { + client = net.connect(server.address().port); + + var boundary = "------WebKitFormBoundaryvfUZhxgsZDO7FXLF\r\n"; + var oneAttachment = boundary + + "Content-Disposition: form-data; name=\"upload\"; filename=\"blah1.txt\"\r\n" + + "Content-Type: text/plain\r\n" + + "\r\n" + + "hi1\r\n" + + "\r\n"; + var payloadSize = oneAttachment.length * attachmentCount + boundary.length; + + client.write("POST /upload HTTP/1.1\r\n" + + "Content-Length: " + payloadSize + "\r\n" + + "Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryvfUZhxgsZDO7FXLF\r\n" + + "\r\n"); + + for (var i = 0; i < attachmentCount; i += 1) { + client.write(oneAttachment); + } + client.write(boundary); +}); diff --git a/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/test.js b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/test.js new file mode 100644 index 0000000..199d5cd --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/multiparty/test/test.js @@ -0,0 +1,117 @@ +var spawn = require('child_process').spawn + , findit = require('findit') + , path = require('path') + , hashish = require('hashish') + , fs = require('fs') + , http = require('http') + , net = require('net') + , assert = require('assert') + , multiparty = require('../') + , mkdirp = require('mkdirp') + , STANDALONE_PATH = path.join(__dirname, 'standalone') + , server = http.createServer() + , PORT = 13532 + , FIXTURE_PATH = path.join(__dirname, 'fixture') + , TMP_PATH = path.join(__dirname, 'tmp') + +mkdirp.sync(TMP_PATH); + +describe("fixtures", function() { + before(function(done) { + server.listen(PORT, done); + }); + var fixtures = []; + findit + .sync(path.join(FIXTURE_PATH, 'js')) + .forEach(function(jsPath) { + if (!/\.js$/.test(jsPath)) return; + var group = path.basename(jsPath, '.js'); + hashish.forEach(require(jsPath), function(fixture, name) { + it(group + '/' + name, createTest({ + name : group + '/' + name, + fixture : fixture, + })); + }); + }); +}); + +describe("standalone", function() { + findit + .sync(STANDALONE_PATH) + .forEach(function(jsPath) { + if (!/\.js$/.test(jsPath)) return; + it(path.basename(jsPath, '.js'), function(done) { + var child = spawn(process.execPath, [jsPath], { stdio: 'inherit' }); + child.on('error', function(err) { + done(err); + }); + child.on('exit', function(code) { + if (code) return done(new Error("exited with code " + code)); + done(); + }); + }); + }); +}); + +function createTest(fixture) { + var name = fixture.name; + fixture = fixture.fixture; + return function(done) { + uploadFixture(name, function(err, parts) { + if (err) return done(err); + fixture.forEach(function(expectedPart, i) { + var parsedPart = parts[i]; + assert.equal(parsedPart.type, expectedPart.type); + assert.equal(parsedPart.name, expectedPart.name); + + if (parsedPart.type === 'file') { + var file = parsedPart.value; + assert.equal(file.originalFilename, expectedPart.filename); + if(expectedPart.sha1) assert.strictEqual(file.hash, expectedPart.sha1); + if(expectedPart.size) assert.strictEqual(file.size, expectedPart.size); + } + }); + done(); + }); + }; + +} + +function uploadFixture(name, cb) { + server.once('request', function(req, res) { + var parts = []; + var form = new multiparty.Form({ + autoFields: true, + autoFiles: true, + }); + form.uploadDir = TMP_PATH; + form.hash = "sha1"; + + form.on('error', callback); + form.on('file', function(name, value) { + parts.push({type: 'file', name: name, value: value}); + }); + form.on('field', function(name, value) { + parts.push({type: 'field', name: name, value: value}); + }); + form.on('close', function() { + res.end('OK'); + callback(null, parts); + }); + form.parse(req); + + function callback() { + var realCallback = cb; + cb = function() {}; + realCallback.apply(null, arguments); + } + }); + + var socket = net.createConnection(PORT); + var file = fs.createReadStream(FIXTURE_PATH + '/http/' + name); + + file.pipe(socket, {end: false}); + socket.on('data', function () { + socket.end(); + }); +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/negotiator/LICENSE b/server/node_modules/express/node_modules/connect/node_modules/negotiator/LICENSE new file mode 100644 index 0000000..42ca2e7 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/negotiator/LICENSE @@ -0,0 +1,27 @@ +Original "Negotiator" program Copyright Federico Romero +Port to JavaScript Copyright Isaac Z. Schlueter + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/server/node_modules/express/node_modules/connect/node_modules/negotiator/examples/accept.js b/server/node_modules/express/node_modules/connect/node_modules/negotiator/examples/accept.js new file mode 100644 index 0000000..2a18039 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/negotiator/examples/accept.js @@ -0,0 +1,47 @@ +(function() { + var Negotiator, availableMediaTypes, http, key, representations, server, val; + + Negotiator = require('../lib/negotiator').Negotiator; + + http = require('http'); + + representations = { + 'text/html': '

    Hello world!

    ', + 'text/plain': 'Hello World!', + 'application/json': JSON.stringify({ + hello: 'world!' + }) + }; + + availableMediaTypes = (function() { + var _results; + _results = []; + for (key in representations) { + val = representations[key]; + _results.push(key); + } + return _results; + })(); + + server = http.createServer(function(req, res) { + var mediaType, negotiator; + negotiator = new Negotiator(req); + console.log("Accept: " + req.headers['accept']); + console.log("Preferred: " + (negotiator.preferredMediaTypes())); + console.log("Possible: " + (negotiator.preferredMediaTypes(availableMediaTypes))); + mediaType = negotiator.preferredMediaType(availableMediaTypes); + console.log("Selected: " + mediaType); + if (mediaType) { + res.writeHead(200, { + 'Content-Type': mediaType + }); + return res.end(representations[mediaType]); + } else { + res.writeHead(406); + return res.end(); + } + }); + + server.listen(8080); + +}).call(this); diff --git a/server/node_modules/express/node_modules/connect/node_modules/negotiator/examples/charset.js b/server/node_modules/express/node_modules/connect/node_modules/negotiator/examples/charset.js new file mode 100644 index 0000000..6455eff --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/negotiator/examples/charset.js @@ -0,0 +1,52 @@ +(function() { + var Buffer, Iconv, Negotiator, availableCharsets, http, iconv, key, message, messages, server, val; + + Negotiator = require('../lib/negotiator').Negotiator; + + http = require('http'); + + Buffer = require('buffer').Buffer; + + Iconv = require('iconv').Iconv; + + iconv = new Iconv('UTF-8', 'ISO-8859-1'); + + message = "ë"; + + messages = { + 'utf-8': message, + 'iso-8859-1': iconv.convert(new Buffer(message)) + }; + + availableCharsets = (function() { + var _results; + _results = []; + for (key in messages) { + val = messages[key]; + _results.push(key); + } + return _results; + })(); + + server = http.createServer(function(req, res) { + var charset, negotiator; + negotiator = new Negotiator(req); + console.log("Accept-Charset: " + req.headers['accept-charset']); + console.log("Preferred: " + (negotiator.preferredCharsets())); + console.log("Possible: " + (negotiator.preferredCharsets(availableCharsets))); + charset = negotiator.preferredCharset(availableCharsets); + console.log("Selected: " + charset); + if (charset) { + res.writeHead(200, { + 'Content-Type': "text/html; charset=" + charset + }); + return res.end(messages[charset]); + } else { + res.writeHead(406); + return res.end(); + } + }); + + server.listen(8080); + +}).call(this); diff --git a/server/node_modules/express/node_modules/connect/node_modules/negotiator/examples/encoding.js b/server/node_modules/express/node_modules/connect/node_modules/negotiator/examples/encoding.js new file mode 100644 index 0000000..a02d0f4 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/negotiator/examples/encoding.js @@ -0,0 +1,48 @@ +(function() { + var Negotiator, gbuf, http, messages; + + Negotiator = require('../lib/negotiator').Negotiator; + + http = require('http'); + + gbuf = require('gzip-buffer'); + + messages = { + identity: 'Hello World' + }; + + gbuf.gzip(messages.identity, function(zipped) { + var availableEncodings, key, server, val; + messages.gzip = zipped; + availableEncodings = (function() { + var _results; + _results = []; + for (key in messages) { + val = messages[key]; + _results.push(key); + } + return _results; + })(); + console.log(availableEncodings); + server = http.createServer(function(req, res) { + var encoding, negotiator; + negotiator = new Negotiator(req); + console.log("Accept-Encoding: " + req.headers['accept-encoding']); + console.log("Preferred: " + (negotiator.preferredEncodings())); + console.log("Possible: " + (negotiator.preferredEncodings(availableEncodings))); + encoding = negotiator.preferredEncoding(availableEncodings); + console.log("Selected: " + encoding); + if (encoding) { + res.writeHead(200, { + 'Content-Encoding': encoding + }); + return res.end(messages[encoding]); + } else { + res.writeHead(406); + return res.end(); + } + }); + return server.listen(8080); + }); + +}).call(this); diff --git a/server/node_modules/express/node_modules/connect/node_modules/negotiator/examples/language.js b/server/node_modules/express/node_modules/connect/node_modules/negotiator/examples/language.js new file mode 100644 index 0000000..f161743 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/negotiator/examples/language.js @@ -0,0 +1,44 @@ +(function() { + var Negotiator, availableLanguages, http, key, messages, server, val; + + Negotiator = require('../lib/negotiator').Negotiator; + + http = require('http'); + + messages = { + es: "¡Hola Mundo!", + en: "Hello World!" + }; + + availableLanguages = (function() { + var _results; + _results = []; + for (key in messages) { + val = messages[key]; + _results.push(key); + } + return _results; + })(); + + server = http.createServer(function(req, res) { + var language, negotiator; + negotiator = new Negotiator(req); + console.log("Accept-Language: " + req.headers['accept-language']); + console.log("Preferred: " + (negotiator.preferredLanguages())); + console.log("Possible: " + (negotiator.preferredLanguages(availableLanguages))); + language = negotiator.preferredLanguage(availableLanguages); + console.log("Selected: " + language); + if (language) { + res.writeHead(200, { + 'Content-Language': language + }); + return res.end(messages[language]); + } else { + res.writeHead(406); + return res.end(); + } + }); + + server.listen(8080); + +}).call(this); diff --git a/server/node_modules/express/node_modules/connect/node_modules/negotiator/lib/charset.js b/server/node_modules/express/node_modules/connect/node_modules/negotiator/lib/charset.js new file mode 100644 index 0000000..3300457 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/negotiator/lib/charset.js @@ -0,0 +1,71 @@ +module.exports = preferredCharsets; +preferredCharsets.preferredCharsets = preferredCharsets; + +function parseAcceptCharset(accept) { + return accept.split(',').map(function(e) { + return parseCharset(e.trim()); + }).filter(function(e) { + return e && e.q > 0; + }); +} + +function parseCharset(s) { + var match = s.match(/^\s*(\S+?)\s*(?:;(.*))?$/); + if (!match) return null; + + var charset = match[1]; + var q = 1; + if (match[2]) { + var params = match[2].split(';') + for (var i = 0; i < params.length; i ++) { + var p = params[i].trim().split('='); + if (p[0] === 'q') { + q = parseFloat(p[1]); + break; + } + } + } + + return { + charset: charset, + q: q + }; +} + +function getCharsetPriority(charset, accepted) { + return (accepted.filter(function(a) { + return specify(charset, a); + }).sort(function (a, b) { + // revsort + return a.q === b.q ? 0 : a.q > b.q ? -1 : 1; + })[0] || {q:0}).q; +} + +function specify(charset, spec) { + if (spec.charset === '*' || spec.charset === charset) { + return spec; + } +}; + +function preferredCharsets(accept, provided) { + accept = parseAcceptCharset(accept || ''); + if (provided) { + return provided.map(function(type) { + return [type, getCharsetPriority(type, accept)]; + }).filter(function(pair) { + return pair[1] > 0; + }).sort(function(a, b) { + // revsort + return a[1] === b[1] ? 0 : a[1] > b[1] ? -1 : 1; + }).map(function(pair) { + return pair[0]; + }); + } else { + return accept.sort(function (a, b) { + // revsort + return a.q < b.q ? 1 : -1; + }).map(function(type) { + return type.charset; + }); + } +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/negotiator/lib/encoding.js b/server/node_modules/express/node_modules/connect/node_modules/negotiator/lib/encoding.js new file mode 100644 index 0000000..b4fc889 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/negotiator/lib/encoding.js @@ -0,0 +1,89 @@ +module.exports = preferredEncodings; +preferredEncodings.preferredEncodings = preferredEncodings; + +function parseAcceptEncoding(accept) { + var acceptableEncodings; + + if (accept) { + acceptableEncodings = accept.split(',').map(function(e) { + return parseEncoding(e.trim()); + }); + } else { + acceptableEncodings = []; + } + + if (!acceptableEncodings.some(function(e) { + return e && e.encoding === 'identity'; + })) { + acceptableEncodings.push({ + encoding: 'identity', + q: 0.1 + }); + } + + return acceptableEncodings.filter(function(e) { + return e && e.q > 0; + }); +} + +function parseEncoding(s) { + var match = s.match(/^\s*(\S+?)\s*(?:;(.*))?$/); + + if (!match) return null; + + var encoding = match[1]; + var q = 1; + if (match[2]) { + var params = match[2].split(';'); + for (var i = 0; i < params.length; i ++) { + var p = params[i].trim().split('='); + if (p[0] === 'q') { + q = parseFloat(p[1]); + break; + } + } + } + + return { + encoding: encoding, + q: q + }; +} + +function getEncodingPriority(encoding, accepted) { + return (accepted.filter(function(a) { + return specify(encoding, a); + }).sort(function (a, b) { + // revsort + return a.q === b.q ? 0 : a.q > b.q ? -1 : 1; + })[0] || {q:0}).q; +} + +function specify(encoding, spec) { + if (spec.encoding === '*' || spec.encoding === encoding) { + return spec; + } +} + +function preferredEncodings(accept, provided) { + accept = parseAcceptEncoding(accept || ''); + if (provided) { + return provided.map(function(type) { + return [type, getEncodingPriority(type, accept)]; + }).filter(function(pair) { + return pair[1] > 0; + }).sort(function(a, b) { + // revsort + return a[1] === b[1] ? 0 : a[1] > b[1] ? -1 : 1; + }).map(function(pair) { + return pair[0]; + }); + } else { + return accept.sort(function (a, b) { + // revsort + return a.q < b.q ? 1 : -1; + }).map(function(type) { + return type.encoding; + }); + } +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/negotiator/lib/language.js b/server/node_modules/express/node_modules/connect/node_modules/negotiator/lib/language.js new file mode 100644 index 0000000..432b702 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/negotiator/lib/language.js @@ -0,0 +1,92 @@ +module.exports = preferredLanguages; +preferredLanguages.preferredLanguages = preferredLanguages; + +function parseAcceptLanguage(accept) { + return accept.split(',').map(function(e) { + return parseLanguage(e.trim()); + }).filter(function(e) { + return e && e.q > 0; + }); +} + +function parseLanguage(s) { + var match = s.match(/^\s*(\S+?)(?:-(\S+?))?\s*(?:;(.*))?$/); + if (!match) return null; + + var prefix = match[1], + suffix = match[2], + full = prefix; + + if (suffix) full += "-" + suffix; + + var q = 1; + if (match[3]) { + var params = match[3].split(';') + for (var i = 0; i < params.length; i ++) { + var p = params[i].split('='); + if (p[0] === 'q') q = parseFloat(p[1]); + } + } + + return { + prefix: prefix, + suffix: suffix, + q: q, + full: full + }; +} + +function getLanguagePriority(language, accepted) { + var match = getClosestMatch(language, accepted); + return match ? match.q : 0; +} + +function getClosestMatch(language, accepted) { + var parsed = parseLanguage(language); + + var matches = accepted.filter(function(a) { + return a.full === parsed.full; + }); + if (matches.length) return matches[0]; + + matches = accepted.filter(function(a) { + return a.prefix === parsed.prefix && !a.suffix; + }); + if (matches.length) return matches[0]; + + matches = accepted.filter(function(a) { + return a.prefix === parsed.prefix; + }); + if (matches.length) return matches[0]; + + matches = accepted.filter(function(a) { + return a.prefix === '*'; + }); + return matches[0]; +} + +function preferredLanguages(accept, provided) { + accept = parseAcceptLanguage(accept || ''); + if (provided) { + + var ret = provided.map(function(type) { + return [type, getLanguagePriority(type, accept)]; + }).filter(function(pair) { + return pair[1] > 0; + }).sort(function(a, b) { + // revsort + return a[1] === b[1] ? 0 : a[1] > b[1] ? -1 : 1; + }).map(function(pair) { + return pair[0]; + }); + return ret; + + } else { + return accept.sort(function (a, b) { + // revsort + return a.q < b.q ? 1 : -1; + }).map(function(type) { + return type.full; + }); + } +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/negotiator/lib/mediaType.js b/server/node_modules/express/node_modules/connect/node_modules/negotiator/lib/mediaType.js new file mode 100644 index 0000000..3dc017f --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/negotiator/lib/mediaType.js @@ -0,0 +1,101 @@ +module.exports = preferredMediaTypes; +preferredMediaTypes.preferredMediaTypes = preferredMediaTypes; + +function parseAccept(accept) { + return accept.split(',').map(function(e) { + return parseMediaType(e.trim()); + }).filter(function(e) { + return e && e.q > 0; + }); +}; + +function parseMediaType(s) { + var match = s.match(/\s*(\S+)\/([^;\s]+)\s*(?:;(.*))?/); + if (!match) return null; + + var type = match[1], + subtype = match[2], + full = "" + type + "/" + subtype, + params = {}, + q = 1; + + if (match[3]) { + params = match[3].split(';').map(function(s) { + return s.trim().split('='); + }).reduce(function (set, p) { + set[p[0]] = p[1]; + return set + }, params); + + if (params.q != null) { + q = parseFloat(params.q); + delete params.q; + } + } + + return { + type: type, + subtype: subtype, + params: params, + q: q, + full: full + }; +} + +function getMediaTypePriority(type, accepted) { + return (accepted.filter(function(a) { + return specify(type, a); + }).sort(function (a, b) { + // revsort + return a.q > b.q ? -1 : 1; + })[0] || {q:0}).q; +} + +function specifies(spec, type) { + return spec === '*' || spec === type; +} + +function specify(type, spec) { + var p = parseMediaType(type); + + if (spec.params) { + var keys = Object.keys(spec.params); + if (keys.some(function (k) { + return !specifies(spec.params[k], p.params[k]); + })) { + // some didn't specify. + return null; + } + } + + if (specifies(spec.type, p.type) && + specifies(spec.subtype, p.subtype)) { + return spec; + } +} + +function preferredMediaTypes(accept, provided) { + accept = parseAccept(accept || ''); + if (provided) { + return provided.map(function(type) { + return [type, getMediaTypePriority(type, accept)]; + }).filter(function(pair) { + return pair[1] > 0; + }).sort(function(a, b) { + // revsort + return a[1] === b[1] ? 0 : a[1] > b[1] ? -1 : 1; + }).map(function(pair) { + return pair[0]; + }); + + } else { + return accept.sort(function (a, b) { + // revsort + return a.q < b.q ? 1 : -1; + }).map(function(type) { + return type.full; + }); + } +} + + diff --git a/server/node_modules/express/node_modules/connect/node_modules/negotiator/lib/negotiator.js b/server/node_modules/express/node_modules/connect/node_modules/negotiator/lib/negotiator.js new file mode 100644 index 0000000..fe0e58a --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/negotiator/lib/negotiator.js @@ -0,0 +1,29 @@ +module.exports = Negotiator; +Negotiator.Negotiator = Negotiator; + +function Negotiator(request) { + if (!(this instanceof Negotiator)) return new Negotiator(request); + this.request = request; +} + +var set = { preferredCharset: [require('./charset.js'), 'accept-charset'], + preferredEncoding: [require('./encoding.js'), 'accept-encoding'], + preferredLanguage: [require('./language.js'), 'accept-language'], + preferredMediaType: [require('./mediaType.js'), 'accept'] }; + +Object.keys(set).forEach(function (k) { + var mh = set[k], + method = mh[0], + header = mh[1], + singular = k, + plural = k + 's'; + + Negotiator.prototype[plural] = function (available) { + return method(this.request.headers[header], available); + }; + + Negotiator.prototype[singular] = function(available) { + var set = this[plural](available); + if (set) return set[0]; + }; +}) diff --git a/server/node_modules/express/node_modules/connect/node_modules/negotiator/package.json b/server/node_modules/express/node_modules/connect/node_modules/negotiator/package.json new file mode 100644 index 0000000..1399d4f --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/negotiator/package.json @@ -0,0 +1,54 @@ +{ + "name": "negotiator", + "description": "HTTP content negotiation", + "version": "0.3.0", + "author": { + "name": "Federico Romero", + "email": "federico.romero@outboxlabs.com" + }, + "contributors": [ + { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + } + ], + "repository": { + "type": "git", + "url": "git://github.com/federomero/negotiator.git" + }, + "keywords": [ + "http", + "content negotiation", + "accept", + "accept-language", + "accept-encoding", + "accept-charset" + ], + "engine": "node >= 0.6", + "license": "MIT", + "devDependencies": { + "nodeunit": "0.6.x" + }, + "scripts": { + "test": "nodeunit test" + }, + "optionalDependencies": {}, + "engines": { + "node": "*" + }, + "main": "lib/negotiator.js", + "readme": "# Negotiator\n\nAn HTTP content negotiator for node.js written in javascript.\n\n# Accept Negotiation\n\n Negotiator = require('negotiator')\n\n availableMediaTypes = ['text/html', 'text/plain', 'application/json']\n\n // The negotiator constructor receives a request object\n negotiator = new Negotiator(request)\n\n // Let's say Accept header is 'text/html, application/*;q=0.2, image/jpeg;q=0.8'\n\n negotiator.preferredMediaTypes()\n // -> ['text/html', 'image/jpeg', 'application/*']\n\n negotiator.preferredMediaTypes(availableMediaTypes)\n // -> ['text/html', 'application/json']\n\n negotiator.preferredMediaType(availableMediaTypes)\n // -> 'text/html'\n\nYou can check a working example at `examples/accept.js`.\n\n## Methods\n\n`preferredMediaTypes(availableMediaTypes)`:\n\nReturns an array of preferred media types ordered by priority from a list of available media types.\n\n`preferredMediaType(availableMediaType)`:\n\nReturns the top preferred media type from a list of available media types.\n\n# Accept-Language Negotiation\n\n Negotiator = require('negotiator')\n\n negotiator = new Negotiator(request)\n\n availableLanguages = 'en', 'es', 'fr'\n\n // Let's say Accept-Language header is 'en;q=0.8, es, pt'\n\n negotiator.preferredLanguages()\n // -> ['es', 'pt', 'en']\n\n negotiator.preferredLanguages(availableLanguages)\n // -> ['es', 'en']\n\n language = negotiator.preferredLanguage(availableLanguages)\n // -> 'es'\n\nYou can check a working example at `examples/language.js`.\n\n## Methods\n\n`preferredLanguages(availableLanguages)`:\n\nReturns an array of preferred languages ordered by priority from a list of available languages.\n\n`preferredLanguage(availableLanguages)`:\n\nReturns the top preferred language from a list of available languages.\n\n# Accept-Charset Negotiation\n\n Negotiator = require('negotiator')\n\n availableCharsets = ['utf-8', 'iso-8859-1', 'iso-8859-5']\n\n negotiator = new Negotiator(request)\n\n // Let's say Accept-Charset header is 'utf-8, iso-8859-1;q=0.8, utf-7;q=0.2'\n\n negotiator.preferredCharsets()\n // -> ['utf-8', 'iso-8859-1', 'utf-7']\n\n negotiator.preferredCharsets(availableCharsets)\n // -> ['utf-8', 'iso-8859-1']\n\n negotiator.preferredCharset(availableCharsets)\n // -> 'utf-8'\n\nYou can check a working example at `examples/charset.js`.\n\n## Methods\n\n`preferredCharsets(availableCharsets)`:\n\nReturns an array of preferred charsets ordered by priority from a list of available charsets.\n\n`preferredCharset(availableCharsets)`:\n\nReturns the top preferred charset from a list of available charsets.\n\n# Accept-Encoding Negotiation\n\n Negotiator = require('negotiator').Negotiator\n\n availableEncodings = ['identity', 'gzip']\n\n negotiator = new Negotiator(request)\n\n // Let's say Accept-Encoding header is 'gzip, compress;q=0.2, identity;q=0.5'\n\n negotiator.preferredEncodings()\n // -> ['gzip', 'identity', 'compress']\n\n negotiator.preferredEncodings(availableEncodings)\n // -> ['gzip', 'identity']\n\n negotiator.preferredEncoding(availableEncodings)\n // -> 'gzip'\n\nYou can check a working example at `examples/encoding.js`.\n\n## Methods\n\n`preferredEncodings(availableEncodings)`:\n\nReturns an array of preferred encodings ordered by priority from a list of available encodings.\n\n`preferredEncoding(availableEncodings)`:\n\nReturns the top preferred encoding from a list of available encodings.\n\n# License\n\nMIT\n", + "readmeFilename": "readme.md", + "bugs": { + "url": "https://github.com/federomero/negotiator/issues" + }, + "homepage": "https://github.com/federomero/negotiator", + "dependencies": {}, + "_id": "negotiator@0.3.0", + "dist": { + "shasum": "34bb3f52e48d01a7ea6942d318b8eb52f8763f91" + }, + "_from": "negotiator@0.3.0", + "_resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.3.0.tgz" +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/negotiator/readme.md b/server/node_modules/express/node_modules/connect/node_modules/negotiator/readme.md new file mode 100644 index 0000000..0a077bb --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/negotiator/readme.md @@ -0,0 +1,132 @@ +# Negotiator + +An HTTP content negotiator for node.js written in javascript. + +# Accept Negotiation + + Negotiator = require('negotiator') + + availableMediaTypes = ['text/html', 'text/plain', 'application/json'] + + // The negotiator constructor receives a request object + negotiator = new Negotiator(request) + + // Let's say Accept header is 'text/html, application/*;q=0.2, image/jpeg;q=0.8' + + negotiator.preferredMediaTypes() + // -> ['text/html', 'image/jpeg', 'application/*'] + + negotiator.preferredMediaTypes(availableMediaTypes) + // -> ['text/html', 'application/json'] + + negotiator.preferredMediaType(availableMediaTypes) + // -> 'text/html' + +You can check a working example at `examples/accept.js`. + +## Methods + +`preferredMediaTypes(availableMediaTypes)`: + +Returns an array of preferred media types ordered by priority from a list of available media types. + +`preferredMediaType(availableMediaType)`: + +Returns the top preferred media type from a list of available media types. + +# Accept-Language Negotiation + + Negotiator = require('negotiator') + + negotiator = new Negotiator(request) + + availableLanguages = 'en', 'es', 'fr' + + // Let's say Accept-Language header is 'en;q=0.8, es, pt' + + negotiator.preferredLanguages() + // -> ['es', 'pt', 'en'] + + negotiator.preferredLanguages(availableLanguages) + // -> ['es', 'en'] + + language = negotiator.preferredLanguage(availableLanguages) + // -> 'es' + +You can check a working example at `examples/language.js`. + +## Methods + +`preferredLanguages(availableLanguages)`: + +Returns an array of preferred languages ordered by priority from a list of available languages. + +`preferredLanguage(availableLanguages)`: + +Returns the top preferred language from a list of available languages. + +# Accept-Charset Negotiation + + Negotiator = require('negotiator') + + availableCharsets = ['utf-8', 'iso-8859-1', 'iso-8859-5'] + + negotiator = new Negotiator(request) + + // Let's say Accept-Charset header is 'utf-8, iso-8859-1;q=0.8, utf-7;q=0.2' + + negotiator.preferredCharsets() + // -> ['utf-8', 'iso-8859-1', 'utf-7'] + + negotiator.preferredCharsets(availableCharsets) + // -> ['utf-8', 'iso-8859-1'] + + negotiator.preferredCharset(availableCharsets) + // -> 'utf-8' + +You can check a working example at `examples/charset.js`. + +## Methods + +`preferredCharsets(availableCharsets)`: + +Returns an array of preferred charsets ordered by priority from a list of available charsets. + +`preferredCharset(availableCharsets)`: + +Returns the top preferred charset from a list of available charsets. + +# Accept-Encoding Negotiation + + Negotiator = require('negotiator').Negotiator + + availableEncodings = ['identity', 'gzip'] + + negotiator = new Negotiator(request) + + // Let's say Accept-Encoding header is 'gzip, compress;q=0.2, identity;q=0.5' + + negotiator.preferredEncodings() + // -> ['gzip', 'identity', 'compress'] + + negotiator.preferredEncodings(availableEncodings) + // -> ['gzip', 'identity'] + + negotiator.preferredEncoding(availableEncodings) + // -> 'gzip' + +You can check a working example at `examples/encoding.js`. + +## Methods + +`preferredEncodings(availableEncodings)`: + +Returns an array of preferred encodings ordered by priority from a list of available encodings. + +`preferredEncoding(availableEncodings)`: + +Returns the top preferred encoding from a list of available encodings. + +# License + +MIT diff --git a/server/node_modules/express/node_modules/connect/node_modules/negotiator/test/charset.js b/server/node_modules/express/node_modules/connect/node_modules/negotiator/test/charset.js new file mode 100644 index 0000000..79224a7 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/negotiator/test/charset.js @@ -0,0 +1,62 @@ +(function() { + var configuration, preferredCharsets, testConfigurations, testCorrectCharset, _i, _len, + _this = this; + + preferredCharsets = require('../lib/charset').preferredCharsets; + + this["Should not return a charset when no charset is provided"] = function(test) { + test.deepEqual(preferredCharsets('*', []), []); + return test.done(); + }; + + this["Should not return a charset when no charset is acceptable"] = function(test) { + test.deepEqual(preferredCharsets('ISO-8859-1', ['utf-8']), []); + return test.done(); + }; + + this["Should not return a charset with q = 0"] = function(test) { + test.deepEqual(preferredCharsets('utf-8;q=0', ['utf-8']), []); + return test.done(); + }; + + testCorrectCharset = function(c) { + return _this["Should return " + c.selected + " for accept-charset header " + c.accept + " with provided charset " + c.provided] = function(test) { + test.deepEqual(preferredCharsets(c.accept, c.provided), c.selected); + return test.done(); + }; + }; + + testConfigurations = [ + { + accept: 'utf-8', + provided: ['utf-8'], + selected: ['utf-8'] + }, { + accept: '*', + provided: ['utf-8'], + selected: ['utf-8'] + }, { + accept: 'utf-8', + provided: ['utf-8', 'ISO-8859-1'], + selected: ['utf-8'] + }, { + accept: 'utf-8, ISO-8859-1', + provided: ['utf-8'], + selected: ['utf-8'] + }, { + accept: 'utf-8;q=0.8, ISO-8859-1', + provided: ['utf-8', 'ISO-8859-1'], + selected: ['ISO-8859-1', 'utf-8'] + }, { + accept: 'utf-8;q=0.8, ISO-8859-1', + provided: null, + selected: ['ISO-8859-1', 'utf-8'] + } + ]; + + for (_i = 0, _len = testConfigurations.length; _i < _len; _i++) { + configuration = testConfigurations[_i]; + testCorrectCharset(configuration); + } + +}).call(this); diff --git a/server/node_modules/express/node_modules/connect/node_modules/negotiator/test/encoding.js b/server/node_modules/express/node_modules/connect/node_modules/negotiator/test/encoding.js new file mode 100644 index 0000000..7859d5e --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/negotiator/test/encoding.js @@ -0,0 +1,70 @@ +(function() { + var configuration, preferredEncodings, testConfigurations, testCorrectEncoding, _i, _len, + _this = this; + + preferredEncodings = require('../lib/encoding').preferredEncodings; + + this["Should return identity encoding when no encoding is provided"] = function(test) { + test.deepEqual(preferredEncodings(null), ['identity']); + return test.done(); + }; + + this["Should include the identity encoding even if not explicity listed"] = function(test) { + test.ok(preferredEncodings('gzip').indexOf('identity') !== -1); + return test.done(); + }; + + this["Should not return identity encoding if q = 0"] = function(test) { + test.ok(preferredEncodings('identity;q=0').indexOf('identity') === -1); + return test.done(); + }; + + testCorrectEncoding = function(c) { + return _this["Should return " + c.selected + " for accept-encoding header " + c.accept + " with provided encoding " + c.provided] = function(test) { + test.deepEqual(preferredEncodings(c.accept, c.provided), c.selected); + return test.done(); + }; + }; + + testConfigurations = [ + { + accept: 'gzip', + provided: ['identity', 'gzip'], + selected: ['gzip', 'identity'] + }, { + accept: 'gzip, compress', + provided: ['compress'], + selected: ['compress'] + }, { + accept: 'deflate', + provided: ['gzip', 'identity'], + selected: ['identity'] + }, { + accept: '*', + provided: ['identity', 'gzip'], + selected: ['identity', 'gzip'] + }, { + accept: 'gzip, compress', + provided: ['compress', 'identity'], + selected: ['compress', 'identity'] + }, { + accept: 'gzip;q=0.8, identity;q=0.5, *;q=0.3', + provided: ['identity', 'gzip', 'compress'], + selected: ['gzip', 'identity', 'compress'] + }, { + accept: 'gzip;q=0.8, compress', + provided: ['gzip', 'compress'], + selected: ['compress', 'gzip'] + }, { + accept: 'gzip;q=0.8, compress', + provided: null, + selected: ['compress', 'gzip', 'identity'] + } + ]; + + for (_i = 0, _len = testConfigurations.length; _i < _len; _i++) { + configuration = testConfigurations[_i]; + testCorrectEncoding(configuration); + } + +}).call(this); diff --git a/server/node_modules/express/node_modules/connect/node_modules/negotiator/test/language.js b/server/node_modules/express/node_modules/connect/node_modules/negotiator/test/language.js new file mode 100644 index 0000000..d98f26d --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/negotiator/test/language.js @@ -0,0 +1,70 @@ +(function() { + var configuration, preferredLanguages, testConfigurations, testCorrectType, _i, _len, + _this = this; + + preferredLanguages = require('../lib/language').preferredLanguages; + + this["Should not return a language when no is provided"] = function(test) { + test.deepEqual(preferredLanguages('*', []), []); + return test.done(); + }; + + this["Should not return a language when no language is acceptable"] = function(test) { + test.deepEqual(preferredLanguages('en', ['es']), []); + return test.done(); + }; + + this["Should not return a language with q = 0"] = function(test) { + test.deepEqual(preferredLanguages('en;q=0', ['en']), []); + return test.done(); + }; + + testCorrectType = function(c) { + return _this["Should return " + c.selected + " for accept-language header " + c.accept + " with provided language " + c.provided] = function(test) { + test.deepEqual(preferredLanguages(c.accept, c.provided), c.selected); + return test.done(); + }; + }; + + testConfigurations = [ + { + accept: 'en', + provided: ['en'], + selected: ['en'] + }, { + accept: '*', + provided: ['en'], + selected: ['en'] + }, { + accept: 'en-US, en;q=0.8', + provided: ['en-US', 'en-GB'], + selected: ['en-US', 'en-GB'] + }, { + accept: 'en-US, en-GB', + provided: ['en-US'], + selected: ['en-US'] + }, { + accept: 'en', + provided: ['en-US'], + selected: ['en-US'] + }, { + accept: 'en;q=0.8, es', + provided: ['en', 'es'], + selected: ['es', 'en'] + }, { + accept: 'en-US;q=0.8, es', + provided: ['en', 'es'], + selected: ['es', 'en'] + }, { + accept: 'en-US;q=0.8, es', + provided: null, + selected: ['es', 'en-US'] + } + ]; + + for (_i = 0, _len = testConfigurations.length; _i < _len; _i++) { + configuration = testConfigurations[_i]; + testCorrectType(configuration); + } + +}).call(this); diff --git a/server/node_modules/express/node_modules/connect/node_modules/negotiator/test/mediaType.js b/server/node_modules/express/node_modules/connect/node_modules/negotiator/test/mediaType.js new file mode 100644 index 0000000..08e4923 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/negotiator/test/mediaType.js @@ -0,0 +1,70 @@ +(function() { + var configuration, preferredMediaTypes, testConfigurations, testCorrectType, _i, _len, + _this = this; + + preferredMediaTypes = require('../lib/mediaType').preferredMediaTypes; + + this["Should not return a media type when no media type provided"] = function(test) { + test.deepEqual(preferredMediaTypes('*/*', []), []); + return test.done(); + }; + + this["Should not return a media type when no media type is acceptable"] = function(test) { + test.deepEqual(preferredMediaTypes('application/json', ['text/html']), []); + return test.done(); + }; + + this["Should not return a media type with q = 0"] = function(test) { + test.deepEqual(preferredMediaTypes('text/html;q=0', ['text/html']), []); + return test.done(); + }; + + testCorrectType = function(c) { + return _this["Should return " + c.selected + " for access header " + c.accept + " with provided types " + c.provided] = function(test) { + test.deepEqual(preferredMediaTypes(c.accept, c.provided), c.selected); + return test.done(); + }; + }; + + testConfigurations = [ + { + accept: 'text/html', + provided: ['text/html'], + selected: ['text/html'] + }, { + accept: '*/*', + provided: ['text/html'], + selected: ['text/html'] + }, { + accept: 'text/*', + provided: ['text/html'], + selected: ['text/html'] + }, { + accept: 'application/json, text/html', + provided: ['text/html'], + selected: ['text/html'] + }, { + accept: 'text/html;q=0.1', + provided: ['text/html'], + selected: ['text/html'] + }, { + accept: 'application/json, text/html', + provided: ['application/json', 'text/html'], + selected: ['application/json', 'text/html'] + }, { + accept: 'application/json;q=0.2, text/html', + provided: ['application/json', 'text/html'], + selected: ['text/html', 'application/json'] + }, { + accept: 'application/json;q=0.2, text/html', + provided: null, + selected: ['text/html', 'application/json'] + } + ]; + + for (_i = 0, _len = testConfigurations.length; _i < _len; _i++) { + configuration = testConfigurations[_i]; + testCorrectType(configuration); + } + +}).call(this); diff --git a/server/node_modules/express/node_modules/connect/node_modules/pause/.npmignore b/server/node_modules/express/node_modules/connect/node_modules/pause/.npmignore new file mode 100644 index 0000000..f1250e5 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/pause/.npmignore @@ -0,0 +1,4 @@ +support +test +examples +*.sock diff --git a/server/node_modules/express/node_modules/connect/node_modules/pause/History.md b/server/node_modules/express/node_modules/connect/node_modules/pause/History.md new file mode 100644 index 0000000..c8aa68f --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/pause/History.md @@ -0,0 +1,5 @@ + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/server/node_modules/express/node_modules/connect/node_modules/pause/Makefile b/server/node_modules/express/node_modules/connect/node_modules/pause/Makefile new file mode 100644 index 0000000..4e9c8d3 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/pause/Makefile @@ -0,0 +1,7 @@ + +test: + @./node_modules/.bin/mocha \ + --require should \ + --reporter spec + +.PHONY: test \ No newline at end of file diff --git a/server/node_modules/express/node_modules/connect/node_modules/pause/Readme.md b/server/node_modules/express/node_modules/connect/node_modules/pause/Readme.md new file mode 100644 index 0000000..1cdd68a --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/pause/Readme.md @@ -0,0 +1,29 @@ + +# pause + + Pause streams... + +## License + +(The MIT License) + +Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/server/node_modules/express/node_modules/connect/node_modules/pause/index.js b/server/node_modules/express/node_modules/connect/node_modules/pause/index.js new file mode 100644 index 0000000..1b7b379 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/pause/index.js @@ -0,0 +1,29 @@ + +module.exports = function(obj){ + var onData + , onEnd + , events = []; + + // buffer data + obj.on('data', onData = function(data, encoding){ + events.push(['data', data, encoding]); + }); + + // buffer end + obj.on('end', onEnd = function(data, encoding){ + events.push(['end', data, encoding]); + }); + + return { + end: function(){ + obj.removeListener('data', onData); + obj.removeListener('end', onEnd); + }, + resume: function(){ + this.end(); + for (var i = 0, len = events.length; i < len; ++i) { + obj.emit.apply(obj, events[i]); + } + } + }; +}; \ No newline at end of file diff --git a/server/node_modules/express/node_modules/connect/node_modules/pause/package.json b/server/node_modules/express/node_modules/connect/node_modules/pause/package.json new file mode 100644 index 0000000..73cfe40 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/pause/package.json @@ -0,0 +1,20 @@ +{ + "name": "pause", + "version": "0.0.1", + "description": "Pause streams...", + "keywords": [], + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "dependencies": {}, + "devDependencies": { + "mocha": "*", + "should": "*" + }, + "main": "index", + "readme": "\n# pause\n\n Pause streams...\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", + "readmeFilename": "Readme.md", + "_id": "pause@0.0.1", + "_from": "pause@0.0.1" +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/qs/.gitmodules b/server/node_modules/express/node_modules/connect/node_modules/qs/.gitmodules new file mode 100644 index 0000000..49e31da --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/qs/.gitmodules @@ -0,0 +1,6 @@ +[submodule "support/expresso"] + path = support/expresso + url = git://github.com/visionmedia/expresso.git +[submodule "support/should"] + path = support/should + url = git://github.com/visionmedia/should.js.git diff --git a/server/node_modules/express/node_modules/connect/node_modules/qs/.npmignore b/server/node_modules/express/node_modules/connect/node_modules/qs/.npmignore new file mode 100644 index 0000000..e85ce2a --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/qs/.npmignore @@ -0,0 +1,7 @@ +test +.travis.yml +benchmark.js +component.json +examples.js +History.md +Makefile diff --git a/server/node_modules/express/node_modules/connect/node_modules/qs/Readme.md b/server/node_modules/express/node_modules/connect/node_modules/qs/Readme.md new file mode 100644 index 0000000..27e54a4 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/qs/Readme.md @@ -0,0 +1,58 @@ +# node-querystring + + query string parser for node and the browser supporting nesting, as it was removed from `0.3.x`, so this library provides the previous and commonly desired behaviour (and twice as fast). Used by [express](http://expressjs.com), [connect](http://senchalabs.github.com/connect) and others. + +## Installation + + $ npm install qs + +## Examples + +```js +var qs = require('qs'); + +qs.parse('user[name][first]=Tobi&user[email]=tobi@learnboost.com'); +// => { user: { name: { first: 'Tobi' }, email: 'tobi@learnboost.com' } } + +qs.stringify({ user: { name: 'Tobi', email: 'tobi@learnboost.com' }}) +// => user[name]=Tobi&user[email]=tobi%40learnboost.com +``` + +## Testing + +Install dev dependencies: + + $ npm install -d + +and execute: + + $ make test + +browser: + + $ open test/browser/index.html + +## License + +(The MIT License) + +Copyright (c) 2010 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/server/node_modules/express/node_modules/connect/node_modules/qs/index.js b/server/node_modules/express/node_modules/connect/node_modules/qs/index.js new file mode 100644 index 0000000..590491e --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/qs/index.js @@ -0,0 +1,387 @@ +/** + * Object#toString() ref for stringify(). + */ + +var toString = Object.prototype.toString; + +/** + * Object#hasOwnProperty ref + */ + +var hasOwnProperty = Object.prototype.hasOwnProperty; + +/** + * Array#indexOf shim. + */ + +var indexOf = typeof Array.prototype.indexOf === 'function' + ? function(arr, el) { return arr.indexOf(el); } + : function(arr, el) { + for (var i = 0; i < arr.length; i++) { + if (arr[i] === el) return i; + } + return -1; + }; + +/** + * Array.isArray shim. + */ + +var isArray = Array.isArray || function(arr) { + return toString.call(arr) == '[object Array]'; +}; + +/** + * Object.keys shim. + */ + +var objectKeys = Object.keys || function(obj) { + var ret = []; + for (var key in obj) ret.push(key); + return ret; +}; + +/** + * Array#forEach shim. + */ + +var forEach = typeof Array.prototype.forEach === 'function' + ? function(arr, fn) { return arr.forEach(fn); } + : function(arr, fn) { + for (var i = 0; i < arr.length; i++) fn(arr[i]); + }; + +/** + * Array#reduce shim. + */ + +var reduce = function(arr, fn, initial) { + if (typeof arr.reduce === 'function') return arr.reduce(fn, initial); + var res = initial; + for (var i = 0; i < arr.length; i++) res = fn(res, arr[i]); + return res; +}; + +/** + * Create a nullary object if possible + */ + +function createObject() { + return Object.create + ? Object.create(null) + : {}; +} + +/** + * Cache non-integer test regexp. + */ + +var isint = /^[0-9]+$/; + +function promote(parent, key) { + if (parent[key].length == 0) return parent[key] = createObject(); + var t = createObject(); + for (var i in parent[key]) { + if (hasOwnProperty.call(parent[key], i)) { + t[i] = parent[key][i]; + } + } + parent[key] = t; + return t; +} + +function parse(parts, parent, key, val) { + var part = parts.shift(); + // end + if (!part) { + if (isArray(parent[key])) { + parent[key].push(val); + } else if ('object' == typeof parent[key]) { + parent[key] = val; + } else if ('undefined' == typeof parent[key]) { + parent[key] = val; + } else { + parent[key] = [parent[key], val]; + } + // array + } else { + var obj = parent[key] = parent[key] || []; + if (']' == part) { + if (isArray(obj)) { + if ('' != val) obj.push(val); + } else if ('object' == typeof obj) { + obj[objectKeys(obj).length] = val; + } else { + obj = parent[key] = [parent[key], val]; + } + // prop + } else if (~indexOf(part, ']')) { + part = part.substr(0, part.length - 1); + if (!isint.test(part) && isArray(obj)) obj = promote(parent, key); + parse(parts, obj, part, val); + // key + } else { + if (!isint.test(part) && isArray(obj)) obj = promote(parent, key); + parse(parts, obj, part, val); + } + } +} + +/** + * Merge parent key/val pair. + */ + +function merge(parent, key, val){ + if (~indexOf(key, ']')) { + var parts = key.split('[') + , len = parts.length + , last = len - 1; + parse(parts, parent, 'base', val); + // optimize + } else { + if (!isint.test(key) && isArray(parent.base)) { + var t = createObject(); + for (var k in parent.base) t[k] = parent.base[k]; + parent.base = t; + } + set(parent.base, key, val); + } + + return parent; +} + +/** + * Compact sparse arrays. + */ + +function compact(obj) { + if ('object' != typeof obj) return obj; + + if (isArray(obj)) { + var ret = []; + + for (var i in obj) { + if (hasOwnProperty.call(obj, i)) { + ret.push(obj[i]); + } + } + + return ret; + } + + for (var key in obj) { + obj[key] = compact(obj[key]); + } + + return obj; +} + +/** + * Restore Object.prototype. + * see pull-request #58 + */ + +function restoreProto(obj) { + if (!Object.create) return obj; + if (isArray(obj)) return obj; + if (obj && 'object' != typeof obj) return obj; + + for (var key in obj) { + if (hasOwnProperty.call(obj, key)) { + obj[key] = restoreProto(obj[key]); + } + } + + obj.__proto__ = Object.prototype; + return obj; +} + +/** + * Parse the given obj. + */ + +function parseObject(obj){ + var ret = { base: {} }; + + forEach(objectKeys(obj), function(name){ + merge(ret, name, obj[name]); + }); + + return compact(ret.base); +} + +/** + * Parse the given str. + */ + +function parseString(str){ + var ret = reduce(String(str).split('&'), function(ret, pair){ + var eql = indexOf(pair, '=') + , brace = lastBraceInKey(pair) + , key = pair.substr(0, brace || eql) + , val = pair.substr(brace || eql, pair.length) + , val = val.substr(indexOf(val, '=') + 1, val.length); + + // ?foo + if ('' == key) key = pair, val = ''; + if ('' == key) return ret; + + return merge(ret, decode(key), decode(val)); + }, { base: createObject() }).base; + + return restoreProto(compact(ret)); +} + +/** + * Parse the given query `str` or `obj`, returning an object. + * + * @param {String} str | {Object} obj + * @return {Object} + * @api public + */ + +exports.parse = function(str){ + if (null == str || '' == str) return {}; + return 'object' == typeof str + ? parseObject(str) + : parseString(str); +}; + +/** + * Turn the given `obj` into a query string + * + * @param {Object} obj + * @return {String} + * @api public + */ + +var stringify = exports.stringify = function(obj, prefix) { + if (isArray(obj)) { + return stringifyArray(obj, prefix); + } else if ('[object Object]' == toString.call(obj)) { + return stringifyObject(obj, prefix); + } else if ('string' == typeof obj) { + return stringifyString(obj, prefix); + } else { + return prefix + '=' + encodeURIComponent(String(obj)); + } +}; + +/** + * Stringify the given `str`. + * + * @param {String} str + * @param {String} prefix + * @return {String} + * @api private + */ + +function stringifyString(str, prefix) { + if (!prefix) throw new TypeError('stringify expects an object'); + return prefix + '=' + encodeURIComponent(str); +} + +/** + * Stringify the given `arr`. + * + * @param {Array} arr + * @param {String} prefix + * @return {String} + * @api private + */ + +function stringifyArray(arr, prefix) { + var ret = []; + if (!prefix) throw new TypeError('stringify expects an object'); + for (var i = 0; i < arr.length; i++) { + ret.push(stringify(arr[i], prefix + '[' + i + ']')); + } + return ret.join('&'); +} + +/** + * Stringify the given `obj`. + * + * @param {Object} obj + * @param {String} prefix + * @return {String} + * @api private + */ + +function stringifyObject(obj, prefix) { + var ret = [] + , keys = objectKeys(obj) + , key; + + for (var i = 0, len = keys.length; i < len; ++i) { + key = keys[i]; + if ('' == key) continue; + if (null == obj[key]) { + ret.push(encodeURIComponent(key) + '='); + } else { + ret.push(stringify(obj[key], prefix + ? prefix + '[' + encodeURIComponent(key) + ']' + : encodeURIComponent(key))); + } + } + + return ret.join('&'); +} + +/** + * Set `obj`'s `key` to `val` respecting + * the weird and wonderful syntax of a qs, + * where "foo=bar&foo=baz" becomes an array. + * + * @param {Object} obj + * @param {String} key + * @param {String} val + * @api private + */ + +function set(obj, key, val) { + var v = obj[key]; + if (undefined === v) { + obj[key] = val; + } else if (isArray(v)) { + v.push(val); + } else { + obj[key] = [v, val]; + } +} + +/** + * Locate last brace in `str` within the key. + * + * @param {String} str + * @return {Number} + * @api private + */ + +function lastBraceInKey(str) { + var len = str.length + , brace + , c; + for (var i = 0; i < len; ++i) { + c = str[i]; + if (']' == c) brace = false; + if ('[' == c) brace = true; + if ('=' == c && !brace) return i; + } +} + +/** + * Decode `str`. + * + * @param {String} str + * @return {String} + * @api private + */ + +function decode(str) { + try { + return decodeURIComponent(str.replace(/\+/g, ' ')); + } catch (err) { + return str; + } +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/qs/package.json b/server/node_modules/express/node_modules/connect/node_modules/qs/package.json new file mode 100644 index 0000000..f542ba2 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/qs/package.json @@ -0,0 +1,42 @@ +{ + "name": "qs", + "description": "querystring parser", + "version": "0.6.5", + "keywords": [ + "query string", + "parser", + "component" + ], + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/node-querystring.git" + }, + "devDependencies": { + "mocha": "*", + "expect.js": "*" + }, + "scripts": { + "test": "make test" + }, + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca", + "url": "http://tjholowaychuk.com" + }, + "main": "index", + "engines": { + "node": "*" + }, + "readme": "# node-querystring\n\n query string parser for node and the browser supporting nesting, as it was removed from `0.3.x`, so this library provides the previous and commonly desired behaviour (and twice as fast). Used by [express](http://expressjs.com), [connect](http://senchalabs.github.com/connect) and others.\n\n## Installation\n\n $ npm install qs\n\n## Examples\n\n```js\nvar qs = require('qs');\n\nqs.parse('user[name][first]=Tobi&user[email]=tobi@learnboost.com');\n// => { user: { name: { first: 'Tobi' }, email: 'tobi@learnboost.com' } }\n\nqs.stringify({ user: { name: 'Tobi', email: 'tobi@learnboost.com' }})\n// => user[name]=Tobi&user[email]=tobi%40learnboost.com\n```\n\n## Testing\n\nInstall dev dependencies:\n\n $ npm install -d\n\nand execute:\n\n $ make test\n\nbrowser:\n\n $ open test/browser/index.html\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2010 TJ Holowaychuk <tj@vision-media.ca>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", + "readmeFilename": "Readme.md", + "bugs": { + "url": "https://github.com/visionmedia/node-querystring/issues" + }, + "homepage": "https://github.com/visionmedia/node-querystring", + "_id": "qs@0.6.5", + "dist": { + "shasum": "5172fe6969e83a8bf51d3c72aa973a0dcafefe2e" + }, + "_from": "qs@0.6.5", + "_resolved": "https://registry.npmjs.org/qs/-/qs-0.6.5.tgz" +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/raw-body/.npmignore b/server/node_modules/express/node_modules/connect/node_modules/raw-body/.npmignore new file mode 100644 index 0000000..602eb8e --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/raw-body/.npmignore @@ -0,0 +1 @@ +test.js \ No newline at end of file diff --git a/server/node_modules/express/node_modules/connect/node_modules/raw-body/.travis.yml b/server/node_modules/express/node_modules/connect/node_modules/raw-body/.travis.yml new file mode 100644 index 0000000..e79f715 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/raw-body/.travis.yml @@ -0,0 +1,4 @@ +node_js: +- "0.10" +- "0.8" +language: node_js \ No newline at end of file diff --git a/server/node_modules/express/node_modules/connect/node_modules/raw-body/Makefile b/server/node_modules/express/node_modules/connect/node_modules/raw-body/Makefile new file mode 100644 index 0000000..775fa5d --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/raw-body/Makefile @@ -0,0 +1,11 @@ +BIN = ./node_modules/.bin/ + +test: + @${BIN}mocha \ + --reporter spec \ + --bail + +clean: + @rm -rf node_modules + +.PHONY: test clean \ No newline at end of file diff --git a/server/node_modules/express/node_modules/connect/node_modules/raw-body/README.md b/server/node_modules/express/node_modules/connect/node_modules/raw-body/README.md new file mode 100644 index 0000000..aeeb94b --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/raw-body/README.md @@ -0,0 +1,74 @@ +# Raw Body [![Build Status](https://travis-ci.org/stream-utils/raw-body.png)](https://travis-ci.org/stream-utils/raw-body) + +Gets the entire buffer of a stream and validates its length against an expected length and limit. +Ideal for parsing request bodies. + +This is the callback version of [cat-stream](https://github.com/jonathanong/cat-stream), which is much more convoluted because streams suck. + +## API + +```js +var getRawBody = require('raw-body') + +app.use(function (req, res, next) { + getRawBody(req, { + expected: req.headers['content-length'], + limit: 1 * 1024 * 1024 // 1 mb + }, function (err, buffer) { + if (err) + return next(err) + + req.rawBody = buffer + next() + }) +}) +``` + +### Options + +- `expected` - The expected length of the stream. + If the contents of the stream do not add up to this length, + an `400` error code is returned. +- `limit` - The byte limit of the body. + If the body ends up being larger than this limit, + a `413` error code is returned. + +### Strings + +This library only returns the raw buffer. +If you want the string, +you can do something like this: + +```js +getRawBody(req, function (err, buffer) { + if (err) + return next(err) + + req.text = buffer.toString('utf8') + next() +}) +``` + +## License + +The MIT License (MIT) + +Copyright (c) 2013 Jonathan Ong me@jongleberry.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/server/node_modules/express/node_modules/connect/node_modules/raw-body/index.js b/server/node_modules/express/node_modules/connect/node_modules/raw-body/index.js new file mode 100644 index 0000000..6616b0d --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/raw-body/index.js @@ -0,0 +1,72 @@ +module.exports = function (stream, options, callback) { + if (typeof options === 'function') { + callback = options + options = {} + } + + var limit = typeof options.limit === 'number' + ? options.limit + : null + + var expected = !isNaN(options.expected) + ? parseInt(options.expected, 10) + : null + + if (limit !== null && expected !== null && expected > limit) { + var err = new Error('request entity too large') + err.status = 413 + err.expected = expected + err.limit = limit + callback(err) + stream.resume() // dump stream + cleanup() + return + } + + var received = 0 + var buffers = [] + + stream.on('data', onData) + stream.once('end', onEnd) + stream.once('error', callback) + stream.once('error', cleanup) + stream.once('close', cleanup) + + function onData(chunk) { + buffers.push(chunk) + received += chunk.length + + if (limit !== null && received > limit) { + var err = new Error('request entity too large') + err.status = 413 + err.received = received + err.limit = limit + callback(err) + cleanup() + } + } + + function onEnd() { + if (expected !== null && received !== expected) { + var err = new Error('request size did not match content length') + err.status = 400 + err.received = received + err.expected = expected + callback(err) + } else { + callback(null, Buffer.concat(buffers)) + } + + cleanup() + } + + function cleanup() { + received = buffers = null + + stream.removeListener('data', onData) + stream.removeListener('end', onEnd) + stream.removeListener('error', callback) + stream.removeListener('error', cleanup) + stream.removeListener('close', cleanup) + } +} \ No newline at end of file diff --git a/server/node_modules/express/node_modules/connect/node_modules/raw-body/package.json b/server/node_modules/express/node_modules/connect/node_modules/raw-body/package.json new file mode 100644 index 0000000..7d51f0a --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/raw-body/package.json @@ -0,0 +1,33 @@ +{ + "name": "raw-body", + "description": "Get and validate the raw body of a readable stream.", + "version": "0.0.3", + "author": { + "name": "Jonathan Ong", + "email": "me@jongleberry.com", + "url": "http://jongleberry.com" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/stream-utils/raw-body.git" + }, + "bugs": { + "url": "https://github.com/stream-utils/raw-body/issues" + }, + "devDependencies": { + "mocha": "~1.12" + }, + "scripts": { + "test": "make test" + }, + "readme": "# Raw Body [![Build Status](https://travis-ci.org/stream-utils/raw-body.png)](https://travis-ci.org/stream-utils/raw-body)\n\nGets the entire buffer of a stream and validates its length against an expected length and limit.\nIdeal for parsing request bodies.\n\nThis is the callback version of [cat-stream](https://github.com/jonathanong/cat-stream), which is much more convoluted because streams suck.\n\n## API\n\n```js\nvar getRawBody = require('raw-body')\n\napp.use(function (req, res, next) {\n getRawBody(req, {\n expected: req.headers['content-length'],\n limit: 1 * 1024 * 1024 // 1 mb\n }, function (err, buffer) {\n if (err)\n return next(err)\n\n req.rawBody = buffer\n next()\n })\n})\n```\n\n### Options\n\n- `expected` - The expected length of the stream.\n If the contents of the stream do not add up to this length,\n an `400` error code is returned.\n- `limit` - The byte limit of the body.\n If the body ends up being larger than this limit,\n a `413` error code is returned.\n\n### Strings\n\nThis library only returns the raw buffer.\nIf you want the string,\nyou can do something like this:\n\n```js\ngetRawBody(req, function (err, buffer) {\n if (err)\n return next(err)\n\n req.text = buffer.toString('utf8')\n next()\n})\n```\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2013 Jonathan Ong me@jongleberry.com\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.", + "readmeFilename": "README.md", + "homepage": "https://github.com/stream-utils/raw-body", + "_id": "raw-body@0.0.3", + "dist": { + "shasum": "986f3256601a15b105b0d54b826b1c1c654caba1" + }, + "_from": "raw-body@0.0.3", + "_resolved": "https://registry.npmjs.org/raw-body/-/raw-body-0.0.3.tgz" +} diff --git a/server/node_modules/express/node_modules/connect/node_modules/uid2/LICENSE b/server/node_modules/express/node_modules/connect/node_modules/uid2/LICENSE new file mode 100644 index 0000000..bdfab69 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/uid2/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 Marco Aurelio + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/server/node_modules/express/node_modules/connect/node_modules/uid2/index.js b/server/node_modules/express/node_modules/connect/node_modules/uid2/index.js new file mode 100644 index 0000000..6240b30 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/uid2/index.js @@ -0,0 +1,55 @@ +/** + * Module dependencies + */ + +var crypto = require('crypto'); + +/** + * 62 characters in the ascii range that can be used in URLs without special + * encoding. + */ +var UIDCHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + +/** + * Make a Buffer into a string ready for use in URLs + * + * @param {String} + * @returns {String} + * @api private + */ +function tostr(bytes) { + var chars, r, i; + + r = []; + for (i = 0; i < bytes.length; i++) { + r.push(UIDCHARS[bytes[i] % UIDCHARS.length]); + } + + return r.join(''); +} + +/** + * Generate an Unique Id + * + * @param {Number} length The number of chars of the uid + * @param {Number} cb (optional) Callback for async uid generation + * @api public + */ + +function uid(length, cb) { + + if (typeof cb === 'undefined') { + return tostr(crypto.pseudoRandomBytes(length)); + } else { + crypto.pseudoRandomBytes(length, function(err, bytes) { + if (err) return cb(err); + cb(null, tostr(bytes)); + }) + } +} + +/** + * Exports + */ + +module.exports = uid; diff --git a/server/node_modules/express/node_modules/connect/node_modules/uid2/package.json b/server/node_modules/express/node_modules/connect/node_modules/uid2/package.json new file mode 100644 index 0000000..76759a8 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/node_modules/uid2/package.json @@ -0,0 +1,16 @@ +{ + "name": "uid2", + "description": "strong uid", + "tags": [ + "uid" + ], + "version": "0.0.3", + "dependencies": {}, + "readme": "ERROR: No README data found!", + "_id": "uid2@0.0.3", + "dist": { + "shasum": "ebee11d6ec39c0d7efd3f24a70effa55247d6009" + }, + "_from": "uid2@0.0.3", + "_resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz" +} diff --git a/server/node_modules/express/node_modules/connect/package.json b/server/node_modules/express/node_modules/connect/package.json new file mode 100644 index 0000000..5f168f5 --- /dev/null +++ b/server/node_modules/express/node_modules/connect/package.json @@ -0,0 +1,68 @@ +{ + "name": "connect", + "version": "2.11.0", + "description": "High performance middleware framework", + "keywords": [ + "framework", + "web", + "middleware", + "connect", + "rack" + ], + "repository": { + "type": "git", + "url": "git://github.com/senchalabs/connect.git" + }, + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca", + "url": "http://tjholowaychuk.com" + }, + "dependencies": { + "qs": "0.6.5", + "cookie-signature": "1.0.1", + "buffer-crc32": "0.2.1", + "cookie": "0.1.0", + "send": "0.1.4", + "bytes": "0.2.1", + "fresh": "0.2.0", + "pause": "0.0.1", + "uid2": "0.0.3", + "debug": "*", + "methods": "0.0.1", + "raw-body": "0.0.3", + "negotiator": "0.3.0", + "multiparty": "2.2.0" + }, + "devDependencies": { + "should": ">= 2.0.0", + "mocha": "*", + "jade": "*", + "dox": "*" + }, + "licenses": [ + { + "type": "MIT", + "url": "https://raw.github.com/senchalabs/connect/master/LICENSE" + } + ], + "main": "index", + "engines": { + "node": ">= 0.8.0" + }, + "scripts": { + "test": "make" + }, + "readme": "# Connect [![build status](https://secure.travis-ci.org/senchalabs/connect.png)](http://travis-ci.org/senchalabs/connect)\n\n Connect is an extensible HTTP server framework for [node](http://nodejs.org), providing high performance \"plugins\" known as _middleware_.\n\n Connect is bundled with over _20_ commonly used middleware, including\n a logger, session support, cookie parser, and [more](http://senchalabs.github.com/connect). Be sure to view the 2.x [documentation](http://senchalabs.github.com/connect/).\n\n```js\nvar connect = require('connect')\n , http = require('http');\n\nvar app = connect()\n .use(connect.favicon())\n .use(connect.logger('dev'))\n .use(connect.static('public'))\n .use(connect.directory('public'))\n .use(connect.cookieParser())\n .use(connect.session({ secret: 'my secret here' }))\n .use(function(req, res){\n res.end('Hello from Connect!\\n');\n });\n\nhttp.createServer(app).listen(3000);\n```\n\n## Middleware\n\n - [basicAuth](http://www.senchalabs.org/connect/basicAuth.html)\n - [bodyParser](http://www.senchalabs.org/connect/bodyParser.html)\n - [compress](http://www.senchalabs.org/connect/compress.html)\n - [cookieParser](http://www.senchalabs.org/connect/cookieParser.html)\n - [cookieSession](http://www.senchalabs.org/connect/cookieSession.html)\n - [csrf](http://www.senchalabs.org/connect/csrf.html)\n - [directory](http://www.senchalabs.org/connect/directory.html)\n - [errorHandler](http://www.senchalabs.org/connect/errorHandler.html)\n - [favicon](http://www.senchalabs.org/connect/favicon.html)\n - [json](http://www.senchalabs.org/connect/json.html)\n - [limit](http://www.senchalabs.org/connect/limit.html)\n - [logger](http://www.senchalabs.org/connect/logger.html)\n - [methodOverride](http://www.senchalabs.org/connect/methodOverride.html)\n - [multipart](http://www.senchalabs.org/connect/multipart.html)\n - [urlencoded](http://www.senchalabs.org/connect/urlencoded.html)\n - [query](http://www.senchalabs.org/connect/query.html)\n - [responseTime](http://www.senchalabs.org/connect/responseTime.html)\n - [session](http://www.senchalabs.org/connect/session.html)\n - [static](http://www.senchalabs.org/connect/static.html)\n - [staticCache](http://www.senchalabs.org/connect/staticCache.html)\n - [subdomains](http://www.senchalabs.org/connect/subdomains.html)\n - [vhost](http://www.senchalabs.org/connect/vhost.html)\n\n## Running Tests\n\nfirst:\n\n $ npm install -d\n\nthen:\n\n $ make test\n\n## Contributors\n\n https://github.com/senchalabs/connect/graphs/contributors\n\n## Node Compatibility\n\n Connect `< 1.x` is compatible with node 0.2.x\n\n\n Connect `1.x` is compatible with node 0.4.x\n\n\n Connect `2.x` is compatible with node 0.6.x\n\n\n Connect (_master_) is compatible with node 0.8.x\n\n## CLA\n\n [http://sencha.com/cla](http://sencha.com/cla)\n\n## License\n\nView the [LICENSE](https://github.com/senchalabs/connect/blob/master/LICENSE) file. The [Silk](http://www.famfamfam.com/lab/icons/silk/) icons used by the `directory` middleware created by/copyright of [FAMFAMFAM](http://www.famfamfam.com/).\n", + "readmeFilename": "Readme.md", + "bugs": { + "url": "https://github.com/senchalabs/connect/issues" + }, + "homepage": "https://github.com/senchalabs/connect", + "_id": "connect@2.11.0", + "dist": { + "shasum": "9ce118c582ef6e1a0533ef8d071b9eab6ef0d656" + }, + "_from": "connect@2.11.0", + "_resolved": "https://registry.npmjs.org/connect/-/connect-2.11.0.tgz" +} diff --git a/server/node_modules/express/node_modules/cookie-signature/.npmignore b/server/node_modules/express/node_modules/cookie-signature/.npmignore new file mode 100644 index 0000000..f1250e5 --- /dev/null +++ b/server/node_modules/express/node_modules/cookie-signature/.npmignore @@ -0,0 +1,4 @@ +support +test +examples +*.sock diff --git a/server/node_modules/express/node_modules/cookie-signature/History.md b/server/node_modules/express/node_modules/cookie-signature/History.md new file mode 100644 index 0000000..9e30179 --- /dev/null +++ b/server/node_modules/express/node_modules/cookie-signature/History.md @@ -0,0 +1,11 @@ + +1.0.1 / 2013-04-15 +================== + + * Revert "Changed underlying HMAC algo. to sha512." + * Revert "Fix for timing attacks on MAC verification." + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/server/node_modules/express/node_modules/cookie-signature/Makefile b/server/node_modules/express/node_modules/cookie-signature/Makefile new file mode 100644 index 0000000..4e9c8d3 --- /dev/null +++ b/server/node_modules/express/node_modules/cookie-signature/Makefile @@ -0,0 +1,7 @@ + +test: + @./node_modules/.bin/mocha \ + --require should \ + --reporter spec + +.PHONY: test \ No newline at end of file diff --git a/server/node_modules/express/node_modules/cookie-signature/Readme.md b/server/node_modules/express/node_modules/cookie-signature/Readme.md new file mode 100644 index 0000000..2559e84 --- /dev/null +++ b/server/node_modules/express/node_modules/cookie-signature/Readme.md @@ -0,0 +1,42 @@ + +# cookie-signature + + Sign and unsign cookies. + +## Example + +```js +var cookie = require('cookie-signature'); + +var val = cookie.sign('hello', 'tobiiscool'); +val.should.equal('hello.DGDUkGlIkCzPz+C0B064FNgHdEjox7ch8tOBGslZ5QI'); + +var val = cookie.sign('hello', 'tobiiscool'); +cookie.unsign(val, 'tobiiscool').should.equal('hello'); +cookie.unsign(val, 'luna').should.be.false; +``` + +## License + +(The MIT License) + +Copyright (c) 2012 LearnBoost <tj@learnboost.com> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/server/node_modules/express/node_modules/cookie-signature/index.js b/server/node_modules/express/node_modules/cookie-signature/index.js new file mode 100644 index 0000000..ed62814 --- /dev/null +++ b/server/node_modules/express/node_modules/cookie-signature/index.js @@ -0,0 +1,42 @@ + +/** + * Module dependencies. + */ + +var crypto = require('crypto'); + +/** + * Sign the given `val` with `secret`. + * + * @param {String} val + * @param {String} secret + * @return {String} + * @api private + */ + +exports.sign = function(val, secret){ + if ('string' != typeof val) throw new TypeError('cookie required'); + if ('string' != typeof secret) throw new TypeError('secret required'); + return val + '.' + crypto + .createHmac('sha256', secret) + .update(val) + .digest('base64') + .replace(/\=+$/, ''); +}; + +/** + * Unsign and decode the given `val` with `secret`, + * returning `false` if the signature is invalid. + * + * @param {String} val + * @param {String} secret + * @return {String|Boolean} + * @api private + */ + +exports.unsign = function(val, secret){ + if ('string' != typeof val) throw new TypeError('cookie required'); + if ('string' != typeof secret) throw new TypeError('secret required'); + var str = val.slice(0, val.lastIndexOf('.')); + return exports.sign(str, secret) == val ? str : false; +}; diff --git a/server/node_modules/express/node_modules/cookie-signature/package.json b/server/node_modules/express/node_modules/cookie-signature/package.json new file mode 100644 index 0000000..06e9c09 --- /dev/null +++ b/server/node_modules/express/node_modules/cookie-signature/package.json @@ -0,0 +1,28 @@ +{ + "name": "cookie-signature", + "version": "1.0.1", + "description": "Sign and unsign cookies", + "keywords": [ + "cookie", + "sign", + "unsign" + ], + "author": { + "name": "TJ Holowaychuk", + "email": "tj@learnboost.com" + }, + "dependencies": {}, + "devDependencies": { + "mocha": "*", + "should": "*" + }, + "main": "index", + "readme": "\n# cookie-signature\n\n Sign and unsign cookies.\n\n## Example\n\n```js\nvar cookie = require('cookie-signature');\n\nvar val = cookie.sign('hello', 'tobiiscool');\nval.should.equal('hello.DGDUkGlIkCzPz+C0B064FNgHdEjox7ch8tOBGslZ5QI');\n\nvar val = cookie.sign('hello', 'tobiiscool');\ncookie.unsign(val, 'tobiiscool').should.equal('hello');\ncookie.unsign(val, 'luna').should.be.false;\n```\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2012 LearnBoost <tj@learnboost.com>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", + "readmeFilename": "Readme.md", + "_id": "cookie-signature@1.0.1", + "dist": { + "shasum": "1ca73522157ec7f00383baa2a2d0d5b6985f88ee" + }, + "_from": "cookie-signature@1.0.1", + "_resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.1.tgz" +} diff --git a/server/node_modules/express/node_modules/cookie/.npmignore b/server/node_modules/express/node_modules/cookie/.npmignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/server/node_modules/express/node_modules/cookie/.npmignore @@ -0,0 +1 @@ +node_modules diff --git a/server/node_modules/express/node_modules/cookie/.travis.yml b/server/node_modules/express/node_modules/cookie/.travis.yml new file mode 100644 index 0000000..9400c11 --- /dev/null +++ b/server/node_modules/express/node_modules/cookie/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - "0.6" + - "0.8" + - "0.10" diff --git a/server/node_modules/express/node_modules/cookie/LICENSE b/server/node_modules/express/node_modules/cookie/LICENSE new file mode 100644 index 0000000..249d9de --- /dev/null +++ b/server/node_modules/express/node_modules/cookie/LICENSE @@ -0,0 +1,9 @@ +// MIT License + +Copyright (C) Roman Shtylman + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/server/node_modules/express/node_modules/cookie/README.md b/server/node_modules/express/node_modules/cookie/README.md new file mode 100644 index 0000000..5187ed1 --- /dev/null +++ b/server/node_modules/express/node_modules/cookie/README.md @@ -0,0 +1,44 @@ +# cookie [![Build Status](https://secure.travis-ci.org/shtylman/node-cookie.png?branch=master)](http://travis-ci.org/shtylman/node-cookie) # + +cookie is a basic cookie parser and serializer. It doesn't make assumptions about how you are going to deal with your cookies. It basically just provides a way to read and write the HTTP cookie headers. + +See [RFC6265](http://tools.ietf.org/html/rfc6265) for details about the http header for cookies. + +## how? + +``` +npm install cookie +``` + +```javascript +var cookie = require('cookie'); + +var hdr = cookie.serialize('foo', 'bar'); +// hdr = 'foo=bar'; + +var cookies = cookie.parse('foo=bar; cat=meow; dog=ruff'); +// cookies = { foo: 'bar', cat: 'meow', dog: 'ruff' }; +``` + +## more + +The serialize function takes a third parameter, an object, to set cookie options. See the RFC for valid values. + +### path +> cookie path + +### expires +> absolute expiration date for the cookie (Date object) + +### maxAge +> relative max age of the cookie from when the client receives it (seconds) + +### domain +> domain for the cookie + +### secure +> true or false + +### httpOnly +> true or false + diff --git a/server/node_modules/express/node_modules/cookie/index.js b/server/node_modules/express/node_modules/cookie/index.js new file mode 100644 index 0000000..16bdb65 --- /dev/null +++ b/server/node_modules/express/node_modules/cookie/index.js @@ -0,0 +1,70 @@ + +/// Serialize the a name value pair into a cookie string suitable for +/// http headers. An optional options object specified cookie parameters +/// +/// serialize('foo', 'bar', { httpOnly: true }) +/// => "foo=bar; httpOnly" +/// +/// @param {String} name +/// @param {String} val +/// @param {Object} options +/// @return {String} +var serialize = function(name, val, opt){ + opt = opt || {}; + var enc = opt.encode || encode; + var pairs = [name + '=' + enc(val)]; + + if (opt.maxAge) pairs.push('Max-Age=' + opt.maxAge); + if (opt.domain) pairs.push('Domain=' + opt.domain); + if (opt.path) pairs.push('Path=' + opt.path); + if (opt.expires) pairs.push('Expires=' + opt.expires.toUTCString()); + if (opt.httpOnly) pairs.push('HttpOnly'); + if (opt.secure) pairs.push('Secure'); + + return pairs.join('; '); +}; + +/// Parse the given cookie header string into an object +/// The object has the various cookies as keys(names) => values +/// @param {String} str +/// @return {Object} +var parse = function(str, opt) { + opt = opt || {}; + var obj = {} + var pairs = str.split(/[;,] */); + var dec = opt.decode || decode; + + pairs.forEach(function(pair) { + var eq_idx = pair.indexOf('=') + + // skip things that don't look like key=value + if (eq_idx < 0) { + return; + } + + var key = pair.substr(0, eq_idx).trim() + var val = pair.substr(++eq_idx, pair.length).trim(); + + // quoted values + if ('"' == val[0]) { + val = val.slice(1, -1); + } + + // only assign once + if (undefined == obj[key]) { + try { + obj[key] = dec(val); + } catch (e) { + obj[key] = val; + } + } + }); + + return obj; +}; + +var encode = encodeURIComponent; +var decode = decodeURIComponent; + +module.exports.serialize = serialize; +module.exports.parse = parse; diff --git a/server/node_modules/express/node_modules/cookie/package.json b/server/node_modules/express/node_modules/cookie/package.json new file mode 100644 index 0000000..d0a5d8e --- /dev/null +++ b/server/node_modules/express/node_modules/cookie/package.json @@ -0,0 +1,41 @@ +{ + "author": { + "name": "Roman Shtylman", + "email": "shtylman@gmail.com" + }, + "name": "cookie", + "description": "cookie parsing and serialization", + "version": "0.1.0", + "repository": { + "type": "git", + "url": "git://github.com/shtylman/node-cookie.git" + }, + "keywords": [ + "cookie", + "cookies" + ], + "main": "index.js", + "scripts": { + "test": "mocha" + }, + "dependencies": {}, + "devDependencies": { + "mocha": "1.x.x" + }, + "optionalDependencies": {}, + "engines": { + "node": "*" + }, + "readme": "# cookie [![Build Status](https://secure.travis-ci.org/shtylman/node-cookie.png?branch=master)](http://travis-ci.org/shtylman/node-cookie) #\n\ncookie is a basic cookie parser and serializer. It doesn't make assumptions about how you are going to deal with your cookies. It basically just provides a way to read and write the HTTP cookie headers.\n\nSee [RFC6265](http://tools.ietf.org/html/rfc6265) for details about the http header for cookies.\n\n## how?\n\n```\nnpm install cookie\n```\n\n```javascript\nvar cookie = require('cookie');\n\nvar hdr = cookie.serialize('foo', 'bar');\n// hdr = 'foo=bar';\n\nvar cookies = cookie.parse('foo=bar; cat=meow; dog=ruff');\n// cookies = { foo: 'bar', cat: 'meow', dog: 'ruff' };\n```\n\n## more\n\nThe serialize function takes a third parameter, an object, to set cookie options. See the RFC for valid values.\n\n### path\n> cookie path\n\n### expires\n> absolute expiration date for the cookie (Date object)\n\n### maxAge\n> relative max age of the cookie from when the client receives it (seconds)\n\n### domain\n> domain for the cookie\n\n### secure\n> true or false\n\n### httpOnly\n> true or false\n\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/shtylman/node-cookie/issues" + }, + "homepage": "https://github.com/shtylman/node-cookie", + "_id": "cookie@0.1.0", + "dist": { + "shasum": "40ef7eb038967f8e0a8e8df65e7e6d29cb39ca46" + }, + "_from": "cookie@0.1.0", + "_resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz" +} diff --git a/server/node_modules/express/node_modules/cookie/test/mocha.opts b/server/node_modules/express/node_modules/cookie/test/mocha.opts new file mode 100644 index 0000000..e2bfcc5 --- /dev/null +++ b/server/node_modules/express/node_modules/cookie/test/mocha.opts @@ -0,0 +1 @@ +--ui qunit diff --git a/server/node_modules/express/node_modules/cookie/test/parse.js b/server/node_modules/express/node_modules/cookie/test/parse.js new file mode 100644 index 0000000..c6c27a2 --- /dev/null +++ b/server/node_modules/express/node_modules/cookie/test/parse.js @@ -0,0 +1,44 @@ + +var assert = require('assert'); + +var cookie = require('..'); + +suite('parse'); + +test('basic', function() { + assert.deepEqual({ foo: 'bar' }, cookie.parse('foo=bar')); + assert.deepEqual({ foo: '123' }, cookie.parse('foo=123')); +}); + +test('ignore spaces', function() { + assert.deepEqual({ FOO: 'bar', baz: 'raz' }, + cookie.parse('FOO = bar; baz = raz')); +}); + +test('escaping', function() { + assert.deepEqual({ foo: 'bar=123456789&name=Magic+Mouse' }, + cookie.parse('foo="bar=123456789&name=Magic+Mouse"')); + + assert.deepEqual({ email: ' ",;/' }, + cookie.parse('email=%20%22%2c%3b%2f')); +}); + +test('ignore escaping error and return original value', function() { + assert.deepEqual({ foo: '%1', bar: 'bar' }, cookie.parse('foo=%1;bar=bar')); +}); + +test('ignore non values', function() { + assert.deepEqual({ foo: '%1', bar: 'bar' }, cookie.parse('foo=%1;bar=bar;HttpOnly;Secure')); +}); + +test('unencoded', function() { + assert.deepEqual({ foo: 'bar=123456789&name=Magic+Mouse' }, + cookie.parse('foo="bar=123456789&name=Magic+Mouse"',{ + decode: function(value) { return value; } + })); + + assert.deepEqual({ email: '%20%22%2c%3b%2f' }, + cookie.parse('email=%20%22%2c%3b%2f',{ + decode: function(value) { return value; } + })); +}) diff --git a/server/node_modules/express/node_modules/cookie/test/serialize.js b/server/node_modules/express/node_modules/cookie/test/serialize.js new file mode 100644 index 0000000..86bb8c9 --- /dev/null +++ b/server/node_modules/express/node_modules/cookie/test/serialize.js @@ -0,0 +1,64 @@ +// builtin +var assert = require('assert'); + +var cookie = require('..'); + +suite('serialize'); + +test('basic', function() { + assert.equal('foo=bar', cookie.serialize('foo', 'bar')); + assert.equal('foo=bar%20baz', cookie.serialize('foo', 'bar baz')); +}); + +test('path', function() { + assert.equal('foo=bar; Path=/', cookie.serialize('foo', 'bar', { + path: '/' + })); +}); + +test('secure', function() { + assert.equal('foo=bar; Secure', cookie.serialize('foo', 'bar', { + secure: true + })); + + assert.equal('foo=bar', cookie.serialize('foo', 'bar', { + secure: false + })); +}); + +test('domain', function() { + assert.equal('foo=bar; Domain=example.com', cookie.serialize('foo', 'bar', { + domain: 'example.com' + })); +}); + +test('httpOnly', function() { + assert.equal('foo=bar; HttpOnly', cookie.serialize('foo', 'bar', { + httpOnly: true + })); +}); + +test('maxAge', function() { + assert.equal('foo=bar; Max-Age=1000', cookie.serialize('foo', 'bar', { + maxAge: 1000 + })); +}); + +test('escaping', function() { + assert.deepEqual('cat=%2B%20', cookie.serialize('cat', '+ ')); +}); + +test('parse->serialize', function() { + + assert.deepEqual({ cat: 'foo=123&name=baz five' }, cookie.parse( + cookie.serialize('cat', 'foo=123&name=baz five'))); + + assert.deepEqual({ cat: ' ";/' }, cookie.parse( + cookie.serialize('cat', ' ";/'))); +}); + +test('unencoded', function() { + assert.deepEqual('cat=+ ', cookie.serialize('cat', '+ ', { + encode: function(value) { return value; } + })); +}) diff --git a/server/node_modules/express/node_modules/debug/Readme.md b/server/node_modules/express/node_modules/debug/Readme.md new file mode 100644 index 0000000..c5a34e8 --- /dev/null +++ b/server/node_modules/express/node_modules/debug/Readme.md @@ -0,0 +1,115 @@ +# debug + + tiny node.js debugging utility modelled after node core's debugging technique. + +## Installation + +``` +$ npm install debug +``` + +## Usage + + With `debug` you simply invoke the exported function to generate your debug function, passing it a name which will determine if a noop function is returned, or a decorated `console.error`, so all of the `console` format string goodies you're used to work fine. A unique color is selected per-function for visibility. + +Example _app.js_: + +```js +var debug = require('debug')('http') + , http = require('http') + , name = 'My App'; + +// fake app + +debug('booting %s', name); + +http.createServer(function(req, res){ + debug(req.method + ' ' + req.url); + res.end('hello\n'); +}).listen(3000, function(){ + debug('listening'); +}); + +// fake worker of some kind + +require('./worker'); +``` + +Example _worker.js_: + +```js +var debug = require('debug')('worker'); + +setInterval(function(){ + debug('doing some work'); +}, 1000); +``` + + The __DEBUG__ environment variable is then used to enable these based on space or comma-delimited names. Here are some examples: + + ![debug http and worker](http://f.cl.ly/items/18471z1H402O24072r1J/Screenshot.png) + + ![debug worker](http://f.cl.ly/items/1X413v1a3M0d3C2c1E0i/Screenshot.png) + +## Millisecond diff + + When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls. + + ![](http://f.cl.ly/items/2i3h1d3t121M2Z1A3Q0N/Screenshot.png) + + When stderr is not a TTY, `Date#toUTCString()` is used, making it more useful for logging the debug information as shown below: + _(NOTE: Debug now uses stderr instead of stdout, so the correct shell command for this example is actually `DEBUG=* node example/worker 2> out &`)_ + + ![](http://f.cl.ly/items/112H3i0e0o0P0a2Q2r11/Screenshot.png) + +## Conventions + + If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use ":" to separate features. For example "bodyParser" from Connect would then be "connect:bodyParser". + +## Wildcards + + The "*" character may be used as a wildcard. Suppose for example your library has debuggers named "connect:bodyParser", "connect:compress", "connect:session", instead of listing all three with `DEBUG=connect:bodyParser,connect.compress,connect:session`, you may simply do `DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`. + + You can also exclude specific debuggers by prefixing them with a "-" character. For example, `DEBUG=* -connect:*` would include all debuggers except those starting with "connect:". + +## Browser support + + Debug works in the browser as well, currently persisted by `localStorage`. For example if you have `worker:a` and `worker:b` as shown below, and wish to debug both type `debug.enable('worker:*')` in the console and refresh the page, this will remain until you disable with `debug.disable()`. + +```js +a = debug('worker:a'); +b = debug('worker:b'); + +setInterval(function(){ + a('doing some work'); +}, 1000); + +setInterval(function(){ + a('doing some work'); +}, 1200); +``` + +## License + +(The MIT License) + +Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/server/node_modules/express/node_modules/debug/debug.js b/server/node_modules/express/node_modules/debug/debug.js new file mode 100644 index 0000000..509dc0d --- /dev/null +++ b/server/node_modules/express/node_modules/debug/debug.js @@ -0,0 +1,137 @@ + +/** + * Expose `debug()` as the module. + */ + +module.exports = debug; + +/** + * Create a debugger with the given `name`. + * + * @param {String} name + * @return {Type} + * @api public + */ + +function debug(name) { + if (!debug.enabled(name)) return function(){}; + + return function(fmt){ + fmt = coerce(fmt); + + var curr = new Date; + var ms = curr - (debug[name] || curr); + debug[name] = curr; + + fmt = name + + ' ' + + fmt + + ' +' + debug.humanize(ms); + + // This hackery is required for IE8 + // where `console.log` doesn't have 'apply' + window.console + && console.log + && Function.prototype.apply.call(console.log, console, arguments); + } +} + +/** + * The currently active debug mode names. + */ + +debug.names = []; +debug.skips = []; + +/** + * Enables a debug mode by name. This can include modes + * separated by a colon and wildcards. + * + * @param {String} name + * @api public + */ + +debug.enable = function(name) { + try { + localStorage.debug = name; + } catch(e){} + + var split = (name || '').split(/[\s,]+/) + , len = split.length; + + for (var i = 0; i < len; i++) { + name = split[i].replace('*', '.*?'); + if (name[0] === '-') { + debug.skips.push(new RegExp('^' + name.substr(1) + '$')); + } + else { + debug.names.push(new RegExp('^' + name + '$')); + } + } +}; + +/** + * Disable debug output. + * + * @api public + */ + +debug.disable = function(){ + debug.enable(''); +}; + +/** + * Humanize the given `ms`. + * + * @param {Number} m + * @return {String} + * @api private + */ + +debug.humanize = function(ms) { + var sec = 1000 + , min = 60 * 1000 + , hour = 60 * min; + + if (ms >= hour) return (ms / hour).toFixed(1) + 'h'; + if (ms >= min) return (ms / min).toFixed(1) + 'm'; + if (ms >= sec) return (ms / sec | 0) + 's'; + return ms + 'ms'; +}; + +/** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + +debug.enabled = function(name) { + for (var i = 0, len = debug.skips.length; i < len; i++) { + if (debug.skips[i].test(name)) { + return false; + } + } + for (var i = 0, len = debug.names.length; i < len; i++) { + if (debug.names[i].test(name)) { + return true; + } + } + return false; +}; + +/** + * Coerce `val`. + */ + +function coerce(val) { + if (val instanceof Error) return val.stack || val.message; + return val; +} + +// persist + +try { + if (window.localStorage) debug.enable(localStorage.debug); +} catch(e){} diff --git a/server/node_modules/express/node_modules/debug/index.js b/server/node_modules/express/node_modules/debug/index.js new file mode 100644 index 0000000..e02c13b --- /dev/null +++ b/server/node_modules/express/node_modules/debug/index.js @@ -0,0 +1,5 @@ +if ('undefined' == typeof window) { + module.exports = require('./lib/debug'); +} else { + module.exports = require('./debug'); +} diff --git a/server/node_modules/express/node_modules/debug/lib/debug.js b/server/node_modules/express/node_modules/debug/lib/debug.js new file mode 100644 index 0000000..3b0a918 --- /dev/null +++ b/server/node_modules/express/node_modules/debug/lib/debug.js @@ -0,0 +1,147 @@ +/** + * Module dependencies. + */ + +var tty = require('tty'); + +/** + * Expose `debug()` as the module. + */ + +module.exports = debug; + +/** + * Enabled debuggers. + */ + +var names = [] + , skips = []; + +(process.env.DEBUG || '') + .split(/[\s,]+/) + .forEach(function(name){ + name = name.replace('*', '.*?'); + if (name[0] === '-') { + skips.push(new RegExp('^' + name.substr(1) + '$')); + } else { + names.push(new RegExp('^' + name + '$')); + } + }); + +/** + * Colors. + */ + +var colors = [6, 2, 3, 4, 5, 1]; + +/** + * Previous debug() call. + */ + +var prev = {}; + +/** + * Previously assigned color. + */ + +var prevColor = 0; + +/** + * Is stdout a TTY? Colored output is disabled when `true`. + */ + +var isatty = tty.isatty(2); + +/** + * Select a color. + * + * @return {Number} + * @api private + */ + +function color() { + return colors[prevColor++ % colors.length]; +} + +/** + * Humanize the given `ms`. + * + * @param {Number} m + * @return {String} + * @api private + */ + +function humanize(ms) { + var sec = 1000 + , min = 60 * 1000 + , hour = 60 * min; + + if (ms >= hour) return (ms / hour).toFixed(1) + 'h'; + if (ms >= min) return (ms / min).toFixed(1) + 'm'; + if (ms >= sec) return (ms / sec | 0) + 's'; + return ms + 'ms'; +} + +/** + * Create a debugger with the given `name`. + * + * @param {String} name + * @return {Type} + * @api public + */ + +function debug(name) { + function disabled(){} + disabled.enabled = false; + + var match = skips.some(function(re){ + return re.test(name); + }); + + if (match) return disabled; + + match = names.some(function(re){ + return re.test(name); + }); + + if (!match) return disabled; + var c = color(); + + function colored(fmt) { + fmt = coerce(fmt); + + var curr = new Date; + var ms = curr - (prev[name] || curr); + prev[name] = curr; + + fmt = ' \u001b[9' + c + 'm' + name + ' ' + + '\u001b[3' + c + 'm\u001b[90m' + + fmt + '\u001b[3' + c + 'm' + + ' +' + humanize(ms) + '\u001b[0m'; + + console.error.apply(this, arguments); + } + + function plain(fmt) { + fmt = coerce(fmt); + + fmt = new Date().toUTCString() + + ' ' + name + ' ' + fmt; + console.error.apply(this, arguments); + } + + colored.enabled = plain.enabled = true; + + return isatty || process.env.DEBUG_COLORS + ? colored + : plain; +} + +/** + * Coerce `val`. + */ + +function coerce(val) { + if (val instanceof Error) return val.stack || val.message; + return val; +} diff --git a/server/node_modules/express/node_modules/debug/package.json b/server/node_modules/express/node_modules/debug/package.json new file mode 100644 index 0000000..f44874e --- /dev/null +++ b/server/node_modules/express/node_modules/debug/package.json @@ -0,0 +1,46 @@ +{ + "name": "debug", + "version": "0.7.4", + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/debug.git" + }, + "description": "small debugging utility", + "keywords": [ + "debug", + "log", + "debugger" + ], + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "dependencies": {}, + "devDependencies": { + "mocha": "*" + }, + "main": "lib/debug.js", + "browser": "./debug.js", + "engines": { + "node": "*" + }, + "files": [ + "lib/debug.js", + "debug.js", + "index.js" + ], + "component": { + "scripts": { + "debug/index.js": "index.js", + "debug/debug.js": "debug.js" + } + }, + "readme": "# debug\n\n tiny node.js debugging utility modelled after node core's debugging technique.\n\n## Installation\n\n```\n$ npm install debug\n```\n\n## Usage\n\n With `debug` you simply invoke the exported function to generate your debug function, passing it a name which will determine if a noop function is returned, or a decorated `console.error`, so all of the `console` format string goodies you're used to work fine. A unique color is selected per-function for visibility.\n \nExample _app.js_:\n\n```js\nvar debug = require('debug')('http')\n , http = require('http')\n , name = 'My App';\n\n// fake app\n\ndebug('booting %s', name);\n\nhttp.createServer(function(req, res){\n debug(req.method + ' ' + req.url);\n res.end('hello\\n');\n}).listen(3000, function(){\n debug('listening');\n});\n\n// fake worker of some kind\n\nrequire('./worker');\n```\n\nExample _worker.js_:\n\n```js\nvar debug = require('debug')('worker');\n\nsetInterval(function(){\n debug('doing some work');\n}, 1000);\n```\n\n The __DEBUG__ environment variable is then used to enable these based on space or comma-delimited names. Here are some examples:\n\n ![debug http and worker](http://f.cl.ly/items/18471z1H402O24072r1J/Screenshot.png)\n\n ![debug worker](http://f.cl.ly/items/1X413v1a3M0d3C2c1E0i/Screenshot.png)\n\n## Millisecond diff\n\n When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the \"+NNNms\" will show you how much time was spent between calls.\n\n ![](http://f.cl.ly/items/2i3h1d3t121M2Z1A3Q0N/Screenshot.png)\n\n When stderr is not a TTY, `Date#toUTCString()` is used, making it more useful for logging the debug information as shown below:\n _(NOTE: Debug now uses stderr instead of stdout, so the correct shell command for this example is actually `DEBUG=* node example/worker 2> out &`)_\n \n ![](http://f.cl.ly/items/112H3i0e0o0P0a2Q2r11/Screenshot.png)\n \n## Conventions\n\n If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use \":\" to separate features. For example \"bodyParser\" from Connect would then be \"connect:bodyParser\". \n\n## Wildcards\n\n The \"*\" character may be used as a wildcard. Suppose for example your library has debuggers named \"connect:bodyParser\", \"connect:compress\", \"connect:session\", instead of listing all three with `DEBUG=connect:bodyParser,connect.compress,connect:session`, you may simply do `DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`.\n\n You can also exclude specific debuggers by prefixing them with a \"-\" character. For example, `DEBUG=* -connect:*` would include all debuggers except those starting with \"connect:\".\n\n## Browser support\n\n Debug works in the browser as well, currently persisted by `localStorage`. For example if you have `worker:a` and `worker:b` as shown below, and wish to debug both type `debug.enable('worker:*')` in the console and refresh the page, this will remain until you disable with `debug.disable()`. \n\n```js\na = debug('worker:a');\nb = debug('worker:b');\n\nsetInterval(function(){\n a('doing some work');\n}, 1000);\n\nsetInterval(function(){\n a('doing some work');\n}, 1200);\n```\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", + "readmeFilename": "Readme.md", + "bugs": { + "url": "https://github.com/visionmedia/debug/issues" + }, + "homepage": "https://github.com/visionmedia/debug", + "_id": "debug@0.7.4", + "_from": "debug@*" +} diff --git a/server/node_modules/express/node_modules/fresh/.npmignore b/server/node_modules/express/node_modules/fresh/.npmignore new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/server/node_modules/express/node_modules/fresh/.npmignore @@ -0,0 +1 @@ +test diff --git a/server/node_modules/express/node_modules/fresh/History.md b/server/node_modules/express/node_modules/fresh/History.md new file mode 100644 index 0000000..60a2903 --- /dev/null +++ b/server/node_modules/express/node_modules/fresh/History.md @@ -0,0 +1,5 @@ + +0.2.0 / 2013-08-11 +================== + + * fix: return false for no-cache diff --git a/server/node_modules/express/node_modules/fresh/Makefile b/server/node_modules/express/node_modules/fresh/Makefile new file mode 100644 index 0000000..8e8640f --- /dev/null +++ b/server/node_modules/express/node_modules/fresh/Makefile @@ -0,0 +1,7 @@ + +test: + @./node_modules/.bin/mocha \ + --reporter spec \ + --require should + +.PHONY: test \ No newline at end of file diff --git a/server/node_modules/express/node_modules/fresh/Readme.md b/server/node_modules/express/node_modules/fresh/Readme.md new file mode 100644 index 0000000..61366c5 --- /dev/null +++ b/server/node_modules/express/node_modules/fresh/Readme.md @@ -0,0 +1,57 @@ + +# node-fresh + + HTTP response freshness testing + +## fresh(req, res) + + Check freshness of `req` and `res` headers. + + When the cache is "fresh" __true__ is returned, + otherwise __false__ is returned to indicate that + the cache is now stale. + +## Example: + +```js +var req = { 'if-none-match': 'tobi' }; +var res = { 'etag': 'luna' }; +fresh(req, res); +// => false + +var req = { 'if-none-match': 'tobi' }; +var res = { 'etag': 'tobi' }; +fresh(req, res); +// => true +``` + +## Installation + +``` +$ npm install fresh +``` + +## License + +(The MIT License) + +Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/server/node_modules/express/node_modules/fresh/index.js b/server/node_modules/express/node_modules/fresh/index.js new file mode 100644 index 0000000..9c3f47d --- /dev/null +++ b/server/node_modules/express/node_modules/fresh/index.js @@ -0,0 +1,53 @@ + +/** + * Expose `fresh()`. + */ + +module.exports = fresh; + +/** + * Check freshness of `req` and `res` headers. + * + * When the cache is "fresh" __true__ is returned, + * otherwise __false__ is returned to indicate that + * the cache is now stale. + * + * @param {Object} req + * @param {Object} res + * @return {Boolean} + * @api public + */ + +function fresh(req, res) { + // defaults + var etagMatches = true; + var notModified = true; + + // fields + var modifiedSince = req['if-modified-since']; + var noneMatch = req['if-none-match']; + var lastModified = res['last-modified']; + var etag = res['etag']; + var cc = req['cache-control']; + + // unconditional request + if (!modifiedSince && !noneMatch) return false; + + // check for no-cache cache request directive + if (cc && cc.indexOf('no-cache') !== -1) return false; + + // parse if-none-match + if (noneMatch) noneMatch = noneMatch.split(/ *, */); + + // if-none-match + if (noneMatch) etagMatches = ~noneMatch.indexOf(etag) || '*' == noneMatch[0]; + + // if-modified-since + if (modifiedSince) { + modifiedSince = new Date(modifiedSince); + lastModified = new Date(lastModified); + notModified = lastModified <= modifiedSince; + } + + return !! (etagMatches && notModified); +} \ No newline at end of file diff --git a/server/node_modules/express/node_modules/fresh/package.json b/server/node_modules/express/node_modules/fresh/package.json new file mode 100644 index 0000000..3b4ec0e --- /dev/null +++ b/server/node_modules/express/node_modules/fresh/package.json @@ -0,0 +1,32 @@ +{ + "name": "fresh", + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca", + "url": "http://tjholowaychuk.com" + }, + "description": "HTTP response freshness testing", + "version": "0.2.0", + "main": "index.js", + "repository": { + "type": "git", + "url": "https://github.com/visionmedia/node-fresh.git" + }, + "dependencies": {}, + "devDependencies": { + "mocha": "*", + "should": "*" + }, + "readme": "\n# node-fresh\n\n HTTP response freshness testing\n\n## fresh(req, res)\n\n Check freshness of `req` and `res` headers.\n\n When the cache is \"fresh\" __true__ is returned,\n otherwise __false__ is returned to indicate that\n the cache is now stale.\n\n## Example:\n\n```js\nvar req = { 'if-none-match': 'tobi' };\nvar res = { 'etag': 'luna' };\nfresh(req, res);\n// => false\n\nvar req = { 'if-none-match': 'tobi' };\nvar res = { 'etag': 'tobi' };\nfresh(req, res);\n// => true\n```\n\n## Installation\n\n```\n$ npm install fresh\n```\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", + "readmeFilename": "Readme.md", + "bugs": { + "url": "https://github.com/visionmedia/node-fresh/issues" + }, + "homepage": "https://github.com/visionmedia/node-fresh", + "_id": "fresh@0.2.0", + "dist": { + "shasum": "3fad479ba1e3bbd821dcbcb0bfa07f1ddd70d78a" + }, + "_from": "fresh@0.2.0", + "_resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.0.tgz" +} diff --git a/server/node_modules/express/node_modules/methods/History.md b/server/node_modules/express/node_modules/methods/History.md new file mode 100644 index 0000000..1d0e229 --- /dev/null +++ b/server/node_modules/express/node_modules/methods/History.md @@ -0,0 +1,5 @@ + +0.1.0 / 2013-10-28 +================== + + * add http.METHODS support diff --git a/server/node_modules/express/node_modules/methods/Readme.md b/server/node_modules/express/node_modules/methods/Readme.md new file mode 100644 index 0000000..ac0658e --- /dev/null +++ b/server/node_modules/express/node_modules/methods/Readme.md @@ -0,0 +1,4 @@ + +# Methods + + HTTP verbs that node core's parser supports. diff --git a/server/node_modules/express/node_modules/methods/index.js b/server/node_modules/express/node_modules/methods/index.js new file mode 100644 index 0000000..95b93f5 --- /dev/null +++ b/server/node_modules/express/node_modules/methods/index.js @@ -0,0 +1,37 @@ + +var http = require('http'); + +if (http.METHODS) { + module.exports = http.METHODS.map(function(method){ + return method.toLowerCase(); + }); + + return; +} + +module.exports = [ + 'get', + 'post', + 'put', + 'head', + 'delete', + 'options', + 'trace', + 'copy', + 'lock', + 'mkcol', + 'move', + 'propfind', + 'proppatch', + 'unlock', + 'report', + 'mkactivity', + 'checkout', + 'merge', + 'm-search', + 'notify', + 'subscribe', + 'unsubscribe', + 'patch', + 'search' +]; diff --git a/server/node_modules/express/node_modules/methods/package.json b/server/node_modules/express/node_modules/methods/package.json new file mode 100644 index 0000000..a8a120c --- /dev/null +++ b/server/node_modules/express/node_modules/methods/package.json @@ -0,0 +1,33 @@ +{ + "name": "methods", + "version": "0.1.0", + "description": "HTTP methods that node supports", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "http", + "methods" + ], + "author": { + "name": "TJ Holowaychuk" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/node-methods.git" + }, + "readme": "\n# Methods\n\n HTTP verbs that node core's parser supports.\n", + "readmeFilename": "Readme.md", + "bugs": { + "url": "https://github.com/visionmedia/node-methods/issues" + }, + "homepage": "https://github.com/visionmedia/node-methods", + "_id": "methods@0.1.0", + "dist": { + "shasum": "2e7de0fa025568bc718fa97c27da95e460306d40" + }, + "_from": "methods@0.1.0", + "_resolved": "https://registry.npmjs.org/methods/-/methods-0.1.0.tgz" +} diff --git a/server/node_modules/express/node_modules/mkdirp/.npmignore b/server/node_modules/express/node_modules/mkdirp/.npmignore new file mode 100644 index 0000000..9303c34 --- /dev/null +++ b/server/node_modules/express/node_modules/mkdirp/.npmignore @@ -0,0 +1,2 @@ +node_modules/ +npm-debug.log \ No newline at end of file diff --git a/server/node_modules/express/node_modules/mkdirp/.travis.yml b/server/node_modules/express/node_modules/mkdirp/.travis.yml new file mode 100644 index 0000000..84fd7ca --- /dev/null +++ b/server/node_modules/express/node_modules/mkdirp/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - 0.6 + - 0.8 + - 0.9 diff --git a/server/node_modules/express/node_modules/mkdirp/LICENSE b/server/node_modules/express/node_modules/mkdirp/LICENSE new file mode 100644 index 0000000..432d1ae --- /dev/null +++ b/server/node_modules/express/node_modules/mkdirp/LICENSE @@ -0,0 +1,21 @@ +Copyright 2010 James Halliday (mail@substack.net) + +This project is free software released under the MIT/X11 license: + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/server/node_modules/express/node_modules/mkdirp/examples/pow.js b/server/node_modules/express/node_modules/mkdirp/examples/pow.js new file mode 100644 index 0000000..e692421 --- /dev/null +++ b/server/node_modules/express/node_modules/mkdirp/examples/pow.js @@ -0,0 +1,6 @@ +var mkdirp = require('mkdirp'); + +mkdirp('/tmp/foo/bar/baz', function (err) { + if (err) console.error(err) + else console.log('pow!') +}); diff --git a/server/node_modules/express/node_modules/mkdirp/index.js b/server/node_modules/express/node_modules/mkdirp/index.js new file mode 100644 index 0000000..fda6de8 --- /dev/null +++ b/server/node_modules/express/node_modules/mkdirp/index.js @@ -0,0 +1,82 @@ +var path = require('path'); +var fs = require('fs'); + +module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; + +function mkdirP (p, mode, f, made) { + if (typeof mode === 'function' || mode === undefined) { + f = mode; + mode = 0777 & (~process.umask()); + } + if (!made) made = null; + + var cb = f || function () {}; + if (typeof mode === 'string') mode = parseInt(mode, 8); + p = path.resolve(p); + + fs.mkdir(p, mode, function (er) { + if (!er) { + made = made || p; + return cb(null, made); + } + switch (er.code) { + case 'ENOENT': + mkdirP(path.dirname(p), mode, function (er, made) { + if (er) cb(er, made); + else mkdirP(p, mode, cb, made); + }); + break; + + // In the case of any other error, just see if there's a dir + // there already. If so, then hooray! If not, then something + // is borked. + default: + fs.stat(p, function (er2, stat) { + // if the stat fails, then that's super weird. + // let the original error be the failure reason. + if (er2 || !stat.isDirectory()) cb(er, made) + else cb(null, made); + }); + break; + } + }); +} + +mkdirP.sync = function sync (p, mode, made) { + if (mode === undefined) { + mode = 0777 & (~process.umask()); + } + if (!made) made = null; + + if (typeof mode === 'string') mode = parseInt(mode, 8); + p = path.resolve(p); + + try { + fs.mkdirSync(p, mode); + made = made || p; + } + catch (err0) { + switch (err0.code) { + case 'ENOENT' : + made = sync(path.dirname(p), mode, made); + sync(p, mode, made); + break; + + // In the case of any other error, just see if there's a dir + // there already. If so, then hooray! If not, then something + // is borked. + default: + var stat; + try { + stat = fs.statSync(p); + } + catch (err1) { + throw err0; + } + if (!stat.isDirectory()) throw err0; + break; + } + } + + return made; +}; diff --git a/server/node_modules/express/node_modules/mkdirp/package.json b/server/node_modules/express/node_modules/mkdirp/package.json new file mode 100644 index 0000000..8addde8 --- /dev/null +++ b/server/node_modules/express/node_modules/mkdirp/package.json @@ -0,0 +1,38 @@ +{ + "name": "mkdirp", + "description": "Recursively mkdir, like `mkdir -p`", + "version": "0.3.5", + "author": { + "name": "James Halliday", + "email": "mail@substack.net", + "url": "http://substack.net" + }, + "main": "./index", + "keywords": [ + "mkdir", + "directory" + ], + "repository": { + "type": "git", + "url": "http://github.com/substack/node-mkdirp.git" + }, + "scripts": { + "test": "tap test/*.js" + }, + "devDependencies": { + "tap": "~0.4.0" + }, + "license": "MIT", + "readme": "# mkdirp\n\nLike `mkdir -p`, but in node.js!\n\n[![build status](https://secure.travis-ci.org/substack/node-mkdirp.png)](http://travis-ci.org/substack/node-mkdirp)\n\n# example\n\n## pow.js\n\n```js\nvar mkdirp = require('mkdirp');\n \nmkdirp('/tmp/foo/bar/baz', function (err) {\n if (err) console.error(err)\n else console.log('pow!')\n});\n```\n\nOutput\n\n```\npow!\n```\n\nAnd now /tmp/foo/bar/baz exists, huzzah!\n\n# methods\n\n```js\nvar mkdirp = require('mkdirp');\n```\n\n## mkdirp(dir, mode, cb)\n\nCreate a new directory and any necessary subdirectories at `dir` with octal\npermission string `mode`.\n\nIf `mode` isn't specified, it defaults to `0777 & (~process.umask())`.\n\n`cb(err, made)` fires with the error or the first directory `made`\nthat had to be created, if any.\n\n## mkdirp.sync(dir, mode)\n\nSynchronously create a new directory and any necessary subdirectories at `dir`\nwith octal permission string `mode`.\n\nIf `mode` isn't specified, it defaults to `0777 & (~process.umask())`.\n\nReturns the first directory that had to be created, if any.\n\n# install\n\nWith [npm](http://npmjs.org) do:\n\n```\nnpm install mkdirp\n```\n\n# license\n\nMIT\n", + "readmeFilename": "readme.markdown", + "bugs": { + "url": "https://github.com/substack/node-mkdirp/issues" + }, + "homepage": "https://github.com/substack/node-mkdirp", + "_id": "mkdirp@0.3.5", + "dist": { + "shasum": "67e5c12bd18b5d68b5f56518ff1ea28068eecb4f" + }, + "_from": "mkdirp@0.3.5", + "_resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" +} diff --git a/server/node_modules/express/node_modules/mkdirp/readme.markdown b/server/node_modules/express/node_modules/mkdirp/readme.markdown new file mode 100644 index 0000000..83b0216 --- /dev/null +++ b/server/node_modules/express/node_modules/mkdirp/readme.markdown @@ -0,0 +1,63 @@ +# mkdirp + +Like `mkdir -p`, but in node.js! + +[![build status](https://secure.travis-ci.org/substack/node-mkdirp.png)](http://travis-ci.org/substack/node-mkdirp) + +# example + +## pow.js + +```js +var mkdirp = require('mkdirp'); + +mkdirp('/tmp/foo/bar/baz', function (err) { + if (err) console.error(err) + else console.log('pow!') +}); +``` + +Output + +``` +pow! +``` + +And now /tmp/foo/bar/baz exists, huzzah! + +# methods + +```js +var mkdirp = require('mkdirp'); +``` + +## mkdirp(dir, mode, cb) + +Create a new directory and any necessary subdirectories at `dir` with octal +permission string `mode`. + +If `mode` isn't specified, it defaults to `0777 & (~process.umask())`. + +`cb(err, made)` fires with the error or the first directory `made` +that had to be created, if any. + +## mkdirp.sync(dir, mode) + +Synchronously create a new directory and any necessary subdirectories at `dir` +with octal permission string `mode`. + +If `mode` isn't specified, it defaults to `0777 & (~process.umask())`. + +Returns the first directory that had to be created, if any. + +# install + +With [npm](http://npmjs.org) do: + +``` +npm install mkdirp +``` + +# license + +MIT diff --git a/server/node_modules/express/node_modules/mkdirp/test/chmod.js b/server/node_modules/express/node_modules/mkdirp/test/chmod.js new file mode 100644 index 0000000..520dcb8 --- /dev/null +++ b/server/node_modules/express/node_modules/mkdirp/test/chmod.js @@ -0,0 +1,38 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +var ps = [ '', 'tmp' ]; + +for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); +} + +var file = ps.join('/'); + +test('chmod-pre', function (t) { + var mode = 0744 + mkdirp(file, mode, function (er) { + t.ifError(er, 'should not error'); + fs.stat(file, function (er, stat) { + t.ifError(er, 'should exist'); + t.ok(stat && stat.isDirectory(), 'should be directory'); + t.equal(stat && stat.mode & 0777, mode, 'should be 0744'); + t.end(); + }); + }); +}); + +test('chmod', function (t) { + var mode = 0755 + mkdirp(file, mode, function (er) { + t.ifError(er, 'should not error'); + fs.stat(file, function (er, stat) { + t.ifError(er, 'should exist'); + t.ok(stat && stat.isDirectory(), 'should be directory'); + t.end(); + }); + }); +}); diff --git a/server/node_modules/express/node_modules/mkdirp/test/clobber.js b/server/node_modules/express/node_modules/mkdirp/test/clobber.js new file mode 100644 index 0000000..0eb7099 --- /dev/null +++ b/server/node_modules/express/node_modules/mkdirp/test/clobber.js @@ -0,0 +1,37 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +var ps = [ '', 'tmp' ]; + +for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); +} + +var file = ps.join('/'); + +// a file in the way +var itw = ps.slice(0, 3).join('/'); + + +test('clobber-pre', function (t) { + console.error("about to write to "+itw) + fs.writeFileSync(itw, 'I AM IN THE WAY, THE TRUTH, AND THE LIGHT.'); + + fs.stat(itw, function (er, stat) { + t.ifError(er) + t.ok(stat && stat.isFile(), 'should be file') + t.end() + }) +}) + +test('clobber', function (t) { + t.plan(2); + mkdirp(file, 0755, function (err) { + t.ok(err); + t.equal(err.code, 'ENOTDIR'); + t.end(); + }); +}); diff --git a/server/node_modules/express/node_modules/mkdirp/test/mkdirp.js b/server/node_modules/express/node_modules/mkdirp/test/mkdirp.js new file mode 100644 index 0000000..b07cd70 --- /dev/null +++ b/server/node_modules/express/node_modules/mkdirp/test/mkdirp.js @@ -0,0 +1,28 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('woo', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); diff --git a/server/node_modules/express/node_modules/mkdirp/test/perm.js b/server/node_modules/express/node_modules/mkdirp/test/perm.js new file mode 100644 index 0000000..23a7abb --- /dev/null +++ b/server/node_modules/express/node_modules/mkdirp/test/perm.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('async perm', function (t) { + t.plan(2); + var file = '/tmp/' + (Math.random() * (1<<30)).toString(16); + + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); + +test('async root perm', function (t) { + mkdirp('/tmp', 0755, function (err) { + if (err) t.fail(err); + t.end(); + }); + t.end(); +}); diff --git a/server/node_modules/express/node_modules/mkdirp/test/perm_sync.js b/server/node_modules/express/node_modules/mkdirp/test/perm_sync.js new file mode 100644 index 0000000..f685f60 --- /dev/null +++ b/server/node_modules/express/node_modules/mkdirp/test/perm_sync.js @@ -0,0 +1,39 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('sync perm', function (t) { + t.plan(2); + var file = '/tmp/' + (Math.random() * (1<<30)).toString(16) + '.json'; + + mkdirp.sync(file, 0755); + path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }); +}); + +test('sync root perm', function (t) { + t.plan(1); + + var file = '/tmp'; + mkdirp.sync(file, 0755); + path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }); +}); diff --git a/server/node_modules/express/node_modules/mkdirp/test/race.js b/server/node_modules/express/node_modules/mkdirp/test/race.js new file mode 100644 index 0000000..96a0447 --- /dev/null +++ b/server/node_modules/express/node_modules/mkdirp/test/race.js @@ -0,0 +1,41 @@ +var mkdirp = require('../').mkdirp; +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('race', function (t) { + t.plan(4); + var ps = [ '', 'tmp' ]; + + for (var i = 0; i < 25; i++) { + var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + ps.push(dir); + } + var file = ps.join('/'); + + var res = 2; + mk(file, function () { + if (--res === 0) t.end(); + }); + + mk(file, function () { + if (--res === 0) t.end(); + }); + + function mk (file, cb) { + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + if (cb) cb(); + } + }) + }) + }); + } +}); diff --git a/server/node_modules/express/node_modules/mkdirp/test/rel.js b/server/node_modules/express/node_modules/mkdirp/test/rel.js new file mode 100644 index 0000000..7985824 --- /dev/null +++ b/server/node_modules/express/node_modules/mkdirp/test/rel.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('rel', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var cwd = process.cwd(); + process.chdir('/tmp'); + + var file = [x,y,z].join('/'); + + mkdirp(file, 0755, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + process.chdir(cwd); + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); diff --git a/server/node_modules/express/node_modules/mkdirp/test/return.js b/server/node_modules/express/node_modules/mkdirp/test/return.js new file mode 100644 index 0000000..bce68e5 --- /dev/null +++ b/server/node_modules/express/node_modules/mkdirp/test/return.js @@ -0,0 +1,25 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('return value', function (t) { + t.plan(4); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + // should return the first dir created. + // By this point, it would be profoundly surprising if /tmp didn't + // already exist, since every other test makes things in there. + mkdirp(file, function (err, made) { + t.ifError(err); + t.equal(made, '/tmp/' + x); + mkdirp(file, function (err, made) { + t.ifError(err); + t.equal(made, null); + }); + }); +}); diff --git a/server/node_modules/express/node_modules/mkdirp/test/return_sync.js b/server/node_modules/express/node_modules/mkdirp/test/return_sync.js new file mode 100644 index 0000000..7c222d3 --- /dev/null +++ b/server/node_modules/express/node_modules/mkdirp/test/return_sync.js @@ -0,0 +1,24 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('return value', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + // should return the first dir created. + // By this point, it would be profoundly surprising if /tmp didn't + // already exist, since every other test makes things in there. + // Note that this will throw on failure, which will fail the test. + var made = mkdirp.sync(file); + t.equal(made, '/tmp/' + x); + + // making the same file again should have no effect. + made = mkdirp.sync(file); + t.equal(made, null); +}); diff --git a/server/node_modules/express/node_modules/mkdirp/test/root.js b/server/node_modules/express/node_modules/mkdirp/test/root.js new file mode 100644 index 0000000..97ad7a2 --- /dev/null +++ b/server/node_modules/express/node_modules/mkdirp/test/root.js @@ -0,0 +1,18 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('root', function (t) { + // '/' on unix, 'c:/' on windows. + var file = path.resolve('/'); + + mkdirp(file, 0755, function (err) { + if (err) throw err + fs.stat(file, function (er, stat) { + if (er) throw er + t.ok(stat.isDirectory(), 'target is a directory'); + t.end(); + }) + }); +}); diff --git a/server/node_modules/express/node_modules/mkdirp/test/sync.js b/server/node_modules/express/node_modules/mkdirp/test/sync.js new file mode 100644 index 0000000..7530cad --- /dev/null +++ b/server/node_modules/express/node_modules/mkdirp/test/sync.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('sync', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + try { + mkdirp.sync(file, 0755); + } catch (err) { + t.fail(err); + return t.end(); + } + + path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0755); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }); + }); +}); diff --git a/server/node_modules/express/node_modules/mkdirp/test/umask.js b/server/node_modules/express/node_modules/mkdirp/test/umask.js new file mode 100644 index 0000000..64ccafe --- /dev/null +++ b/server/node_modules/express/node_modules/mkdirp/test/umask.js @@ -0,0 +1,28 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('implicit mode from umask', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + mkdirp(file, function (err) { + if (err) t.fail(err); + else path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, 0777 & (~process.umask())); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }) + }) + }); +}); diff --git a/server/node_modules/express/node_modules/mkdirp/test/umask_sync.js b/server/node_modules/express/node_modules/mkdirp/test/umask_sync.js new file mode 100644 index 0000000..35bd5cb --- /dev/null +++ b/server/node_modules/express/node_modules/mkdirp/test/umask_sync.js @@ -0,0 +1,32 @@ +var mkdirp = require('../'); +var path = require('path'); +var fs = require('fs'); +var test = require('tap').test; + +test('umask sync modes', function (t) { + t.plan(2); + var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16); + + var file = '/tmp/' + [x,y,z].join('/'); + + try { + mkdirp.sync(file); + } catch (err) { + t.fail(err); + return t.end(); + } + + path.exists(file, function (ex) { + if (!ex) t.fail('file not created') + else fs.stat(file, function (err, stat) { + if (err) t.fail(err) + else { + t.equal(stat.mode & 0777, (0777 & (~process.umask()))); + t.ok(stat.isDirectory(), 'target not a directory'); + t.end(); + } + }); + }); +}); diff --git a/server/node_modules/express/node_modules/range-parser/.npmignore b/server/node_modules/express/node_modules/range-parser/.npmignore new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/server/node_modules/express/node_modules/range-parser/.npmignore @@ -0,0 +1 @@ +test diff --git a/server/node_modules/express/node_modules/range-parser/History.md b/server/node_modules/express/node_modules/range-parser/History.md new file mode 100644 index 0000000..82df7b1 --- /dev/null +++ b/server/node_modules/express/node_modules/range-parser/History.md @@ -0,0 +1,15 @@ + +0.0.4 / 2012-06-17 +================== + + * changed: ret -1 for unsatisfiable and -2 when invalid + +0.0.3 / 2012-06-17 +================== + + * fix last-byte-pos default to len - 1 + +0.0.2 / 2012-06-14 +================== + + * add `.type` diff --git a/server/node_modules/express/node_modules/range-parser/Makefile b/server/node_modules/express/node_modules/range-parser/Makefile new file mode 100644 index 0000000..8e8640f --- /dev/null +++ b/server/node_modules/express/node_modules/range-parser/Makefile @@ -0,0 +1,7 @@ + +test: + @./node_modules/.bin/mocha \ + --reporter spec \ + --require should + +.PHONY: test \ No newline at end of file diff --git a/server/node_modules/express/node_modules/range-parser/Readme.md b/server/node_modules/express/node_modules/range-parser/Readme.md new file mode 100644 index 0000000..b2a67fe --- /dev/null +++ b/server/node_modules/express/node_modules/range-parser/Readme.md @@ -0,0 +1,28 @@ + +# node-range-parser + + Range header field parser. + +## Example: + +```js +assert(-1 == parse(200, 'bytes=500-20')); +assert(-2 == parse(200, 'bytes=malformed')); +parse(200, 'bytes=0-499').should.eql(arr('bytes', [{ start: 0, end: 199 }])); +parse(1000, 'bytes=0-499').should.eql(arr('bytes', [{ start: 0, end: 499 }])); +parse(1000, 'bytes=40-80').should.eql(arr('bytes', [{ start: 40, end: 80 }])); +parse(1000, 'bytes=-500').should.eql(arr('bytes', [{ start: 500, end: 999 }])); +parse(1000, 'bytes=-400').should.eql(arr('bytes', [{ start: 600, end: 999 }])); +parse(1000, 'bytes=500-').should.eql(arr('bytes', [{ start: 500, end: 999 }])); +parse(1000, 'bytes=400-').should.eql(arr('bytes', [{ start: 400, end: 999 }])); +parse(1000, 'bytes=0-0').should.eql(arr('bytes', [{ start: 0, end: 0 }])); +parse(1000, 'bytes=-1').should.eql(arr('bytes', [{ start: 999, end: 999 }])); +parse(1000, 'items=0-5').should.eql(arr('items', [{ start: 0, end: 5 }])); +parse(1000, 'bytes=40-80,-1').should.eql(arr('bytes', [{ start: 40, end: 80 }, { start: 999, end: 999 }])); +``` + +## Installation + +``` +$ npm install range-parser +``` \ No newline at end of file diff --git a/server/node_modules/express/node_modules/range-parser/index.js b/server/node_modules/express/node_modules/range-parser/index.js new file mode 100644 index 0000000..9b0f7a8 --- /dev/null +++ b/server/node_modules/express/node_modules/range-parser/index.js @@ -0,0 +1,49 @@ + +/** + * Parse "Range" header `str` relative to the given file `size`. + * + * @param {Number} size + * @param {String} str + * @return {Array} + * @api public + */ + +module.exports = function(size, str){ + var valid = true; + var i = str.indexOf('='); + + if (-1 == i) return -2; + + var arr = str.slice(i + 1).split(',').map(function(range){ + var range = range.split('-') + , start = parseInt(range[0], 10) + , end = parseInt(range[1], 10); + + // -nnn + if (isNaN(start)) { + start = size - end; + end = size - 1; + // nnn- + } else if (isNaN(end)) { + end = size - 1; + } + + // limit last-byte-pos to current length + if (end > size - 1) end = size - 1; + + // invalid + if (isNaN(start) + || isNaN(end) + || start > end + || start < 0) valid = false; + + return { + start: start, + end: end + }; + }); + + arr.type = str.slice(0, i); + + return valid ? arr : -1; +}; \ No newline at end of file diff --git a/server/node_modules/express/node_modules/range-parser/package.json b/server/node_modules/express/node_modules/range-parser/package.json new file mode 100644 index 0000000..efdf450 --- /dev/null +++ b/server/node_modules/express/node_modules/range-parser/package.json @@ -0,0 +1,20 @@ +{ + "name": "range-parser", + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca", + "url": "http://tjholowaychuk.com" + }, + "description": "Range header field string parser", + "version": "0.0.4", + "main": "index.js", + "dependencies": {}, + "devDependencies": { + "mocha": "*", + "should": "*" + }, + "readme": "\n# node-range-parser\n\n Range header field parser.\n\n## Example:\n\n```js\nassert(-1 == parse(200, 'bytes=500-20'));\nassert(-2 == parse(200, 'bytes=malformed'));\nparse(200, 'bytes=0-499').should.eql(arr('bytes', [{ start: 0, end: 199 }]));\nparse(1000, 'bytes=0-499').should.eql(arr('bytes', [{ start: 0, end: 499 }]));\nparse(1000, 'bytes=40-80').should.eql(arr('bytes', [{ start: 40, end: 80 }]));\nparse(1000, 'bytes=-500').should.eql(arr('bytes', [{ start: 500, end: 999 }]));\nparse(1000, 'bytes=-400').should.eql(arr('bytes', [{ start: 600, end: 999 }]));\nparse(1000, 'bytes=500-').should.eql(arr('bytes', [{ start: 500, end: 999 }]));\nparse(1000, 'bytes=400-').should.eql(arr('bytes', [{ start: 400, end: 999 }]));\nparse(1000, 'bytes=0-0').should.eql(arr('bytes', [{ start: 0, end: 0 }]));\nparse(1000, 'bytes=-1').should.eql(arr('bytes', [{ start: 999, end: 999 }]));\nparse(1000, 'items=0-5').should.eql(arr('items', [{ start: 0, end: 5 }]));\nparse(1000, 'bytes=40-80,-1').should.eql(arr('bytes', [{ start: 40, end: 80 }, { start: 999, end: 999 }]));\n```\n\n## Installation\n\n```\n$ npm install range-parser\n```", + "readmeFilename": "Readme.md", + "_id": "range-parser@0.0.4", + "_from": "range-parser@0.0.4" +} diff --git a/server/node_modules/express/node_modules/send/.npmignore b/server/node_modules/express/node_modules/send/.npmignore new file mode 100644 index 0000000..f1250e5 --- /dev/null +++ b/server/node_modules/express/node_modules/send/.npmignore @@ -0,0 +1,4 @@ +support +test +examples +*.sock diff --git a/server/node_modules/express/node_modules/send/History.md b/server/node_modules/express/node_modules/send/History.md new file mode 100644 index 0000000..55c4af7 --- /dev/null +++ b/server/node_modules/express/node_modules/send/History.md @@ -0,0 +1,40 @@ + +0.1.4 / 2013-08-11 +================== + + * update fresh + +0.1.3 / 2013-07-08 +================== + + * Revert "Fix fd leak" + +0.1.2 / 2013-07-03 +================== + + * Fix fd leak + +0.1.0 / 2012-08-25 +================== + + * add options parameter to send() that is passed to fs.createReadStream() [kanongil] + +0.0.4 / 2012-08-16 +================== + + * allow custom "Accept-Ranges" definition + +0.0.3 / 2012-07-16 +================== + + * fix normalization of the root directory. Closes #3 + +0.0.2 / 2012-07-09 +================== + + * add passing of req explicitly for now (YUCK) + +0.0.1 / 2010-01-03 +================== + + * Initial release diff --git a/server/node_modules/express/node_modules/send/Makefile b/server/node_modules/express/node_modules/send/Makefile new file mode 100644 index 0000000..a9dcfd5 --- /dev/null +++ b/server/node_modules/express/node_modules/send/Makefile @@ -0,0 +1,8 @@ + +test: + @./node_modules/.bin/mocha \ + --require should \ + --reporter spec \ + --bail + +.PHONY: test \ No newline at end of file diff --git a/server/node_modules/express/node_modules/send/Readme.md b/server/node_modules/express/node_modules/send/Readme.md new file mode 100644 index 0000000..ea7b234 --- /dev/null +++ b/server/node_modules/express/node_modules/send/Readme.md @@ -0,0 +1,128 @@ +# send + + Send is Connect's `static()` extracted for generalized use, a streaming static file + server supporting partial responses (Ranges), conditional-GET negotiation, high test coverage, and granular events which may be leveraged to take appropriate actions in your application or framework. + +## Installation + + $ npm install send + +## Examples + + Small: + +```js +var http = require('http'); +var send = require('send'); + +var app = http.createServer(function(req, res){ + send(req, req.url).pipe(res); +}).listen(3000); +``` + + Serving from a root directory with custom error-handling: + +```js +var http = require('http'); +var send = require('send'); +var url = require('url'); + +var app = http.createServer(function(req, res){ + // your custom error-handling logic: + function error(err) { + res.statusCode = err.status || 500; + res.end(err.message); + } + + // your custom directory handling logic: + function redirect() { + res.statusCode = 301; + res.setHeader('Location', req.url + '/'); + res.end('Redirecting to ' + req.url + '/'); + } + + // transfer arbitrary files from within + // /www/example.com/public/* + send(req, url.parse(req.url).pathname) + .root('/www/example.com/public') + .on('error', error) + .on('directory', redirect) + .pipe(res); +}).listen(3000); +``` + +## API + +### Events + + - `error` an error occurred `(err)` + - `directory` a directory was requested + - `file` a file was requested `(path, stat)` + - `stream` file streaming has started `(stream)` + - `end` streaming has completed + +### .root(dir) + + Serve files relative to `path`. Aliased as `.from(dir)`. + +### .index(path) + + By default send supports "index.html" files, to disable this + invoke `.index(false)` or to supply a new index pass a string. + +### .maxage(ms) + + Provide a max-age in milliseconds for http caching, defaults to 0. + +### .hidden(bool) + + Enable or disable transfer of hidden files, defaults to false. + +## Error-handling + + By default when no `error` listeners are present an automatic response will be made, otherwise you have full control over the response, aka you may show a 5xx page etc. + +## Caching + + It does _not_ perform internal caching, you should use a reverse proxy cache such + as Varnish for this, or those fancy things called CDNs. If your application is small enough that it would benefit from single-node memory caching, it's small enough that it does not need caching at all ;). + +## Debugging + + To enable `debug()` instrumentation output export __DEBUG__: + +``` +$ DEBUG=send node app +``` + +## Running tests + +``` +$ npm install +$ make test +``` + +## License + +(The MIT License) + +Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/server/node_modules/express/node_modules/send/index.js b/server/node_modules/express/node_modules/send/index.js new file mode 100644 index 0000000..f17158d --- /dev/null +++ b/server/node_modules/express/node_modules/send/index.js @@ -0,0 +1,2 @@ + +module.exports = require('./lib/send'); \ No newline at end of file diff --git a/server/node_modules/express/node_modules/send/lib/send.js b/server/node_modules/express/node_modules/send/lib/send.js new file mode 100644 index 0000000..a3d94a6 --- /dev/null +++ b/server/node_modules/express/node_modules/send/lib/send.js @@ -0,0 +1,474 @@ + +/** + * Module dependencies. + */ + +var debug = require('debug')('send') + , parseRange = require('range-parser') + , Stream = require('stream') + , mime = require('mime') + , fresh = require('fresh') + , path = require('path') + , http = require('http') + , fs = require('fs') + , basename = path.basename + , normalize = path.normalize + , join = path.join + , utils = require('./utils'); + +/** + * Expose `send`. + */ + +exports = module.exports = send; + +/** + * Expose mime module. + */ + +exports.mime = mime; + +/** + * Return a `SendStream` for `req` and `path`. + * + * @param {Request} req + * @param {String} path + * @param {Object} options + * @return {SendStream} + * @api public + */ + +function send(req, path, options) { + return new SendStream(req, path, options); +} + +/** + * Initialize a `SendStream` with the given `path`. + * + * Events: + * + * - `error` an error occurred + * - `stream` file streaming has started + * - `end` streaming has completed + * - `directory` a directory was requested + * + * @param {Request} req + * @param {String} path + * @param {Object} options + * @api private + */ + +function SendStream(req, path, options) { + var self = this; + this.req = req; + this.path = path; + this.options = options || {}; + this.maxage(0); + this.hidden(false); + this.index('index.html'); +} + +/** + * Inherits from `Stream.prototype`. + */ + +SendStream.prototype.__proto__ = Stream.prototype; + +/** + * Enable or disable "hidden" (dot) files. + * + * @param {Boolean} path + * @return {SendStream} + * @api public + */ + +SendStream.prototype.hidden = function(val){ + debug('hidden %s', val); + this._hidden = val; + return this; +}; + +/** + * Set index `path`, set to a falsy + * value to disable index support. + * + * @param {String|Boolean} path + * @return {SendStream} + * @api public + */ + +SendStream.prototype.index = function(path){ + debug('index %s', path); + this._index = path; + return this; +}; + +/** + * Set root `path`. + * + * @param {String} path + * @return {SendStream} + * @api public + */ + +SendStream.prototype.root = +SendStream.prototype.from = function(path){ + this._root = normalize(path); + return this; +}; + +/** + * Set max-age to `ms`. + * + * @param {Number} ms + * @return {SendStream} + * @api public + */ + +SendStream.prototype.maxage = function(ms){ + if (Infinity == ms) ms = 60 * 60 * 24 * 365 * 1000; + debug('max-age %d', ms); + this._maxage = ms; + return this; +}; + +/** + * Emit error with `status`. + * + * @param {Number} status + * @api private + */ + +SendStream.prototype.error = function(status, err){ + var res = this.res; + var msg = http.STATUS_CODES[status]; + err = err || new Error(msg); + err.status = status; + if (this.listeners('error').length) return this.emit('error', err); + res.statusCode = err.status; + res.end(msg); +}; + +/** + * Check if the pathname is potentially malicious. + * + * @return {Boolean} + * @api private + */ + +SendStream.prototype.isMalicious = function(){ + return !this._root && ~this.path.indexOf('..'); +}; + +/** + * Check if the pathname ends with "/". + * + * @return {Boolean} + * @api private + */ + +SendStream.prototype.hasTrailingSlash = function(){ + return '/' == this.path[this.path.length - 1]; +}; + +/** + * Check if the basename leads with ".". + * + * @return {Boolean} + * @api private + */ + +SendStream.prototype.hasLeadingDot = function(){ + return '.' == basename(this.path)[0]; +}; + +/** + * Check if this is a conditional GET request. + * + * @return {Boolean} + * @api private + */ + +SendStream.prototype.isConditionalGET = function(){ + return this.req.headers['if-none-match'] + || this.req.headers['if-modified-since']; +}; + +/** + * Strip content-* header fields. + * + * @api private + */ + +SendStream.prototype.removeContentHeaderFields = function(){ + var res = this.res; + Object.keys(res._headers).forEach(function(field){ + if (0 == field.indexOf('content')) { + res.removeHeader(field); + } + }); +}; + +/** + * Respond with 304 not modified. + * + * @api private + */ + +SendStream.prototype.notModified = function(){ + var res = this.res; + debug('not modified'); + this.removeContentHeaderFields(); + res.statusCode = 304; + res.end(); +}; + +/** + * Check if the request is cacheable, aka + * responded with 2xx or 304 (see RFC 2616 section 14.2{5,6}). + * + * @return {Boolean} + * @api private + */ + +SendStream.prototype.isCachable = function(){ + var res = this.res; + return (res.statusCode >= 200 && res.statusCode < 300) || 304 == res.statusCode; +}; + +/** + * Handle stat() error. + * + * @param {Error} err + * @api private + */ + +SendStream.prototype.onStatError = function(err){ + var notfound = ['ENOENT', 'ENAMETOOLONG', 'ENOTDIR']; + if (~notfound.indexOf(err.code)) return this.error(404, err); + this.error(500, err); +}; + +/** + * Check if the cache is fresh. + * + * @return {Boolean} + * @api private + */ + +SendStream.prototype.isFresh = function(){ + return fresh(this.req.headers, this.res._headers); +}; + +/** + * Redirect to `path`. + * + * @param {String} path + * @api private + */ + +SendStream.prototype.redirect = function(path){ + if (this.listeners('directory').length) return this.emit('directory'); + var res = this.res; + path += '/'; + res.statusCode = 301; + res.setHeader('Location', path); + res.end('Redirecting to ' + utils.escape(path)); +}; + +/** + * Pipe to `res. + * + * @param {Stream} res + * @return {Stream} res + * @api public + */ + +SendStream.prototype.pipe = function(res){ + var self = this + , args = arguments + , path = this.path + , root = this._root; + + // references + this.res = res; + + // invalid request uri + path = utils.decode(path); + if (-1 == path) return this.error(400); + + // null byte(s) + if (~path.indexOf('\0')) return this.error(400); + + // join / normalize from optional root dir + if (root) path = normalize(join(this._root, path)); + + // ".." is malicious without "root" + if (this.isMalicious()) return this.error(403); + + // malicious path + if (root && 0 != path.indexOf(root)) return this.error(403); + + // hidden file support + if (!this._hidden && this.hasLeadingDot()) return this.error(404); + + // index file support + if (this._index && this.hasTrailingSlash()) path += this._index; + + debug('stat "%s"', path); + fs.stat(path, function(err, stat){ + if (err) return self.onStatError(err); + if (stat.isDirectory()) return self.redirect(self.path); + self.emit('file', path, stat); + self.send(path, stat); + }); + + return res; +}; + +/** + * Transfer `path`. + * + * @param {String} path + * @api public + */ + +SendStream.prototype.send = function(path, stat){ + var options = this.options; + var len = stat.size; + var res = this.res; + var req = this.req; + var ranges = req.headers.range; + var offset = options.start || 0; + + // set header fields + this.setHeader(stat); + + // set content-type + this.type(path); + + // conditional GET support + if (this.isConditionalGET() + && this.isCachable() + && this.isFresh()) { + return this.notModified(); + } + + // adjust len to start/end options + len = Math.max(0, len - offset); + if (options.end !== undefined) { + var bytes = options.end - offset + 1; + if (len > bytes) len = bytes; + } + + // Range support + if (ranges) { + ranges = parseRange(len, ranges); + + // unsatisfiable + if (-1 == ranges) { + res.setHeader('Content-Range', 'bytes */' + stat.size); + return this.error(416); + } + + // valid (syntactically invalid ranges are treated as a regular response) + if (-2 != ranges) { + options.start = offset + ranges[0].start; + options.end = offset + ranges[0].end; + + // Content-Range + res.statusCode = 206; + res.setHeader('Content-Range', 'bytes ' + + ranges[0].start + + '-' + + ranges[0].end + + '/' + + len); + len = options.end - options.start + 1; + } + } + + // content-length + res.setHeader('Content-Length', len); + + // HEAD support + if ('HEAD' == req.method) return res.end(); + + this.stream(path, options); +}; + +/** + * Stream `path` to the response. + * + * @param {String} path + * @param {Object} options + * @api private + */ + +SendStream.prototype.stream = function(path, options){ + // TODO: this is all lame, refactor meeee + var self = this; + var res = this.res; + var req = this.req; + + // pipe + var stream = fs.createReadStream(path, options); + this.emit('stream', stream); + stream.pipe(res); + + // socket closed, done with the fd + req.on('close', stream.destroy.bind(stream)); + + // error handling code-smell + stream.on('error', function(err){ + // no hope in responding + if (res._header) { + console.error(err.stack); + req.destroy(); + return; + } + + // 500 + err.status = 500; + self.emit('error', err); + }); + + // end + stream.on('end', function(){ + self.emit('end'); + }); +}; + +/** + * Set content-type based on `path` + * if it hasn't been explicitly set. + * + * @param {String} path + * @api private + */ + +SendStream.prototype.type = function(path){ + var res = this.res; + if (res.getHeader('Content-Type')) return; + var type = mime.lookup(path); + var charset = mime.charsets.lookup(type); + debug('content-type %s', type); + res.setHeader('Content-Type', type + (charset ? '; charset=' + charset : '')); +}; + +/** + * Set reaponse header fields, most + * fields may be pre-defined. + * + * @param {Object} stat + * @api private + */ + +SendStream.prototype.setHeader = function(stat){ + var res = this.res; + if (!res.getHeader('Accept-Ranges')) res.setHeader('Accept-Ranges', 'bytes'); + if (!res.getHeader('ETag')) res.setHeader('ETag', utils.etag(stat)); + if (!res.getHeader('Date')) res.setHeader('Date', new Date().toUTCString()); + if (!res.getHeader('Cache-Control')) res.setHeader('Cache-Control', 'public, max-age=' + (this._maxage / 1000)); + if (!res.getHeader('Last-Modified')) res.setHeader('Last-Modified', stat.mtime.toUTCString()); +}; diff --git a/server/node_modules/express/node_modules/send/lib/utils.js b/server/node_modules/express/node_modules/send/lib/utils.js new file mode 100644 index 0000000..950e5a2 --- /dev/null +++ b/server/node_modules/express/node_modules/send/lib/utils.js @@ -0,0 +1,47 @@ + +/** + * Return an ETag in the form of `"-"` + * from the given `stat`. + * + * @param {Object} stat + * @return {String} + * @api private + */ + +exports.etag = function(stat) { + return '"' + stat.size + '-' + Number(stat.mtime) + '"'; +}; + +/** + * decodeURIComponent. + * + * Allows V8 to only deoptimize this fn instead of all + * of send(). + * + * @param {String} path + * @api private + */ + +exports.decode = function(path){ + try { + return decodeURIComponent(path); + } catch (err) { + return -1; + } +}; + +/** + * Escape the given string of `html`. + * + * @param {String} html + * @return {String} + * @api private + */ + +exports.escape = function(html){ + return String(html) + .replace(/&(?!\w+;)/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +}; \ No newline at end of file diff --git a/server/node_modules/express/node_modules/send/node_modules/mime/LICENSE b/server/node_modules/express/node_modules/send/node_modules/mime/LICENSE new file mode 100644 index 0000000..451fc45 --- /dev/null +++ b/server/node_modules/express/node_modules/send/node_modules/mime/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2010 Benjamin Thomas, Robert Kieffer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/server/node_modules/express/node_modules/send/node_modules/mime/README.md b/server/node_modules/express/node_modules/send/node_modules/mime/README.md new file mode 100644 index 0000000..6ca19bd --- /dev/null +++ b/server/node_modules/express/node_modules/send/node_modules/mime/README.md @@ -0,0 +1,66 @@ +# mime + +Comprehensive MIME type mapping API. Includes all 600+ types and 800+ extensions defined by the Apache project, plus additional types submitted by the node.js community. + +## Install + +Install with [npm](http://github.com/isaacs/npm): + + npm install mime + +## API - Queries + +### mime.lookup(path) +Get the mime type associated with a file, if no mime type is found `application/octet-stream` is returned. Performs a case-insensitive lookup using the extension in `path` (the substring after the last '/' or '.'). E.g. + + var mime = require('mime'); + + mime.lookup('/path/to/file.txt'); // => 'text/plain' + mime.lookup('file.txt'); // => 'text/plain' + mime.lookup('.TXT'); // => 'text/plain' + mime.lookup('htm'); // => 'text/html' + +### mime.default_type +Sets the mime type returned when `mime.lookup` fails to find the extension searched for. (Default is `application/octet-stream`.) + +### mime.extension(type) +Get the default extension for `type` + + mime.extension('text/html'); // => 'html' + mime.extension('application/octet-stream'); // => 'bin' + +### mime.charsets.lookup() + +Map mime-type to charset + + mime.charsets.lookup('text/plain'); // => 'UTF-8' + +(The logic for charset lookups is pretty rudimentary. Feel free to suggest improvements.) + +## API - Defining Custom Types + +The following APIs allow you to add your own type mappings within your project. If you feel a type should be included as part of node-mime, see [requesting new types](https://github.com/broofa/node-mime/wiki/Requesting-New-Types). + +### mime.define() + +Add custom mime/extension mappings + + mime.define({ + 'text/x-some-format': ['x-sf', 'x-sft', 'x-sfml'], + 'application/x-my-type': ['x-mt', 'x-mtt'], + // etc ... + }); + + mime.lookup('x-sft'); // => 'text/x-some-format' + +The first entry in the extensions array is returned by `mime.extension()`. E.g. + + mime.extension('text/x-some-format'); // => 'x-sf' + +### mime.load(filepath) + +Load mappings from an Apache ".types" format file + + mime.load('./my_project.types'); + +The .types file format is simple - See the `types` dir for examples. diff --git a/server/node_modules/express/node_modules/send/node_modules/mime/mime.js b/server/node_modules/express/node_modules/send/node_modules/mime/mime.js new file mode 100644 index 0000000..48be0c5 --- /dev/null +++ b/server/node_modules/express/node_modules/send/node_modules/mime/mime.js @@ -0,0 +1,114 @@ +var path = require('path'); +var fs = require('fs'); + +function Mime() { + // Map of extension -> mime type + this.types = Object.create(null); + + // Map of mime type -> extension + this.extensions = Object.create(null); +} + +/** + * Define mimetype -> extension mappings. Each key is a mime-type that maps + * to an array of extensions associated with the type. The first extension is + * used as the default extension for the type. + * + * e.g. mime.define({'audio/ogg', ['oga', 'ogg', 'spx']}); + * + * @param map (Object) type definitions + */ +Mime.prototype.define = function (map) { + for (var type in map) { + var exts = map[type]; + + for (var i = 0; i < exts.length; i++) { + if (process.env.DEBUG_MIME && this.types[exts]) { + console.warn(this._loading.replace(/.*\//, ''), 'changes "' + exts[i] + '" extension type from ' + + this.types[exts] + ' to ' + type); + } + + this.types[exts[i]] = type; + } + + // Default extension is the first one we encounter + if (!this.extensions[type]) { + this.extensions[type] = exts[0]; + } + } +}; + +/** + * Load an Apache2-style ".types" file + * + * This may be called multiple times (it's expected). Where files declare + * overlapping types/extensions, the last file wins. + * + * @param file (String) path of file to load. + */ +Mime.prototype.load = function(file) { + + this._loading = file; + // Read file and split into lines + var map = {}, + content = fs.readFileSync(file, 'ascii'), + lines = content.split(/[\r\n]+/); + + lines.forEach(function(line) { + // Clean up whitespace/comments, and split into fields + var fields = line.replace(/\s*#.*|^\s*|\s*$/g, '').split(/\s+/); + map[fields.shift()] = fields; + }); + + this.define(map); + + this._loading = null; +}; + +/** + * Lookup a mime type based on extension + */ +Mime.prototype.lookup = function(path, fallback) { + var ext = path.replace(/.*[\.\/\\]/, '').toLowerCase(); + + return this.types[ext] || fallback || this.default_type; +}; + +/** + * Return file extension associated with a mime type + */ +Mime.prototype.extension = function(mimeType) { + var type = mimeType.match(/^\s*([^;\s]*)(?:;|\s|$)/)[1].toLowerCase(); + return this.extensions[type]; +}; + +// Default instance +var mime = new Mime(); + +// Load local copy of +// http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types +mime.load(path.join(__dirname, 'types/mime.types')); + +// Load additional types from node.js community +mime.load(path.join(__dirname, 'types/node.types')); + +// Default type +mime.default_type = mime.lookup('bin'); + +// +// Additional API specific to the default instance +// + +mime.Mime = Mime; + +/** + * Lookup a charset based on mime type. + */ +mime.charsets = { + lookup: function(mimeType, fallback) { + // Assume text types are utf8 + return (/^text\//).test(mimeType) ? 'UTF-8' : fallback; + } +}; + +module.exports = mime; diff --git a/server/node_modules/express/node_modules/send/node_modules/mime/package.json b/server/node_modules/express/node_modules/send/node_modules/mime/package.json new file mode 100644 index 0000000..a975939 --- /dev/null +++ b/server/node_modules/express/node_modules/send/node_modules/mime/package.json @@ -0,0 +1,36 @@ +{ + "author": { + "name": "Robert Kieffer", + "email": "robert@broofa.com", + "url": "http://github.com/broofa" + }, + "contributors": [ + { + "name": "Benjamin Thomas", + "email": "benjamin@benjaminthomas.org", + "url": "http://github.com/bentomas" + } + ], + "dependencies": {}, + "description": "A comprehensive library for mime-type mapping", + "devDependencies": {}, + "keywords": [ + "util", + "mime" + ], + "main": "mime.js", + "name": "mime", + "repository": { + "url": "https://github.com/broofa/node-mime", + "type": "git" + }, + "version": "1.2.11", + "readme": "# mime\n\nComprehensive MIME type mapping API. Includes all 600+ types and 800+ extensions defined by the Apache project, plus additional types submitted by the node.js community.\n\n## Install\n\nInstall with [npm](http://github.com/isaacs/npm):\n\n npm install mime\n\n## API - Queries\n\n### mime.lookup(path)\nGet the mime type associated with a file, if no mime type is found `application/octet-stream` is returned. Performs a case-insensitive lookup using the extension in `path` (the substring after the last '/' or '.'). E.g.\n\n var mime = require('mime');\n\n mime.lookup('/path/to/file.txt'); // => 'text/plain'\n mime.lookup('file.txt'); // => 'text/plain'\n mime.lookup('.TXT'); // => 'text/plain'\n mime.lookup('htm'); // => 'text/html'\n\n### mime.default_type\nSets the mime type returned when `mime.lookup` fails to find the extension searched for. (Default is `application/octet-stream`.)\n\n### mime.extension(type)\nGet the default extension for `type`\n\n mime.extension('text/html'); // => 'html'\n mime.extension('application/octet-stream'); // => 'bin'\n\n### mime.charsets.lookup()\n\nMap mime-type to charset\n\n mime.charsets.lookup('text/plain'); // => 'UTF-8'\n\n(The logic for charset lookups is pretty rudimentary. Feel free to suggest improvements.)\n\n## API - Defining Custom Types\n\nThe following APIs allow you to add your own type mappings within your project. If you feel a type should be included as part of node-mime, see [requesting new types](https://github.com/broofa/node-mime/wiki/Requesting-New-Types).\n\n### mime.define()\n\nAdd custom mime/extension mappings\n\n mime.define({\n 'text/x-some-format': ['x-sf', 'x-sft', 'x-sfml'],\n 'application/x-my-type': ['x-mt', 'x-mtt'],\n // etc ...\n });\n\n mime.lookup('x-sft'); // => 'text/x-some-format'\n\nThe first entry in the extensions array is returned by `mime.extension()`. E.g.\n\n mime.extension('text/x-some-format'); // => 'x-sf'\n\n### mime.load(filepath)\n\nLoad mappings from an Apache \".types\" format file\n\n mime.load('./my_project.types');\n\nThe .types file format is simple - See the `types` dir for examples.\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/broofa/node-mime/issues" + }, + "homepage": "https://github.com/broofa/node-mime", + "_id": "mime@1.2.11", + "_from": "mime@~1.2.9" +} diff --git a/server/node_modules/express/node_modules/send/node_modules/mime/test.js b/server/node_modules/express/node_modules/send/node_modules/mime/test.js new file mode 100644 index 0000000..2cda1c7 --- /dev/null +++ b/server/node_modules/express/node_modules/send/node_modules/mime/test.js @@ -0,0 +1,84 @@ +/** + * Usage: node test.js + */ + +var mime = require('./mime'); +var assert = require('assert'); +var path = require('path'); + +function eq(a, b) { + console.log('Test: ' + a + ' === ' + b); + assert.strictEqual.apply(null, arguments); +} + +console.log(Object.keys(mime.extensions).length + ' types'); +console.log(Object.keys(mime.types).length + ' extensions\n'); + +// +// Test mime lookups +// + +eq('text/plain', mime.lookup('text.txt')); // normal file +eq('text/plain', mime.lookup('TEXT.TXT')); // uppercase +eq('text/plain', mime.lookup('dir/text.txt')); // dir + file +eq('text/plain', mime.lookup('.text.txt')); // hidden file +eq('text/plain', mime.lookup('.txt')); // nameless +eq('text/plain', mime.lookup('txt')); // extension-only +eq('text/plain', mime.lookup('/txt')); // extension-less () +eq('text/plain', mime.lookup('\\txt')); // Windows, extension-less +eq('application/octet-stream', mime.lookup('text.nope')); // unrecognized +eq('fallback', mime.lookup('text.fallback', 'fallback')); // alternate default + +// +// Test extensions +// + +eq('txt', mime.extension(mime.types.text)); +eq('html', mime.extension(mime.types.htm)); +eq('bin', mime.extension('application/octet-stream')); +eq('bin', mime.extension('application/octet-stream ')); +eq('html', mime.extension(' text/html; charset=UTF-8')); +eq('html', mime.extension('text/html; charset=UTF-8 ')); +eq('html', mime.extension('text/html; charset=UTF-8')); +eq('html', mime.extension('text/html ; charset=UTF-8')); +eq('html', mime.extension('text/html;charset=UTF-8')); +eq('html', mime.extension('text/Html;charset=UTF-8')); +eq(undefined, mime.extension('unrecognized')); + +// +// Test node.types lookups +// + +eq('application/font-woff', mime.lookup('file.woff')); +eq('application/octet-stream', mime.lookup('file.buffer')); +eq('audio/mp4', mime.lookup('file.m4a')); +eq('font/opentype', mime.lookup('file.otf')); + +// +// Test charsets +// + +eq('UTF-8', mime.charsets.lookup('text/plain')); +eq(undefined, mime.charsets.lookup(mime.types.js)); +eq('fallback', mime.charsets.lookup('application/octet-stream', 'fallback')); + +// +// Test for overlaps between mime.types and node.types +// + +var apacheTypes = new mime.Mime(), nodeTypes = new mime.Mime(); +apacheTypes.load(path.join(__dirname, 'types/mime.types')); +nodeTypes.load(path.join(__dirname, 'types/node.types')); + +var keys = [].concat(Object.keys(apacheTypes.types)) + .concat(Object.keys(nodeTypes.types)); +keys.sort(); +for (var i = 1; i < keys.length; i++) { + if (keys[i] == keys[i-1]) { + console.warn('Warning: ' + + 'node.types defines ' + keys[i] + '->' + nodeTypes.types[keys[i]] + + ', mime.types defines ' + keys[i] + '->' + apacheTypes.types[keys[i]]); + } +} + +console.log('\nOK'); diff --git a/server/node_modules/express/node_modules/send/node_modules/mime/types/mime.types b/server/node_modules/express/node_modules/send/node_modules/mime/types/mime.types new file mode 100644 index 0000000..da8cd69 --- /dev/null +++ b/server/node_modules/express/node_modules/send/node_modules/mime/types/mime.types @@ -0,0 +1,1588 @@ +# This file maps Internet media types to unique file extension(s). +# Although created for httpd, this file is used by many software systems +# and has been placed in the public domain for unlimited redisribution. +# +# The table below contains both registered and (common) unregistered types. +# A type that has no unique extension can be ignored -- they are listed +# here to guide configurations toward known types and to make it easier to +# identify "new" types. File extensions are also commonly used to indicate +# content languages and encodings, so choose them carefully. +# +# Internet media types should be registered as described in RFC 4288. +# The registry is at . +# +# MIME type (lowercased) Extensions +# ============================================ ========== +# application/1d-interleaved-parityfec +# application/3gpp-ims+xml +# application/activemessage +application/andrew-inset ez +# application/applefile +application/applixware aw +application/atom+xml atom +application/atomcat+xml atomcat +# application/atomicmail +application/atomsvc+xml atomsvc +# application/auth-policy+xml +# application/batch-smtp +# application/beep+xml +# application/calendar+xml +# application/cals-1840 +# application/ccmp+xml +application/ccxml+xml ccxml +application/cdmi-capability cdmia +application/cdmi-container cdmic +application/cdmi-domain cdmid +application/cdmi-object cdmio +application/cdmi-queue cdmiq +# application/cea-2018+xml +# application/cellml+xml +# application/cfw +# application/cnrp+xml +# application/commonground +# application/conference-info+xml +# application/cpl+xml +# application/csta+xml +# application/cstadata+xml +application/cu-seeme cu +# application/cybercash +application/davmount+xml davmount +# application/dca-rft +# application/dec-dx +# application/dialog-info+xml +# application/dicom +# application/dns +application/docbook+xml dbk +# application/dskpp+xml +application/dssc+der dssc +application/dssc+xml xdssc +# application/dvcs +application/ecmascript ecma +# application/edi-consent +# application/edi-x12 +# application/edifact +application/emma+xml emma +# application/epp+xml +application/epub+zip epub +# application/eshop +# application/example +application/exi exi +# application/fastinfoset +# application/fastsoap +# application/fits +application/font-tdpfr pfr +# application/framework-attributes+xml +application/gml+xml gml +application/gpx+xml gpx +application/gxf gxf +# application/h224 +# application/held+xml +# application/http +application/hyperstudio stk +# application/ibe-key-request+xml +# application/ibe-pkg-reply+xml +# application/ibe-pp-data +# application/iges +# application/im-iscomposing+xml +# application/index +# application/index.cmd +# application/index.obj +# application/index.response +# application/index.vnd +application/inkml+xml ink inkml +# application/iotp +application/ipfix ipfix +# application/ipp +# application/isup +application/java-archive jar +application/java-serialized-object ser +application/java-vm class +application/javascript js +application/json json +application/jsonml+json jsonml +# application/kpml-request+xml +# application/kpml-response+xml +application/lost+xml lostxml +application/mac-binhex40 hqx +application/mac-compactpro cpt +# application/macwriteii +application/mads+xml mads +application/marc mrc +application/marcxml+xml mrcx +application/mathematica ma nb mb +# application/mathml-content+xml +# application/mathml-presentation+xml +application/mathml+xml mathml +# application/mbms-associated-procedure-description+xml +# application/mbms-deregister+xml +# application/mbms-envelope+xml +# application/mbms-msk+xml +# application/mbms-msk-response+xml +# application/mbms-protection-description+xml +# application/mbms-reception-report+xml +# application/mbms-register+xml +# application/mbms-register-response+xml +# application/mbms-user-service-description+xml +application/mbox mbox +# application/media_control+xml +application/mediaservercontrol+xml mscml +application/metalink+xml metalink +application/metalink4+xml meta4 +application/mets+xml mets +# application/mikey +application/mods+xml mods +# application/moss-keys +# application/moss-signature +# application/mosskey-data +# application/mosskey-request +application/mp21 m21 mp21 +application/mp4 mp4s +# application/mpeg4-generic +# application/mpeg4-iod +# application/mpeg4-iod-xmt +# application/msc-ivr+xml +# application/msc-mixer+xml +application/msword doc dot +application/mxf mxf +# application/nasdata +# application/news-checkgroups +# application/news-groupinfo +# application/news-transmission +# application/nss +# application/ocsp-request +# application/ocsp-response +application/octet-stream bin dms lrf mar so dist distz pkg bpk dump elc deploy +application/oda oda +application/oebps-package+xml opf +application/ogg ogx +application/omdoc+xml omdoc +application/onenote onetoc onetoc2 onetmp onepkg +application/oxps oxps +# application/parityfec +application/patch-ops-error+xml xer +application/pdf pdf +application/pgp-encrypted pgp +# application/pgp-keys +application/pgp-signature asc sig +application/pics-rules prf +# application/pidf+xml +# application/pidf-diff+xml +application/pkcs10 p10 +application/pkcs7-mime p7m p7c +application/pkcs7-signature p7s +application/pkcs8 p8 +application/pkix-attr-cert ac +application/pkix-cert cer +application/pkix-crl crl +application/pkix-pkipath pkipath +application/pkixcmp pki +application/pls+xml pls +# application/poc-settings+xml +application/postscript ai eps ps +# application/prs.alvestrand.titrax-sheet +application/prs.cww cww +# application/prs.nprend +# application/prs.plucker +# application/prs.rdf-xml-crypt +# application/prs.xsf+xml +application/pskc+xml pskcxml +# application/qsig +application/rdf+xml rdf +application/reginfo+xml rif +application/relax-ng-compact-syntax rnc +# application/remote-printing +application/resource-lists+xml rl +application/resource-lists-diff+xml rld +# application/riscos +# application/rlmi+xml +application/rls-services+xml rs +application/rpki-ghostbusters gbr +application/rpki-manifest mft +application/rpki-roa roa +# application/rpki-updown +application/rsd+xml rsd +application/rss+xml rss +application/rtf rtf +# application/rtx +# application/samlassertion+xml +# application/samlmetadata+xml +application/sbml+xml sbml +application/scvp-cv-request scq +application/scvp-cv-response scs +application/scvp-vp-request spq +application/scvp-vp-response spp +application/sdp sdp +# application/set-payment +application/set-payment-initiation setpay +# application/set-registration +application/set-registration-initiation setreg +# application/sgml +# application/sgml-open-catalog +application/shf+xml shf +# application/sieve +# application/simple-filter+xml +# application/simple-message-summary +# application/simplesymbolcontainer +# application/slate +# application/smil +application/smil+xml smi smil +# application/soap+fastinfoset +# application/soap+xml +application/sparql-query rq +application/sparql-results+xml srx +# application/spirits-event+xml +application/srgs gram +application/srgs+xml grxml +application/sru+xml sru +application/ssdl+xml ssdl +application/ssml+xml ssml +# application/tamp-apex-update +# application/tamp-apex-update-confirm +# application/tamp-community-update +# application/tamp-community-update-confirm +# application/tamp-error +# application/tamp-sequence-adjust +# application/tamp-sequence-adjust-confirm +# application/tamp-status-query +# application/tamp-status-response +# application/tamp-update +# application/tamp-update-confirm +application/tei+xml tei teicorpus +application/thraud+xml tfi +# application/timestamp-query +# application/timestamp-reply +application/timestamped-data tsd +# application/tve-trigger +# application/ulpfec +# application/vcard+xml +# application/vemmi +# application/vividence.scriptfile +# application/vnd.3gpp.bsf+xml +application/vnd.3gpp.pic-bw-large plb +application/vnd.3gpp.pic-bw-small psb +application/vnd.3gpp.pic-bw-var pvb +# application/vnd.3gpp.sms +# application/vnd.3gpp2.bcmcsinfo+xml +# application/vnd.3gpp2.sms +application/vnd.3gpp2.tcap tcap +application/vnd.3m.post-it-notes pwn +application/vnd.accpac.simply.aso aso +application/vnd.accpac.simply.imp imp +application/vnd.acucobol acu +application/vnd.acucorp atc acutc +application/vnd.adobe.air-application-installer-package+zip air +application/vnd.adobe.formscentral.fcdt fcdt +application/vnd.adobe.fxp fxp fxpl +# application/vnd.adobe.partial-upload +application/vnd.adobe.xdp+xml xdp +application/vnd.adobe.xfdf xfdf +# application/vnd.aether.imp +# application/vnd.ah-barcode +application/vnd.ahead.space ahead +application/vnd.airzip.filesecure.azf azf +application/vnd.airzip.filesecure.azs azs +application/vnd.amazon.ebook azw +application/vnd.americandynamics.acc acc +application/vnd.amiga.ami ami +# application/vnd.amundsen.maze+xml +application/vnd.android.package-archive apk +application/vnd.anser-web-certificate-issue-initiation cii +application/vnd.anser-web-funds-transfer-initiation fti +application/vnd.antix.game-component atx +application/vnd.apple.installer+xml mpkg +application/vnd.apple.mpegurl m3u8 +# application/vnd.arastra.swi +application/vnd.aristanetworks.swi swi +application/vnd.astraea-software.iota iota +application/vnd.audiograph aep +# application/vnd.autopackage +# application/vnd.avistar+xml +application/vnd.blueice.multipass mpm +# application/vnd.bluetooth.ep.oob +application/vnd.bmi bmi +application/vnd.businessobjects rep +# application/vnd.cab-jscript +# application/vnd.canon-cpdl +# application/vnd.canon-lips +# application/vnd.cendio.thinlinc.clientconf +application/vnd.chemdraw+xml cdxml +application/vnd.chipnuts.karaoke-mmd mmd +application/vnd.cinderella cdy +# application/vnd.cirpack.isdn-ext +application/vnd.claymore cla +application/vnd.cloanto.rp9 rp9 +application/vnd.clonk.c4group c4g c4d c4f c4p c4u +application/vnd.cluetrust.cartomobile-config c11amc +application/vnd.cluetrust.cartomobile-config-pkg c11amz +# application/vnd.collection+json +# application/vnd.commerce-battelle +application/vnd.commonspace csp +application/vnd.contact.cmsg cdbcmsg +application/vnd.cosmocaller cmc +application/vnd.crick.clicker clkx +application/vnd.crick.clicker.keyboard clkk +application/vnd.crick.clicker.palette clkp +application/vnd.crick.clicker.template clkt +application/vnd.crick.clicker.wordbank clkw +application/vnd.criticaltools.wbs+xml wbs +application/vnd.ctc-posml pml +# application/vnd.ctct.ws+xml +# application/vnd.cups-pdf +# application/vnd.cups-postscript +application/vnd.cups-ppd ppd +# application/vnd.cups-raster +# application/vnd.cups-raw +# application/vnd.curl +application/vnd.curl.car car +application/vnd.curl.pcurl pcurl +# application/vnd.cybank +application/vnd.dart dart +application/vnd.data-vision.rdz rdz +application/vnd.dece.data uvf uvvf uvd uvvd +application/vnd.dece.ttml+xml uvt uvvt +application/vnd.dece.unspecified uvx uvvx +application/vnd.dece.zip uvz uvvz +application/vnd.denovo.fcselayout-link fe_launch +# application/vnd.dir-bi.plate-dl-nosuffix +application/vnd.dna dna +application/vnd.dolby.mlp mlp +# application/vnd.dolby.mobile.1 +# application/vnd.dolby.mobile.2 +application/vnd.dpgraph dpg +application/vnd.dreamfactory dfac +application/vnd.ds-keypoint kpxx +application/vnd.dvb.ait ait +# application/vnd.dvb.dvbj +# application/vnd.dvb.esgcontainer +# application/vnd.dvb.ipdcdftnotifaccess +# application/vnd.dvb.ipdcesgaccess +# application/vnd.dvb.ipdcesgaccess2 +# application/vnd.dvb.ipdcesgpdd +# application/vnd.dvb.ipdcroaming +# application/vnd.dvb.iptv.alfec-base +# application/vnd.dvb.iptv.alfec-enhancement +# application/vnd.dvb.notif-aggregate-root+xml +# application/vnd.dvb.notif-container+xml +# application/vnd.dvb.notif-generic+xml +# application/vnd.dvb.notif-ia-msglist+xml +# application/vnd.dvb.notif-ia-registration-request+xml +# application/vnd.dvb.notif-ia-registration-response+xml +# application/vnd.dvb.notif-init+xml +# application/vnd.dvb.pfr +application/vnd.dvb.service svc +# application/vnd.dxr +application/vnd.dynageo geo +# application/vnd.easykaraoke.cdgdownload +# application/vnd.ecdis-update +application/vnd.ecowin.chart mag +# application/vnd.ecowin.filerequest +# application/vnd.ecowin.fileupdate +# application/vnd.ecowin.series +# application/vnd.ecowin.seriesrequest +# application/vnd.ecowin.seriesupdate +# application/vnd.emclient.accessrequest+xml +application/vnd.enliven nml +# application/vnd.eprints.data+xml +application/vnd.epson.esf esf +application/vnd.epson.msf msf +application/vnd.epson.quickanime qam +application/vnd.epson.salt slt +application/vnd.epson.ssf ssf +# application/vnd.ericsson.quickcall +application/vnd.eszigno3+xml es3 et3 +# application/vnd.etsi.aoc+xml +# application/vnd.etsi.cug+xml +# application/vnd.etsi.iptvcommand+xml +# application/vnd.etsi.iptvdiscovery+xml +# application/vnd.etsi.iptvprofile+xml +# application/vnd.etsi.iptvsad-bc+xml +# application/vnd.etsi.iptvsad-cod+xml +# application/vnd.etsi.iptvsad-npvr+xml +# application/vnd.etsi.iptvservice+xml +# application/vnd.etsi.iptvsync+xml +# application/vnd.etsi.iptvueprofile+xml +# application/vnd.etsi.mcid+xml +# application/vnd.etsi.overload-control-policy-dataset+xml +# application/vnd.etsi.sci+xml +# application/vnd.etsi.simservs+xml +# application/vnd.etsi.tsl+xml +# application/vnd.etsi.tsl.der +# application/vnd.eudora.data +application/vnd.ezpix-album ez2 +application/vnd.ezpix-package ez3 +# application/vnd.f-secure.mobile +application/vnd.fdf fdf +application/vnd.fdsn.mseed mseed +application/vnd.fdsn.seed seed dataless +# application/vnd.ffsns +# application/vnd.fints +application/vnd.flographit gph +application/vnd.fluxtime.clip ftc +# application/vnd.font-fontforge-sfd +application/vnd.framemaker fm frame maker book +application/vnd.frogans.fnc fnc +application/vnd.frogans.ltf ltf +application/vnd.fsc.weblaunch fsc +application/vnd.fujitsu.oasys oas +application/vnd.fujitsu.oasys2 oa2 +application/vnd.fujitsu.oasys3 oa3 +application/vnd.fujitsu.oasysgp fg5 +application/vnd.fujitsu.oasysprs bh2 +# application/vnd.fujixerox.art-ex +# application/vnd.fujixerox.art4 +# application/vnd.fujixerox.hbpl +application/vnd.fujixerox.ddd ddd +application/vnd.fujixerox.docuworks xdw +application/vnd.fujixerox.docuworks.binder xbd +# application/vnd.fut-misnet +application/vnd.fuzzysheet fzs +application/vnd.genomatix.tuxedo txd +# application/vnd.geocube+xml +application/vnd.geogebra.file ggb +application/vnd.geogebra.tool ggt +application/vnd.geometry-explorer gex gre +application/vnd.geonext gxt +application/vnd.geoplan g2w +application/vnd.geospace g3w +# application/vnd.globalplatform.card-content-mgt +# application/vnd.globalplatform.card-content-mgt-response +application/vnd.gmx gmx +application/vnd.google-earth.kml+xml kml +application/vnd.google-earth.kmz kmz +application/vnd.grafeq gqf gqs +# application/vnd.gridmp +application/vnd.groove-account gac +application/vnd.groove-help ghf +application/vnd.groove-identity-message gim +application/vnd.groove-injector grv +application/vnd.groove-tool-message gtm +application/vnd.groove-tool-template tpl +application/vnd.groove-vcard vcg +# application/vnd.hal+json +application/vnd.hal+xml hal +application/vnd.handheld-entertainment+xml zmm +application/vnd.hbci hbci +# application/vnd.hcl-bireports +application/vnd.hhe.lesson-player les +application/vnd.hp-hpgl hpgl +application/vnd.hp-hpid hpid +application/vnd.hp-hps hps +application/vnd.hp-jlyt jlt +application/vnd.hp-pcl pcl +application/vnd.hp-pclxl pclxl +# application/vnd.httphone +application/vnd.hydrostatix.sof-data sfd-hdstx +# application/vnd.hzn-3d-crossword +# application/vnd.ibm.afplinedata +# application/vnd.ibm.electronic-media +application/vnd.ibm.minipay mpy +application/vnd.ibm.modcap afp listafp list3820 +application/vnd.ibm.rights-management irm +application/vnd.ibm.secure-container sc +application/vnd.iccprofile icc icm +application/vnd.igloader igl +application/vnd.immervision-ivp ivp +application/vnd.immervision-ivu ivu +# application/vnd.informedcontrol.rms+xml +# application/vnd.informix-visionary +# application/vnd.infotech.project +# application/vnd.infotech.project+xml +# application/vnd.innopath.wamp.notification +application/vnd.insors.igm igm +application/vnd.intercon.formnet xpw xpx +application/vnd.intergeo i2g +# application/vnd.intertrust.digibox +# application/vnd.intertrust.nncp +application/vnd.intu.qbo qbo +application/vnd.intu.qfx qfx +# application/vnd.iptc.g2.conceptitem+xml +# application/vnd.iptc.g2.knowledgeitem+xml +# application/vnd.iptc.g2.newsitem+xml +# application/vnd.iptc.g2.newsmessage+xml +# application/vnd.iptc.g2.packageitem+xml +# application/vnd.iptc.g2.planningitem+xml +application/vnd.ipunplugged.rcprofile rcprofile +application/vnd.irepository.package+xml irp +application/vnd.is-xpr xpr +application/vnd.isac.fcs fcs +application/vnd.jam jam +# application/vnd.japannet-directory-service +# application/vnd.japannet-jpnstore-wakeup +# application/vnd.japannet-payment-wakeup +# application/vnd.japannet-registration +# application/vnd.japannet-registration-wakeup +# application/vnd.japannet-setstore-wakeup +# application/vnd.japannet-verification +# application/vnd.japannet-verification-wakeup +application/vnd.jcp.javame.midlet-rms rms +application/vnd.jisp jisp +application/vnd.joost.joda-archive joda +application/vnd.kahootz ktz ktr +application/vnd.kde.karbon karbon +application/vnd.kde.kchart chrt +application/vnd.kde.kformula kfo +application/vnd.kde.kivio flw +application/vnd.kde.kontour kon +application/vnd.kde.kpresenter kpr kpt +application/vnd.kde.kspread ksp +application/vnd.kde.kword kwd kwt +application/vnd.kenameaapp htke +application/vnd.kidspiration kia +application/vnd.kinar kne knp +application/vnd.koan skp skd skt skm +application/vnd.kodak-descriptor sse +application/vnd.las.las+xml lasxml +# application/vnd.liberty-request+xml +application/vnd.llamagraphics.life-balance.desktop lbd +application/vnd.llamagraphics.life-balance.exchange+xml lbe +application/vnd.lotus-1-2-3 123 +application/vnd.lotus-approach apr +application/vnd.lotus-freelance pre +application/vnd.lotus-notes nsf +application/vnd.lotus-organizer org +application/vnd.lotus-screencam scm +application/vnd.lotus-wordpro lwp +application/vnd.macports.portpkg portpkg +# application/vnd.marlin.drm.actiontoken+xml +# application/vnd.marlin.drm.conftoken+xml +# application/vnd.marlin.drm.license+xml +# application/vnd.marlin.drm.mdcf +application/vnd.mcd mcd +application/vnd.medcalcdata mc1 +application/vnd.mediastation.cdkey cdkey +# application/vnd.meridian-slingshot +application/vnd.mfer mwf +application/vnd.mfmp mfm +application/vnd.micrografx.flo flo +application/vnd.micrografx.igx igx +application/vnd.mif mif +# application/vnd.minisoft-hp3000-save +# application/vnd.mitsubishi.misty-guard.trustweb +application/vnd.mobius.daf daf +application/vnd.mobius.dis dis +application/vnd.mobius.mbk mbk +application/vnd.mobius.mqy mqy +application/vnd.mobius.msl msl +application/vnd.mobius.plc plc +application/vnd.mobius.txf txf +application/vnd.mophun.application mpn +application/vnd.mophun.certificate mpc +# application/vnd.motorola.flexsuite +# application/vnd.motorola.flexsuite.adsi +# application/vnd.motorola.flexsuite.fis +# application/vnd.motorola.flexsuite.gotap +# application/vnd.motorola.flexsuite.kmr +# application/vnd.motorola.flexsuite.ttc +# application/vnd.motorola.flexsuite.wem +# application/vnd.motorola.iprm +application/vnd.mozilla.xul+xml xul +application/vnd.ms-artgalry cil +# application/vnd.ms-asf +application/vnd.ms-cab-compressed cab +# application/vnd.ms-color.iccprofile +application/vnd.ms-excel xls xlm xla xlc xlt xlw +application/vnd.ms-excel.addin.macroenabled.12 xlam +application/vnd.ms-excel.sheet.binary.macroenabled.12 xlsb +application/vnd.ms-excel.sheet.macroenabled.12 xlsm +application/vnd.ms-excel.template.macroenabled.12 xltm +application/vnd.ms-fontobject eot +application/vnd.ms-htmlhelp chm +application/vnd.ms-ims ims +application/vnd.ms-lrm lrm +# application/vnd.ms-office.activex+xml +application/vnd.ms-officetheme thmx +# application/vnd.ms-opentype +# application/vnd.ms-package.obfuscated-opentype +application/vnd.ms-pki.seccat cat +application/vnd.ms-pki.stl stl +# application/vnd.ms-playready.initiator+xml +application/vnd.ms-powerpoint ppt pps pot +application/vnd.ms-powerpoint.addin.macroenabled.12 ppam +application/vnd.ms-powerpoint.presentation.macroenabled.12 pptm +application/vnd.ms-powerpoint.slide.macroenabled.12 sldm +application/vnd.ms-powerpoint.slideshow.macroenabled.12 ppsm +application/vnd.ms-powerpoint.template.macroenabled.12 potm +# application/vnd.ms-printing.printticket+xml +application/vnd.ms-project mpp mpt +# application/vnd.ms-tnef +# application/vnd.ms-wmdrm.lic-chlg-req +# application/vnd.ms-wmdrm.lic-resp +# application/vnd.ms-wmdrm.meter-chlg-req +# application/vnd.ms-wmdrm.meter-resp +application/vnd.ms-word.document.macroenabled.12 docm +application/vnd.ms-word.template.macroenabled.12 dotm +application/vnd.ms-works wps wks wcm wdb +application/vnd.ms-wpl wpl +application/vnd.ms-xpsdocument xps +application/vnd.mseq mseq +# application/vnd.msign +# application/vnd.multiad.creator +# application/vnd.multiad.creator.cif +# application/vnd.music-niff +application/vnd.musician mus +application/vnd.muvee.style msty +application/vnd.mynfc taglet +# application/vnd.ncd.control +# application/vnd.ncd.reference +# application/vnd.nervana +# application/vnd.netfpx +application/vnd.neurolanguage.nlu nlu +application/vnd.nitf ntf nitf +application/vnd.noblenet-directory nnd +application/vnd.noblenet-sealer nns +application/vnd.noblenet-web nnw +# application/vnd.nokia.catalogs +# application/vnd.nokia.conml+wbxml +# application/vnd.nokia.conml+xml +# application/vnd.nokia.isds-radio-presets +# application/vnd.nokia.iptv.config+xml +# application/vnd.nokia.landmark+wbxml +# application/vnd.nokia.landmark+xml +# application/vnd.nokia.landmarkcollection+xml +# application/vnd.nokia.n-gage.ac+xml +application/vnd.nokia.n-gage.data ngdat +application/vnd.nokia.n-gage.symbian.install n-gage +# application/vnd.nokia.ncd +# application/vnd.nokia.pcd+wbxml +# application/vnd.nokia.pcd+xml +application/vnd.nokia.radio-preset rpst +application/vnd.nokia.radio-presets rpss +application/vnd.novadigm.edm edm +application/vnd.novadigm.edx edx +application/vnd.novadigm.ext ext +# application/vnd.ntt-local.file-transfer +# application/vnd.ntt-local.sip-ta_remote +# application/vnd.ntt-local.sip-ta_tcp_stream +application/vnd.oasis.opendocument.chart odc +application/vnd.oasis.opendocument.chart-template otc +application/vnd.oasis.opendocument.database odb +application/vnd.oasis.opendocument.formula odf +application/vnd.oasis.opendocument.formula-template odft +application/vnd.oasis.opendocument.graphics odg +application/vnd.oasis.opendocument.graphics-template otg +application/vnd.oasis.opendocument.image odi +application/vnd.oasis.opendocument.image-template oti +application/vnd.oasis.opendocument.presentation odp +application/vnd.oasis.opendocument.presentation-template otp +application/vnd.oasis.opendocument.spreadsheet ods +application/vnd.oasis.opendocument.spreadsheet-template ots +application/vnd.oasis.opendocument.text odt +application/vnd.oasis.opendocument.text-master odm +application/vnd.oasis.opendocument.text-template ott +application/vnd.oasis.opendocument.text-web oth +# application/vnd.obn +# application/vnd.oftn.l10n+json +# application/vnd.oipf.contentaccessdownload+xml +# application/vnd.oipf.contentaccessstreaming+xml +# application/vnd.oipf.cspg-hexbinary +# application/vnd.oipf.dae.svg+xml +# application/vnd.oipf.dae.xhtml+xml +# application/vnd.oipf.mippvcontrolmessage+xml +# application/vnd.oipf.pae.gem +# application/vnd.oipf.spdiscovery+xml +# application/vnd.oipf.spdlist+xml +# application/vnd.oipf.ueprofile+xml +# application/vnd.oipf.userprofile+xml +application/vnd.olpc-sugar xo +# application/vnd.oma-scws-config +# application/vnd.oma-scws-http-request +# application/vnd.oma-scws-http-response +# application/vnd.oma.bcast.associated-procedure-parameter+xml +# application/vnd.oma.bcast.drm-trigger+xml +# application/vnd.oma.bcast.imd+xml +# application/vnd.oma.bcast.ltkm +# application/vnd.oma.bcast.notification+xml +# application/vnd.oma.bcast.provisioningtrigger +# application/vnd.oma.bcast.sgboot +# application/vnd.oma.bcast.sgdd+xml +# application/vnd.oma.bcast.sgdu +# application/vnd.oma.bcast.simple-symbol-container +# application/vnd.oma.bcast.smartcard-trigger+xml +# application/vnd.oma.bcast.sprov+xml +# application/vnd.oma.bcast.stkm +# application/vnd.oma.cab-address-book+xml +# application/vnd.oma.cab-feature-handler+xml +# application/vnd.oma.cab-pcc+xml +# application/vnd.oma.cab-user-prefs+xml +# application/vnd.oma.dcd +# application/vnd.oma.dcdc +application/vnd.oma.dd2+xml dd2 +# application/vnd.oma.drm.risd+xml +# application/vnd.oma.group-usage-list+xml +# application/vnd.oma.pal+xml +# application/vnd.oma.poc.detailed-progress-report+xml +# application/vnd.oma.poc.final-report+xml +# application/vnd.oma.poc.groups+xml +# application/vnd.oma.poc.invocation-descriptor+xml +# application/vnd.oma.poc.optimized-progress-report+xml +# application/vnd.oma.push +# application/vnd.oma.scidm.messages+xml +# application/vnd.oma.xcap-directory+xml +# application/vnd.omads-email+xml +# application/vnd.omads-file+xml +# application/vnd.omads-folder+xml +# application/vnd.omaloc-supl-init +application/vnd.openofficeorg.extension oxt +# application/vnd.openxmlformats-officedocument.custom-properties+xml +# application/vnd.openxmlformats-officedocument.customxmlproperties+xml +# application/vnd.openxmlformats-officedocument.drawing+xml +# application/vnd.openxmlformats-officedocument.drawingml.chart+xml +# application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml +# application/vnd.openxmlformats-officedocument.drawingml.diagramcolors+xml +# application/vnd.openxmlformats-officedocument.drawingml.diagramdata+xml +# application/vnd.openxmlformats-officedocument.drawingml.diagramlayout+xml +# application/vnd.openxmlformats-officedocument.drawingml.diagramstyle+xml +# application/vnd.openxmlformats-officedocument.extended-properties+xml +# application/vnd.openxmlformats-officedocument.presentationml.commentauthors+xml +# application/vnd.openxmlformats-officedocument.presentationml.comments+xml +# application/vnd.openxmlformats-officedocument.presentationml.handoutmaster+xml +# application/vnd.openxmlformats-officedocument.presentationml.notesmaster+xml +# application/vnd.openxmlformats-officedocument.presentationml.notesslide+xml +application/vnd.openxmlformats-officedocument.presentationml.presentation pptx +# application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml +# application/vnd.openxmlformats-officedocument.presentationml.presprops+xml +application/vnd.openxmlformats-officedocument.presentationml.slide sldx +# application/vnd.openxmlformats-officedocument.presentationml.slide+xml +# application/vnd.openxmlformats-officedocument.presentationml.slidelayout+xml +# application/vnd.openxmlformats-officedocument.presentationml.slidemaster+xml +application/vnd.openxmlformats-officedocument.presentationml.slideshow ppsx +# application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml +# application/vnd.openxmlformats-officedocument.presentationml.slideupdateinfo+xml +# application/vnd.openxmlformats-officedocument.presentationml.tablestyles+xml +# application/vnd.openxmlformats-officedocument.presentationml.tags+xml +application/vnd.openxmlformats-officedocument.presentationml.template potx +# application/vnd.openxmlformats-officedocument.presentationml.template.main+xml +# application/vnd.openxmlformats-officedocument.presentationml.viewprops+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.calcchain+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.externallink+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcachedefinition+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcacherecords+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.pivottable+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.querytable+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.revisionheaders+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.revisionlog+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.sharedstrings+xml +application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx +# application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.sheetmetadata+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.tablesinglecells+xml +application/vnd.openxmlformats-officedocument.spreadsheetml.template xltx +# application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.usernames+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.volatiledependencies+xml +# application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml +# application/vnd.openxmlformats-officedocument.theme+xml +# application/vnd.openxmlformats-officedocument.themeoverride+xml +# application/vnd.openxmlformats-officedocument.vmldrawing +# application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml +application/vnd.openxmlformats-officedocument.wordprocessingml.document docx +# application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.fonttable+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml +application/vnd.openxmlformats-officedocument.wordprocessingml.template dotx +# application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml +# application/vnd.openxmlformats-officedocument.wordprocessingml.websettings+xml +# application/vnd.openxmlformats-package.core-properties+xml +# application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml +# application/vnd.openxmlformats-package.relationships+xml +# application/vnd.quobject-quoxdocument +# application/vnd.osa.netdeploy +application/vnd.osgeo.mapguide.package mgp +# application/vnd.osgi.bundle +application/vnd.osgi.dp dp +application/vnd.osgi.subsystem esa +# application/vnd.otps.ct-kip+xml +application/vnd.palm pdb pqa oprc +# application/vnd.paos.xml +application/vnd.pawaafile paw +application/vnd.pg.format str +application/vnd.pg.osasli ei6 +# application/vnd.piaccess.application-licence +application/vnd.picsel efif +application/vnd.pmi.widget wg +# application/vnd.poc.group-advertisement+xml +application/vnd.pocketlearn plf +application/vnd.powerbuilder6 pbd +# application/vnd.powerbuilder6-s +# application/vnd.powerbuilder7 +# application/vnd.powerbuilder7-s +# application/vnd.powerbuilder75 +# application/vnd.powerbuilder75-s +# application/vnd.preminet +application/vnd.previewsystems.box box +application/vnd.proteus.magazine mgz +application/vnd.publishare-delta-tree qps +application/vnd.pvi.ptid1 ptid +# application/vnd.pwg-multiplexed +# application/vnd.pwg-xhtml-print+xml +# application/vnd.qualcomm.brew-app-res +application/vnd.quark.quarkxpress qxd qxt qwd qwt qxl qxb +# application/vnd.radisys.moml+xml +# application/vnd.radisys.msml+xml +# application/vnd.radisys.msml-audit+xml +# application/vnd.radisys.msml-audit-conf+xml +# application/vnd.radisys.msml-audit-conn+xml +# application/vnd.radisys.msml-audit-dialog+xml +# application/vnd.radisys.msml-audit-stream+xml +# application/vnd.radisys.msml-conf+xml +# application/vnd.radisys.msml-dialog+xml +# application/vnd.radisys.msml-dialog-base+xml +# application/vnd.radisys.msml-dialog-fax-detect+xml +# application/vnd.radisys.msml-dialog-fax-sendrecv+xml +# application/vnd.radisys.msml-dialog-group+xml +# application/vnd.radisys.msml-dialog-speech+xml +# application/vnd.radisys.msml-dialog-transform+xml +# application/vnd.rainstor.data +# application/vnd.rapid +application/vnd.realvnc.bed bed +application/vnd.recordare.musicxml mxl +application/vnd.recordare.musicxml+xml musicxml +# application/vnd.renlearn.rlprint +application/vnd.rig.cryptonote cryptonote +application/vnd.rim.cod cod +application/vnd.rn-realmedia rm +application/vnd.rn-realmedia-vbr rmvb +application/vnd.route66.link66+xml link66 +# application/vnd.rs-274x +# application/vnd.ruckus.download +# application/vnd.s3sms +application/vnd.sailingtracker.track st +# application/vnd.sbm.cid +# application/vnd.sbm.mid2 +# application/vnd.scribus +# application/vnd.sealed.3df +# application/vnd.sealed.csf +# application/vnd.sealed.doc +# application/vnd.sealed.eml +# application/vnd.sealed.mht +# application/vnd.sealed.net +# application/vnd.sealed.ppt +# application/vnd.sealed.tiff +# application/vnd.sealed.xls +# application/vnd.sealedmedia.softseal.html +# application/vnd.sealedmedia.softseal.pdf +application/vnd.seemail see +application/vnd.sema sema +application/vnd.semd semd +application/vnd.semf semf +application/vnd.shana.informed.formdata ifm +application/vnd.shana.informed.formtemplate itp +application/vnd.shana.informed.interchange iif +application/vnd.shana.informed.package ipk +application/vnd.simtech-mindmapper twd twds +application/vnd.smaf mmf +# application/vnd.smart.notebook +application/vnd.smart.teacher teacher +# application/vnd.software602.filler.form+xml +# application/vnd.software602.filler.form-xml-zip +application/vnd.solent.sdkm+xml sdkm sdkd +application/vnd.spotfire.dxp dxp +application/vnd.spotfire.sfs sfs +# application/vnd.sss-cod +# application/vnd.sss-dtf +# application/vnd.sss-ntf +application/vnd.stardivision.calc sdc +application/vnd.stardivision.draw sda +application/vnd.stardivision.impress sdd +application/vnd.stardivision.math smf +application/vnd.stardivision.writer sdw vor +application/vnd.stardivision.writer-global sgl +application/vnd.stepmania.package smzip +application/vnd.stepmania.stepchart sm +# application/vnd.street-stream +application/vnd.sun.xml.calc sxc +application/vnd.sun.xml.calc.template stc +application/vnd.sun.xml.draw sxd +application/vnd.sun.xml.draw.template std +application/vnd.sun.xml.impress sxi +application/vnd.sun.xml.impress.template sti +application/vnd.sun.xml.math sxm +application/vnd.sun.xml.writer sxw +application/vnd.sun.xml.writer.global sxg +application/vnd.sun.xml.writer.template stw +# application/vnd.sun.wadl+xml +application/vnd.sus-calendar sus susp +application/vnd.svd svd +# application/vnd.swiftview-ics +application/vnd.symbian.install sis sisx +application/vnd.syncml+xml xsm +application/vnd.syncml.dm+wbxml bdm +application/vnd.syncml.dm+xml xdm +# application/vnd.syncml.dm.notification +# application/vnd.syncml.ds.notification +application/vnd.tao.intent-module-archive tao +application/vnd.tcpdump.pcap pcap cap dmp +application/vnd.tmobile-livetv tmo +application/vnd.trid.tpt tpt +application/vnd.triscape.mxs mxs +application/vnd.trueapp tra +# application/vnd.truedoc +# application/vnd.ubisoft.webplayer +application/vnd.ufdl ufd ufdl +application/vnd.uiq.theme utz +application/vnd.umajin umj +application/vnd.unity unityweb +application/vnd.uoml+xml uoml +# application/vnd.uplanet.alert +# application/vnd.uplanet.alert-wbxml +# application/vnd.uplanet.bearer-choice +# application/vnd.uplanet.bearer-choice-wbxml +# application/vnd.uplanet.cacheop +# application/vnd.uplanet.cacheop-wbxml +# application/vnd.uplanet.channel +# application/vnd.uplanet.channel-wbxml +# application/vnd.uplanet.list +# application/vnd.uplanet.list-wbxml +# application/vnd.uplanet.listcmd +# application/vnd.uplanet.listcmd-wbxml +# application/vnd.uplanet.signal +application/vnd.vcx vcx +# application/vnd.vd-study +# application/vnd.vectorworks +# application/vnd.verimatrix.vcas +# application/vnd.vidsoft.vidconference +application/vnd.visio vsd vst vss vsw +application/vnd.visionary vis +# application/vnd.vividence.scriptfile +application/vnd.vsf vsf +# application/vnd.wap.sic +# application/vnd.wap.slc +application/vnd.wap.wbxml wbxml +application/vnd.wap.wmlc wmlc +application/vnd.wap.wmlscriptc wmlsc +application/vnd.webturbo wtb +# application/vnd.wfa.wsc +# application/vnd.wmc +# application/vnd.wmf.bootstrap +# application/vnd.wolfram.mathematica +# application/vnd.wolfram.mathematica.package +application/vnd.wolfram.player nbp +application/vnd.wordperfect wpd +application/vnd.wqd wqd +# application/vnd.wrq-hp3000-labelled +application/vnd.wt.stf stf +# application/vnd.wv.csp+wbxml +# application/vnd.wv.csp+xml +# application/vnd.wv.ssp+xml +application/vnd.xara xar +application/vnd.xfdl xfdl +# application/vnd.xfdl.webform +# application/vnd.xmi+xml +# application/vnd.xmpie.cpkg +# application/vnd.xmpie.dpkg +# application/vnd.xmpie.plan +# application/vnd.xmpie.ppkg +# application/vnd.xmpie.xlim +application/vnd.yamaha.hv-dic hvd +application/vnd.yamaha.hv-script hvs +application/vnd.yamaha.hv-voice hvp +application/vnd.yamaha.openscoreformat osf +application/vnd.yamaha.openscoreformat.osfpvg+xml osfpvg +# application/vnd.yamaha.remote-setup +application/vnd.yamaha.smaf-audio saf +application/vnd.yamaha.smaf-phrase spf +# application/vnd.yamaha.through-ngn +# application/vnd.yamaha.tunnel-udpencap +application/vnd.yellowriver-custom-menu cmp +application/vnd.zul zir zirz +application/vnd.zzazz.deck+xml zaz +application/voicexml+xml vxml +# application/vq-rtcpxr +# application/watcherinfo+xml +# application/whoispp-query +# application/whoispp-response +application/widget wgt +application/winhlp hlp +# application/wita +# application/wordperfect5.1 +application/wsdl+xml wsdl +application/wspolicy+xml wspolicy +application/x-7z-compressed 7z +application/x-abiword abw +application/x-ace-compressed ace +# application/x-amf +application/x-apple-diskimage dmg +application/x-authorware-bin aab x32 u32 vox +application/x-authorware-map aam +application/x-authorware-seg aas +application/x-bcpio bcpio +application/x-bittorrent torrent +application/x-blorb blb blorb +application/x-bzip bz +application/x-bzip2 bz2 boz +application/x-cbr cbr cba cbt cbz cb7 +application/x-cdlink vcd +application/x-cfs-compressed cfs +application/x-chat chat +application/x-chess-pgn pgn +application/x-conference nsc +# application/x-compress +application/x-cpio cpio +application/x-csh csh +application/x-debian-package deb udeb +application/x-dgc-compressed dgc +application/x-director dir dcr dxr cst cct cxt w3d fgd swa +application/x-doom wad +application/x-dtbncx+xml ncx +application/x-dtbook+xml dtb +application/x-dtbresource+xml res +application/x-dvi dvi +application/x-envoy evy +application/x-eva eva +application/x-font-bdf bdf +# application/x-font-dos +# application/x-font-framemaker +application/x-font-ghostscript gsf +# application/x-font-libgrx +application/x-font-linux-psf psf +application/x-font-otf otf +application/x-font-pcf pcf +application/x-font-snf snf +# application/x-font-speedo +# application/x-font-sunos-news +application/x-font-ttf ttf ttc +application/x-font-type1 pfa pfb pfm afm +application/font-woff woff +# application/x-font-vfont +application/x-freearc arc +application/x-futuresplash spl +application/x-gca-compressed gca +application/x-glulx ulx +application/x-gnumeric gnumeric +application/x-gramps-xml gramps +application/x-gtar gtar +# application/x-gzip +application/x-hdf hdf +application/x-install-instructions install +application/x-iso9660-image iso +application/x-java-jnlp-file jnlp +application/x-latex latex +application/x-lzh-compressed lzh lha +application/x-mie mie +application/x-mobipocket-ebook prc mobi +application/x-ms-application application +application/x-ms-shortcut lnk +application/x-ms-wmd wmd +application/x-ms-wmz wmz +application/x-ms-xbap xbap +application/x-msaccess mdb +application/x-msbinder obd +application/x-mscardfile crd +application/x-msclip clp +application/x-msdownload exe dll com bat msi +application/x-msmediaview mvb m13 m14 +application/x-msmetafile wmf wmz emf emz +application/x-msmoney mny +application/x-mspublisher pub +application/x-msschedule scd +application/x-msterminal trm +application/x-mswrite wri +application/x-netcdf nc cdf +application/x-nzb nzb +application/x-pkcs12 p12 pfx +application/x-pkcs7-certificates p7b spc +application/x-pkcs7-certreqresp p7r +application/x-rar-compressed rar +application/x-research-info-systems ris +application/x-sh sh +application/x-shar shar +application/x-shockwave-flash swf +application/x-silverlight-app xap +application/x-sql sql +application/x-stuffit sit +application/x-stuffitx sitx +application/x-subrip srt +application/x-sv4cpio sv4cpio +application/x-sv4crc sv4crc +application/x-t3vm-image t3 +application/x-tads gam +application/x-tar tar +application/x-tcl tcl +application/x-tex tex +application/x-tex-tfm tfm +application/x-texinfo texinfo texi +application/x-tgif obj +application/x-ustar ustar +application/x-wais-source src +application/x-x509-ca-cert der crt +application/x-xfig fig +application/x-xliff+xml xlf +application/x-xpinstall xpi +application/x-xz xz +application/x-zmachine z1 z2 z3 z4 z5 z6 z7 z8 +# application/x400-bp +application/xaml+xml xaml +# application/xcap-att+xml +# application/xcap-caps+xml +application/xcap-diff+xml xdf +# application/xcap-el+xml +# application/xcap-error+xml +# application/xcap-ns+xml +# application/xcon-conference-info-diff+xml +# application/xcon-conference-info+xml +application/xenc+xml xenc +application/xhtml+xml xhtml xht +# application/xhtml-voice+xml +application/xml xml xsl +application/xml-dtd dtd +# application/xml-external-parsed-entity +# application/xmpp+xml +application/xop+xml xop +application/xproc+xml xpl +application/xslt+xml xslt +application/xspf+xml xspf +application/xv+xml mxml xhvml xvml xvm +application/yang yang +application/yin+xml yin +application/zip zip +# audio/1d-interleaved-parityfec +# audio/32kadpcm +# audio/3gpp +# audio/3gpp2 +# audio/ac3 +audio/adpcm adp +# audio/amr +# audio/amr-wb +# audio/amr-wb+ +# audio/asc +# audio/atrac-advanced-lossless +# audio/atrac-x +# audio/atrac3 +audio/basic au snd +# audio/bv16 +# audio/bv32 +# audio/clearmode +# audio/cn +# audio/dat12 +# audio/dls +# audio/dsr-es201108 +# audio/dsr-es202050 +# audio/dsr-es202211 +# audio/dsr-es202212 +# audio/dv +# audio/dvi4 +# audio/eac3 +# audio/evrc +# audio/evrc-qcp +# audio/evrc0 +# audio/evrc1 +# audio/evrcb +# audio/evrcb0 +# audio/evrcb1 +# audio/evrcwb +# audio/evrcwb0 +# audio/evrcwb1 +# audio/example +# audio/fwdred +# audio/g719 +# audio/g722 +# audio/g7221 +# audio/g723 +# audio/g726-16 +# audio/g726-24 +# audio/g726-32 +# audio/g726-40 +# audio/g728 +# audio/g729 +# audio/g7291 +# audio/g729d +# audio/g729e +# audio/gsm +# audio/gsm-efr +# audio/gsm-hr-08 +# audio/ilbc +# audio/ip-mr_v2.5 +# audio/isac +# audio/l16 +# audio/l20 +# audio/l24 +# audio/l8 +# audio/lpc +audio/midi mid midi kar rmi +# audio/mobile-xmf +audio/mp4 mp4a +# audio/mp4a-latm +# audio/mpa +# audio/mpa-robust +audio/mpeg mpga mp2 mp2a mp3 m2a m3a +# audio/mpeg4-generic +# audio/musepack +audio/ogg oga ogg spx +# audio/opus +# audio/parityfec +# audio/pcma +# audio/pcma-wb +# audio/pcmu-wb +# audio/pcmu +# audio/prs.sid +# audio/qcelp +# audio/red +# audio/rtp-enc-aescm128 +# audio/rtp-midi +# audio/rtx +audio/s3m s3m +audio/silk sil +# audio/smv +# audio/smv0 +# audio/smv-qcp +# audio/sp-midi +# audio/speex +# audio/t140c +# audio/t38 +# audio/telephone-event +# audio/tone +# audio/uemclip +# audio/ulpfec +# audio/vdvi +# audio/vmr-wb +# audio/vnd.3gpp.iufp +# audio/vnd.4sb +# audio/vnd.audiokoz +# audio/vnd.celp +# audio/vnd.cisco.nse +# audio/vnd.cmles.radio-events +# audio/vnd.cns.anp1 +# audio/vnd.cns.inf1 +audio/vnd.dece.audio uva uvva +audio/vnd.digital-winds eol +# audio/vnd.dlna.adts +# audio/vnd.dolby.heaac.1 +# audio/vnd.dolby.heaac.2 +# audio/vnd.dolby.mlp +# audio/vnd.dolby.mps +# audio/vnd.dolby.pl2 +# audio/vnd.dolby.pl2x +# audio/vnd.dolby.pl2z +# audio/vnd.dolby.pulse.1 +audio/vnd.dra dra +audio/vnd.dts dts +audio/vnd.dts.hd dtshd +# audio/vnd.dvb.file +# audio/vnd.everad.plj +# audio/vnd.hns.audio +audio/vnd.lucent.voice lvp +audio/vnd.ms-playready.media.pya pya +# audio/vnd.nokia.mobile-xmf +# audio/vnd.nortel.vbk +audio/vnd.nuera.ecelp4800 ecelp4800 +audio/vnd.nuera.ecelp7470 ecelp7470 +audio/vnd.nuera.ecelp9600 ecelp9600 +# audio/vnd.octel.sbc +# audio/vnd.qcelp +# audio/vnd.rhetorex.32kadpcm +audio/vnd.rip rip +# audio/vnd.sealedmedia.softseal.mpeg +# audio/vnd.vmx.cvsd +# audio/vorbis +# audio/vorbis-config +audio/webm weba +audio/x-aac aac +audio/x-aiff aif aiff aifc +audio/x-caf caf +audio/x-flac flac +audio/x-matroska mka +audio/x-mpegurl m3u +audio/x-ms-wax wax +audio/x-ms-wma wma +audio/x-pn-realaudio ram ra +audio/x-pn-realaudio-plugin rmp +# audio/x-tta +audio/x-wav wav +audio/xm xm +chemical/x-cdx cdx +chemical/x-cif cif +chemical/x-cmdf cmdf +chemical/x-cml cml +chemical/x-csml csml +# chemical/x-pdb +chemical/x-xyz xyz +image/bmp bmp +image/cgm cgm +# image/example +# image/fits +image/g3fax g3 +image/gif gif +image/ief ief +# image/jp2 +image/jpeg jpeg jpg jpe +# image/jpm +# image/jpx +image/ktx ktx +# image/naplps +image/png png +image/prs.btif btif +# image/prs.pti +image/sgi sgi +image/svg+xml svg svgz +# image/t38 +image/tiff tiff tif +# image/tiff-fx +image/vnd.adobe.photoshop psd +# image/vnd.cns.inf2 +image/vnd.dece.graphic uvi uvvi uvg uvvg +image/vnd.dvb.subtitle sub +image/vnd.djvu djvu djv +image/vnd.dwg dwg +image/vnd.dxf dxf +image/vnd.fastbidsheet fbs +image/vnd.fpx fpx +image/vnd.fst fst +image/vnd.fujixerox.edmics-mmr mmr +image/vnd.fujixerox.edmics-rlc rlc +# image/vnd.globalgraphics.pgb +# image/vnd.microsoft.icon +# image/vnd.mix +image/vnd.ms-modi mdi +image/vnd.ms-photo wdp +image/vnd.net-fpx npx +# image/vnd.radiance +# image/vnd.sealed.png +# image/vnd.sealedmedia.softseal.gif +# image/vnd.sealedmedia.softseal.jpg +# image/vnd.svf +image/vnd.wap.wbmp wbmp +image/vnd.xiff xif +image/webp webp +image/x-3ds 3ds +image/x-cmu-raster ras +image/x-cmx cmx +image/x-freehand fh fhc fh4 fh5 fh7 +image/x-icon ico +image/x-mrsid-image sid +image/x-pcx pcx +image/x-pict pic pct +image/x-portable-anymap pnm +image/x-portable-bitmap pbm +image/x-portable-graymap pgm +image/x-portable-pixmap ppm +image/x-rgb rgb +image/x-tga tga +image/x-xbitmap xbm +image/x-xpixmap xpm +image/x-xwindowdump xwd +# message/cpim +# message/delivery-status +# message/disposition-notification +# message/example +# message/external-body +# message/feedback-report +# message/global +# message/global-delivery-status +# message/global-disposition-notification +# message/global-headers +# message/http +# message/imdn+xml +# message/news +# message/partial +message/rfc822 eml mime +# message/s-http +# message/sip +# message/sipfrag +# message/tracking-status +# message/vnd.si.simp +# model/example +model/iges igs iges +model/mesh msh mesh silo +model/vnd.collada+xml dae +model/vnd.dwf dwf +# model/vnd.flatland.3dml +model/vnd.gdl gdl +# model/vnd.gs-gdl +# model/vnd.gs.gdl +model/vnd.gtw gtw +# model/vnd.moml+xml +model/vnd.mts mts +# model/vnd.parasolid.transmit.binary +# model/vnd.parasolid.transmit.text +model/vnd.vtu vtu +model/vrml wrl vrml +model/x3d+binary x3db x3dbz +model/x3d+vrml x3dv x3dvz +model/x3d+xml x3d x3dz +# multipart/alternative +# multipart/appledouble +# multipart/byteranges +# multipart/digest +# multipart/encrypted +# multipart/example +# multipart/form-data +# multipart/header-set +# multipart/mixed +# multipart/parallel +# multipart/related +# multipart/report +# multipart/signed +# multipart/voice-message +# text/1d-interleaved-parityfec +text/cache-manifest appcache +text/calendar ics ifb +text/css css +text/csv csv +# text/directory +# text/dns +# text/ecmascript +# text/enriched +# text/example +# text/fwdred +text/html html htm +# text/javascript +text/n3 n3 +# text/parityfec +text/plain txt text conf def list log in +# text/prs.fallenstein.rst +text/prs.lines.tag dsc +# text/vnd.radisys.msml-basic-layout +# text/red +# text/rfc822-headers +text/richtext rtx +# text/rtf +# text/rtp-enc-aescm128 +# text/rtx +text/sgml sgml sgm +# text/t140 +text/tab-separated-values tsv +text/troff t tr roff man me ms +text/turtle ttl +# text/ulpfec +text/uri-list uri uris urls +text/vcard vcard +# text/vnd.abc +text/vnd.curl curl +text/vnd.curl.dcurl dcurl +text/vnd.curl.scurl scurl +text/vnd.curl.mcurl mcurl +# text/vnd.dmclientscript +text/vnd.dvb.subtitle sub +# text/vnd.esmertec.theme-descriptor +text/vnd.fly fly +text/vnd.fmi.flexstor flx +text/vnd.graphviz gv +text/vnd.in3d.3dml 3dml +text/vnd.in3d.spot spot +# text/vnd.iptc.newsml +# text/vnd.iptc.nitf +# text/vnd.latex-z +# text/vnd.motorola.reflex +# text/vnd.ms-mediapackage +# text/vnd.net2phone.commcenter.command +# text/vnd.si.uricatalogue +text/vnd.sun.j2me.app-descriptor jad +# text/vnd.trolltech.linguist +# text/vnd.wap.si +# text/vnd.wap.sl +text/vnd.wap.wml wml +text/vnd.wap.wmlscript wmls +text/x-asm s asm +text/x-c c cc cxx cpp h hh dic +text/x-fortran f for f77 f90 +text/x-java-source java +text/x-opml opml +text/x-pascal p pas +text/x-nfo nfo +text/x-setext etx +text/x-sfv sfv +text/x-uuencode uu +text/x-vcalendar vcs +text/x-vcard vcf +# text/xml +# text/xml-external-parsed-entity +# video/1d-interleaved-parityfec +video/3gpp 3gp +# video/3gpp-tt +video/3gpp2 3g2 +# video/bmpeg +# video/bt656 +# video/celb +# video/dv +# video/example +video/h261 h261 +video/h263 h263 +# video/h263-1998 +# video/h263-2000 +video/h264 h264 +# video/h264-rcdo +# video/h264-svc +video/jpeg jpgv +# video/jpeg2000 +video/jpm jpm jpgm +video/mj2 mj2 mjp2 +# video/mp1s +# video/mp2p +# video/mp2t +video/mp4 mp4 mp4v mpg4 +# video/mp4v-es +video/mpeg mpeg mpg mpe m1v m2v +# video/mpeg4-generic +# video/mpv +# video/nv +video/ogg ogv +# video/parityfec +# video/pointer +video/quicktime qt mov +# video/raw +# video/rtp-enc-aescm128 +# video/rtx +# video/smpte292m +# video/ulpfec +# video/vc1 +# video/vnd.cctv +video/vnd.dece.hd uvh uvvh +video/vnd.dece.mobile uvm uvvm +# video/vnd.dece.mp4 +video/vnd.dece.pd uvp uvvp +video/vnd.dece.sd uvs uvvs +video/vnd.dece.video uvv uvvv +# video/vnd.directv.mpeg +# video/vnd.directv.mpeg-tts +# video/vnd.dlna.mpeg-tts +video/vnd.dvb.file dvb +video/vnd.fvt fvt +# video/vnd.hns.video +# video/vnd.iptvforum.1dparityfec-1010 +# video/vnd.iptvforum.1dparityfec-2005 +# video/vnd.iptvforum.2dparityfec-1010 +# video/vnd.iptvforum.2dparityfec-2005 +# video/vnd.iptvforum.ttsavc +# video/vnd.iptvforum.ttsmpeg2 +# video/vnd.motorola.video +# video/vnd.motorola.videop +video/vnd.mpegurl mxu m4u +video/vnd.ms-playready.media.pyv pyv +# video/vnd.nokia.interleaved-multimedia +# video/vnd.nokia.videovoip +# video/vnd.objectvideo +# video/vnd.sealed.mpeg1 +# video/vnd.sealed.mpeg4 +# video/vnd.sealed.swf +# video/vnd.sealedmedia.softseal.mov +video/vnd.uvvu.mp4 uvu uvvu +video/vnd.vivo viv +video/webm webm +video/x-f4v f4v +video/x-fli fli +video/x-flv flv +video/x-m4v m4v +video/x-matroska mkv mk3d mks +video/x-mng mng +video/x-ms-asf asf asx +video/x-ms-vob vob +video/x-ms-wm wm +video/x-ms-wmv wmv +video/x-ms-wmx wmx +video/x-ms-wvx wvx +video/x-msvideo avi +video/x-sgi-movie movie +video/x-smv smv +x-conference/x-cooltalk ice diff --git a/server/node_modules/express/node_modules/send/node_modules/mime/types/node.types b/server/node_modules/express/node_modules/send/node_modules/mime/types/node.types new file mode 100644 index 0000000..55b2cf7 --- /dev/null +++ b/server/node_modules/express/node_modules/send/node_modules/mime/types/node.types @@ -0,0 +1,77 @@ +# What: WebVTT +# Why: To allow formats intended for marking up external text track resources. +# http://dev.w3.org/html5/webvtt/ +# Added by: niftylettuce +text/vtt vtt + +# What: Google Chrome Extension +# Why: To allow apps to (work) be served with the right content type header. +# http://codereview.chromium.org/2830017 +# Added by: niftylettuce +application/x-chrome-extension crx + +# What: HTC support +# Why: To properly render .htc files such as CSS3PIE +# Added by: niftylettuce +text/x-component htc + +# What: HTML5 application cache manifes ('.manifest' extension) +# Why: De-facto standard. Required by Mozilla browser when serving HTML5 apps +# per https://developer.mozilla.org/en/offline_resources_in_firefox +# Added by: louisremi +text/cache-manifest manifest + +# What: node binary buffer format +# Why: semi-standard extension w/in the node community +# Added by: tootallnate +application/octet-stream buffer + +# What: The "protected" MP-4 formats used by iTunes. +# Why: Required for streaming music to browsers (?) +# Added by: broofa +application/mp4 m4p +audio/mp4 m4a + +# What: Video format, Part of RFC1890 +# Why: See https://github.com/bentomas/node-mime/pull/6 +# Added by: mjrusso +video/MP2T ts + +# What: EventSource mime type +# Why: mime type of Server-Sent Events stream +# http://www.w3.org/TR/eventsource/#text-event-stream +# Added by: francois2metz +text/event-stream event-stream + +# What: Mozilla App manifest mime type +# Why: https://developer.mozilla.org/en/Apps/Manifest#Serving_manifests +# Added by: ednapiranha +application/x-web-app-manifest+json webapp + +# What: Lua file types +# Why: Googling around shows de-facto consensus on these +# Added by: creationix (Issue #45) +text/x-lua lua +application/x-lua-bytecode luac + +# What: Markdown files, as per http://daringfireball.net/projects/markdown/syntax +# Why: http://stackoverflow.com/questions/10701983/what-is-the-mime-type-for-markdown +# Added by: avoidwork +text/x-markdown markdown md mkd + +# What: ini files +# Why: because they're just text files +# Added by: Matthew Kastor +text/plain ini + +# What: DASH Adaptive Streaming manifest +# Why: https://developer.mozilla.org/en-US/docs/DASH_Adaptive_Streaming_for_HTML_5_Video +# Added by: eelcocramer +application/dash+xml mdp + +# What: OpenType font files - http://www.microsoft.com/typography/otspec/ +# Why: Browsers usually ignore the font MIME types and sniff the content, +# but Chrome, shows a warning if OpenType fonts aren't served with +# the `font/opentype` MIME type: http://i.imgur.com/8c5RN8M.png. +# Added by: alrra +font/opentype otf diff --git a/server/node_modules/express/node_modules/send/package.json b/server/node_modules/express/node_modules/send/package.json new file mode 100644 index 0000000..7f4d91e --- /dev/null +++ b/server/node_modules/express/node_modules/send/package.json @@ -0,0 +1,46 @@ +{ + "name": "send", + "version": "0.1.4", + "description": "Better streaming static file server with Range and conditional-GET support", + "keywords": [ + "static", + "file", + "server" + ], + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "dependencies": { + "debug": "*", + "mime": "~1.2.9", + "fresh": "0.2.0", + "range-parser": "0.0.4" + }, + "devDependencies": { + "mocha": "*", + "should": "*", + "supertest": "0.0.1", + "connect": "2.x" + }, + "scripts": { + "test": "make test" + }, + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/send.git" + }, + "main": "index", + "readme": "# send\n\n Send is Connect's `static()` extracted for generalized use, a streaming static file\n server supporting partial responses (Ranges), conditional-GET negotiation, high test coverage, and granular events which may be leveraged to take appropriate actions in your application or framework.\n\n## Installation\n\n $ npm install send\n\n## Examples\n\n Small:\n\n```js\nvar http = require('http');\nvar send = require('send');\n\nvar app = http.createServer(function(req, res){\n send(req, req.url).pipe(res);\n}).listen(3000);\n```\n\n Serving from a root directory with custom error-handling:\n\n```js\nvar http = require('http');\nvar send = require('send');\nvar url = require('url');\n\nvar app = http.createServer(function(req, res){\n // your custom error-handling logic:\n function error(err) {\n res.statusCode = err.status || 500;\n res.end(err.message);\n }\n\n // your custom directory handling logic:\n function redirect() {\n res.statusCode = 301;\n res.setHeader('Location', req.url + '/');\n res.end('Redirecting to ' + req.url + '/');\n }\n\n // transfer arbitrary files from within\n // /www/example.com/public/*\n send(req, url.parse(req.url).pathname)\n .root('/www/example.com/public')\n .on('error', error)\n .on('directory', redirect)\n .pipe(res);\n}).listen(3000);\n```\n\n## API\n\n### Events\n\n - `error` an error occurred `(err)`\n - `directory` a directory was requested\n - `file` a file was requested `(path, stat)`\n - `stream` file streaming has started `(stream)`\n - `end` streaming has completed\n\n### .root(dir)\n\n Serve files relative to `path`. Aliased as `.from(dir)`.\n\n### .index(path)\n\n By default send supports \"index.html\" files, to disable this\n invoke `.index(false)` or to supply a new index pass a string.\n\n### .maxage(ms)\n\n Provide a max-age in milliseconds for http caching, defaults to 0.\n\n### .hidden(bool)\n\n Enable or disable transfer of hidden files, defaults to false.\n\n## Error-handling\n\n By default when no `error` listeners are present an automatic response will be made, otherwise you have full control over the response, aka you may show a 5xx page etc.\n\n## Caching\n\n It does _not_ perform internal caching, you should use a reverse proxy cache such\n as Varnish for this, or those fancy things called CDNs. If your application is small enough that it would benefit from single-node memory caching, it's small enough that it does not need caching at all ;).\n\n## Debugging\n\n To enable `debug()` instrumentation output export __DEBUG__:\n\n```\n$ DEBUG=send node app\n```\n\n## Running tests\n\n```\n$ npm install\n$ make test\n```\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", + "readmeFilename": "Readme.md", + "bugs": { + "url": "https://github.com/visionmedia/send/issues" + }, + "homepage": "https://github.com/visionmedia/send", + "_id": "send@0.1.4", + "dist": { + "shasum": "f57dafe76219c92b5e63c8f57705b3315f00dee7" + }, + "_from": "send@0.1.4", + "_resolved": "https://registry.npmjs.org/send/-/send-0.1.4.tgz" +} diff --git a/server/node_modules/express/package.json b/server/node_modules/express/package.json new file mode 100644 index 0000000..ac35c63 --- /dev/null +++ b/server/node_modules/express/package.json @@ -0,0 +1,89 @@ +{ + "name": "express", + "description": "Sinatra inspired web development framework", + "version": "3.4.4", + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "contributors": [ + { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + { + "name": "Aaron Heckmann", + "email": "aaron.heckmann+github@gmail.com" + }, + { + "name": "Ciaran Jessup", + "email": "ciaranj@gmail.com" + }, + { + "name": "Guillermo Rauch", + "email": "rauchg@gmail.com" + } + ], + "dependencies": { + "connect": "2.11.0", + "commander": "1.3.2", + "range-parser": "0.0.4", + "mkdirp": "0.3.5", + "cookie": "0.1.0", + "buffer-crc32": "0.2.1", + "fresh": "0.2.0", + "methods": "0.1.0", + "send": "0.1.4", + "cookie-signature": "1.0.1", + "debug": "*" + }, + "devDependencies": { + "ejs": "*", + "mocha": "*", + "jade": "0.30.0", + "hjs": "*", + "stylus": "*", + "should": "2", + "connect-redis": "*", + "marked": "*", + "supertest": "0.8.1 - 1" + }, + "keywords": [ + "express", + "framework", + "sinatra", + "web", + "rest", + "restful", + "router", + "app", + "api" + ], + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/express" + }, + "main": "index", + "bin": { + "express": "./bin/express" + }, + "scripts": { + "prepublish": "npm prune", + "test": "make test" + }, + "engines": { + "node": "*" + }, + "readme": "[![express logo](http://f.cl.ly/items/0V2S1n0K1i3y1c122g04/Screen%20Shot%202012-04-11%20at%209.59.42%20AM.png)](http://expressjs.com/)\n\n Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).\n\n [![Build Status](https://secure.travis-ci.org/visionmedia/express.png)](http://travis-ci.org/visionmedia/express) [![Gittip](http://img.shields.io/gittip/visionmedia.png)](https://www.gittip.com/visionmedia/)\n\n```js\nvar express = require('express');\nvar app = express();\n\napp.get('/', function(req, res){\n res.send('Hello World');\n});\n\napp.listen(3000);\n```\n\n## Installation\n\n $ npm install -g express\n\n## Quick Start\n\n The quickest way to get started with express is to utilize the executable `express(1)` to generate an application as shown below:\n\n Create the app:\n\n $ npm install -g express\n $ express /tmp/foo && cd /tmp/foo\n\n Install dependencies:\n\n $ npm install\n\n Start the server:\n\n $ node app\n\n## Features\n\n * Built on [Connect](http://github.com/senchalabs/connect)\n * Robust routing\n * HTTP helpers (redirection, caching, etc)\n * View system supporting 14+ template engines\n * Content negotiation\n * Focus on high performance\n * Environment based configuration\n * Executable for generating applications quickly\n * High test coverage\n\n## Philosophy\n\n The Express philosophy is to provide small, robust tooling for HTTP servers. Making\n it a great solution for single page applications, web sites, hybrids, or public\n HTTP APIs.\n\n Built on Connect you can use _only_ what you need, and nothing more, applications\n can be as big or as small as you like, even a single file. Express does\n not force you to use any specific ORM or template engine. With support for over\n 14 template engines via [Consolidate.js](http://github.com/visionmedia/consolidate.js)\n you can quickly craft your perfect framework.\n\n## More Information\n\n * [Website and Documentation](http://expressjs.com/) stored at [visionmedia/expressjs.com](https://github.com/visionmedia/expressjs.com)\n * Join #express on freenode\n * [Google Group](http://groups.google.com/group/express-js) for discussion\n * Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates\n * Visit the [Wiki](http://github.com/visionmedia/express/wiki)\n * [Русскоязычная документация](http://jsman.ru/express/)\n * Run express examples [online](https://runnable.com/express)\n\n## Viewing Examples\n\nClone the Express repo, then install the dev dependencies to install all the example / test suite deps:\n\n $ git clone git://github.com/visionmedia/express.git --depth 1\n $ cd express\n $ npm install\n\nthen run whichever tests you want:\n\n $ node examples/content-negotiation\n\nYou can also view live examples here\n\n\n\n## Running Tests\n\nTo run the test suite first invoke the following command within the repo, installing the development dependencies:\n\n $ npm install\n\nthen run the tests:\n\n $ make test\n\n## Contributors\n\n https://github.com/visionmedia/express/graphs/contributors\n\n## License\n\n(The MIT License)\n\nCopyright (c) 2009-2012 TJ Holowaychuk <tj@vision-media.ca>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", + "readmeFilename": "Readme.md", + "bugs": { + "url": "https://github.com/visionmedia/express/issues" + }, + "homepage": "https://github.com/visionmedia/express", + "_id": "express@3.4.4", + "dist": { + "shasum": "3ac43ae43e807c6e87a540189194ea46212797de" + }, + "_from": "express@", + "_resolved": "https://registry.npmjs.org/express/-/express-3.4.4.tgz" +} diff --git a/server/node_modules/jquery/.npmignore b/server/node_modules/jquery/.npmignore new file mode 100644 index 0000000..2ccbe46 --- /dev/null +++ b/server/node_modules/jquery/.npmignore @@ -0,0 +1 @@ +/node_modules/ diff --git a/server/node_modules/jquery/LICENSE-MIT b/server/node_modules/jquery/LICENSE-MIT new file mode 100644 index 0000000..0e04ca5 --- /dev/null +++ b/server/node_modules/jquery/LICENSE-MIT @@ -0,0 +1,22 @@ +Copyright (c) 2012 James Morrin + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/server/node_modules/jquery/README.md b/server/node_modules/jquery/README.md new file mode 100644 index 0000000..ad7dfa1 --- /dev/null +++ b/server/node_modules/jquery/README.md @@ -0,0 +1,72 @@ +DOES NOT WORK ON WINDOWS +==== +Many people are having problems getting this module to work on windows. The +failure has to do with building contextify on window. It seems to be a windows +environment issue. I don't have access to a windows machine so I cannot explore +working through the windows install process. If you figure out how to build +[contextify](https://github.com/brianmcd/contextify) on windows please send me working instructions! + +NPM module jQuery is an EnderJS package. +==== +please use `npm install jquery` not `npm install jQuery` + + +node-jQuery +==== + +A stupid-simple wrapper over jQuery for Node.JS (server). Currently 1.7.2. + +Node.JS +--- +``` + npm install jquery + + var $ = require('jquery'); +``` + +Examples +--- +```javascript + $("

    test passes

    ").appendTo("body"); + console.log($("body").html()); +``` + +In Node.JS you may also create separate window instances + +```javascript + var jsdom = require('jsdom').jsdom + , myWindow = jsdom().createWindow() + , $ = require('jquery') + , jq = require('jquery').create() + , jQuery = require('jquery').create(myWindow) + ; + + $("

    test passes

    ").appendTo("body"); + console.log($("body").html()); + + jq("

    other test passes

    ").appendTo("body"); + console.log(jq("body").html()); + + jQuery("

    third test passes

    ").appendTo("body"); + console.log(jQuery("body").html()); +``` + +Output: + +```html +

    test passes

    +

    other test passes

    +

    third test passes

    +``` + +JSONP Example +---- + +```javascript + var $ = require('jquery'); + + $.getJSON('http://twitter.com/status/user_timeline/treason.json?count=10&callback=?',function(data) { + console.log(data); + }); +``` + diff --git a/server/node_modules/jquery/grunt.js b/server/node_modules/jquery/grunt.js new file mode 100644 index 0000000..03a97bb --- /dev/null +++ b/server/node_modules/jquery/grunt.js @@ -0,0 +1,100 @@ +module.exports = function(grunt) { + + var exec = require('child_process').exec, + http = require('http'), + fs = require('fs'), + host = 'ajax.googleapis.com', + jqPath = '/ajax/libs/jquery/1.8.3/jquery.js'; + + grunt.registerTask('build', 'builds jquery module for us in nodjs', function() { + var tmpDir = './tmp', distDir = './lib', + done = this.async(), wrapper; + + + function buildjQuery(jq) { + wrapper = fs.readFileSync('./src/wrapper.js', 'utf8'); + wrapper = wrapper.replace('//JQUERY_SOURCE', jq); + fs.writeFileSync('./lib/node-jquery.js', wrapper); + done(); + } + + function writejQuery() { + var data = '', + req = http.request({ + host: host, + port: 80, + path: jqPath, + method: 'GET' + }, function(res) { + res.setEncoding('utf8'); + res.on('data', function(chunk) { + data += chunk; + }); + res.on('end', function() { + fs.writeFileSync(tmpDir+'/jquery.js', data); + buildjQuery(data); + }); + }); + req.write('data\n'); + req.write('data\n'); + req.end(); + + } + + function getjQuery() { + var jq = null; + try { + jq = fs.readFileSync(tmpDir+'/jquery.js', 'utf8'); + buildjQuery(jq); + } catch (e) { + writejQuery(); + } + } + + exec('mkdir '+tmpDir+' && mkdir '+distDir, getjQuery); + }); + + grunt.registerTask('clean', 'removes dist and tmp directories', function() { + var done = this.async(); + exec('rm -rf ./tmp && rm -rf ./lib', function() { + done(); + }); + }); + + // Project configuration. + grunt.initConfig({ + pkg: '', + test: { + files: ['test/*.js'] + }, + lint: { + files: ['grunt.js', 'lib/**/*.js', 'test/**/*.js'] + }, + watch: { + files: '', + tasks: 'default' + }, + jshint: { + options: { + curly: true, + eqeqeq: true, + immed: true, + latedef: true, + newcap: true, + noarg: true, + sub: true, + undef: true, + boss: true, + eqnull: true, + node: true + }, + globals: { + exports: true + } + } + }); + + // Default task. + grunt.registerTask('default', 'build test'); + +}; diff --git a/server/node_modules/jquery/lib/node-jquery.js b/server/node_modules/jquery/lib/node-jquery.js new file mode 100644 index 0000000..73123af --- /dev/null +++ b/server/node_modules/jquery/lib/node-jquery.js @@ -0,0 +1,9505 @@ +(function () { +function create(window) { + + if(window == null ) { + window = require('jsdom').jsdom().createWindow(); + // assume window is a jsdom instance... + // jsdom includes an incomplete version of XMLHttpRequest + window.XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest; + // trick jQuery into thinking CORS is supported (should be in node-XMLHttpRequest) + window.XMLHttpRequest.prototype.withCredentials = false; + + if(window.location == null) { + window.location = require('location'); + } + + if(window.navigator == null) { + window.navigator = require('navigator'); + } + } + + + var location = window.location, + navigator = window.navigator, + XMLHttpRequest = window.XMLHttpRequest; + + /*! + * jQuery JavaScript Library v1.8.3 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2012 jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: Tue Nov 13 2012 08:20:33 GMT-0500 (Eastern Standard Time) + */ +(function( window, undefined ) { +var + // A central reference to the root jQuery(document) + rootjQuery, + + // The deferred used on DOM ready + readyList, + + // Use the correct document accordingly with window argument (sandbox) + document = window.document, + location = window.location, + navigator = window.navigator, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // Save a reference to some core methods + core_push = Array.prototype.push, + core_slice = Array.prototype.slice, + core_indexOf = Array.prototype.indexOf, + core_toString = Object.prototype.toString, + core_hasOwn = Object.prototype.hasOwnProperty, + core_trim = String.prototype.trim, + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Used for matching numbers + core_pnum = /[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source, + + // Used for detecting and trimming whitespace + core_rnotwhite = /\S/, + core_rspace = /\s+/, + + // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, + rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return ( letter + "" ).toUpperCase(); + }, + + // The ready event handler and self cleanup method + DOMContentLoaded = function() { + if ( document.addEventListener ) { + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + jQuery.ready(); + } else if ( document.readyState === "complete" ) { + // we're here because readyState === "complete" in oldIE + // which is good enough for us to call the dom ready! + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + jQuery.ready(); + } + }, + + // [[Class]] -> type pairs + class2type = {}; + +jQuery.fn = jQuery.prototype = { + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem, ret, doc; + + // Handle $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + doc = ( context && context.nodeType ? context.ownerDocument || context : document ); + + // scripts is true for back-compat + selector = jQuery.parseHTML( match[1], doc, true ); + if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { + this.attr.call( selector, context, true ); + } + + return jQuery.merge( this, selector ); + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.8.3", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return core_slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) { + ret.selector = this.selector + ( this.selector ? " " : "" ) + selector; + } else if ( name ) { + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; + }, + + eq: function( i ) { + i = +i; + return i === -1 ? + this.slice( i ) : + this.slice( i, i + 1 ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + slice: function() { + return this.pushStack( core_slice.apply( this, arguments ), + "slice", core_slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: core_push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready, 1 ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger("ready").off("ready"); + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + isWindow: function( obj ) { + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + return !isNaN( parseFloat(obj) ) && isFinite( obj ); + }, + + type: function( obj ) { + return obj == null ? + String( obj ) : + class2type[ core_toString.call(obj) ] || "object"; + }, + + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !core_hasOwn.call(obj, "constructor") && + !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || core_hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw new Error( msg ); + }, + + // data: string of html + // context (optional): If specified, the fragment will be created in this context, defaults to document + // scripts (optional): If true, will include scripts passed in the html string + parseHTML: function( data, context, scripts ) { + var parsed; + if ( !data || typeof data !== "string" ) { + return null; + } + if ( typeof context === "boolean" ) { + scripts = context; + context = 0; + } + context = context || document; + + // Single tag + if ( (parsed = rsingleTag.exec( data )) ) { + return [ context.createElement( parsed[1] ) ]; + } + + parsed = jQuery.buildFragment( [ data ], context, scripts ? null : [] ); + return jQuery.merge( [], + (parsed.cacheable ? jQuery.clone( parsed.fragment ) : parsed.fragment).childNodes ); + }, + + parseJSON: function( data ) { + if ( !data || typeof data !== "string") { + return null; + } + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return ( new Function( "return " + data ) )(); + + } + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + parseXML: function( data ) { + var xml, tmp; + if ( !data || typeof data !== "string" ) { + return null; + } + try { + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + } catch( e ) { + xml = undefined; + } + if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && core_rnotwhite.test( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // args is for internal usage only + each: function( obj, callback, args ) { + var name, + i = 0, + length = obj.length, + isObj = length === undefined || jQuery.isFunction( obj ); + + if ( args ) { + if ( isObj ) { + for ( name in obj ) { + if ( callback.apply( obj[ name ], args ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( obj[ i++ ], args ) === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isObj ) { + for ( name in obj ) { + if ( callback.call( obj[ name ], name, obj[ name ] ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.call( obj[ i ], i, obj[ i++ ] ) === false ) { + break; + } + } + } + } + + return obj; + }, + + // Use native String.trim function wherever possible + trim: core_trim && !core_trim.call("\uFEFF\xA0") ? + function( text ) { + return text == null ? + "" : + core_trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var type, + ret = results || []; + + if ( arr != null ) { + // The window, strings (and functions) also have 'length' + // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 + type = jQuery.type( arr ); + + if ( arr.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( arr ) ) { + core_push.call( ret, arr ); + } else { + jQuery.merge( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + var len; + + if ( arr ) { + if ( core_indexOf ) { + return core_indexOf.call( arr, elem, i ); + } + + len = arr.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in arr && arr[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var l = second.length, + i = first.length, + j = 0; + + if ( typeof l === "number" ) { + for ( ; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var retVal, + ret = [], + i = 0, + length = elems.length; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, key, + ret = [], + i = 0, + length = elems.length, + // jquery objects are treated as arrays + isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( key in elems ) { + value = callback( elems[ key ], key, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return ret.concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var tmp, args, proxy; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = core_slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context, args.concat( core_slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + // Multifunctional method to get and set values of a collection + // The value/s can optionally be executed if it's a function + access: function( elems, fn, key, value, chainable, emptyGet, pass ) { + var exec, + bulk = key == null, + i = 0, + length = elems.length; + + // Sets many values + if ( key && typeof key === "object" ) { + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], 1, emptyGet, value ); + } + chainable = 1; + + // Sets one value + } else if ( value !== undefined ) { + // Optionally, function values get executed if exec is true + exec = pass === undefined && jQuery.isFunction( value ); + + if ( bulk ) { + // Bulk operations only iterate when executing function values + if ( exec ) { + exec = fn; + fn = function( elem, key, value ) { + return exec.call( jQuery( elem ), value ); + }; + + // Otherwise they run against the entire set + } else { + fn.call( elems, value ); + fn = null; + } + } + + if ( fn ) { + for (; i < length; i++ ) { + fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); + } + } + + chainable = 1; + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[0], key ) : emptyGet; + }, + + now: function() { + return ( new Date() ).getTime(); + } +}); + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // we once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready, 1 ); + + // Standards-based browsers support DOMContentLoaded + } else if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", jQuery.ready, false ); + + // If IE event model is used + } else { + // Ensure firing before onload, maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", DOMContentLoaded ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", jQuery.ready ); + + // If IE and not a frame + // continually check to see if the document is ready + var top = false; + + try { + top = window.frameElement == null && document.documentElement; + } catch(e) {} + + if ( top && top.doScroll ) { + (function doScrollCheck() { + if ( !jQuery.isReady ) { + + try { + // Use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + top.doScroll("left"); + } catch(e) { + return setTimeout( doScrollCheck, 50 ); + } + + // and execute any waiting functions + jQuery.ready(); + } + })(); + } + } + } + return readyList.promise( obj ); +}; + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); +// String to Object options format cache +var optionsCache = {}; + +// Convert String-formatted options into Object-formatted ones and store in cache +function createOptions( options ) { + var object = optionsCache[ options ] = {}; + jQuery.each( options.split( core_rspace ), function( _, flag ) { + object[ flag ] = true; + }); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + ( optionsCache[ options ] || createOptions( options ) ) : + jQuery.extend( {}, options ); + + var // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // Flag to know if list is currently firing + firing, + // First callback to fire (used internally by add and fireWith) + firingStart, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = !options.once && [], + // Fire callbacks + fire = function( data ) { + memory = options.memory && data; + fired = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + firing = true; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { + memory = false; // To prevent further calls using add + break; + } + } + firing = false; + if ( list ) { + if ( stack ) { + if ( stack.length ) { + fire( stack.shift() ); + } + } else if ( memory ) { + list = []; + } else { + self.disable(); + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + // First, we save the current length + var start = list.length; + (function add( args ) { + jQuery.each( args, function( _, arg ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { + // Inspect recursively + add( arg ); + } + }); + })( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away + } else if ( memory ) { + firingStart = start; + fire( memory ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + jQuery.each( arguments, function( _, arg ) { + var index; + while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + // Handle firing indexes + if ( firing ) { + if ( index <= firingLength ) { + firingLength--; + } + if ( index <= firingIndex ) { + firingIndex--; + } + } + } + }); + } + return this; + }, + // Control if a given callback is in the list + has: function( fn ) { + return jQuery.inArray( fn, list ) > -1; + }, + // Remove all callbacks from the list + empty: function() { + list = []; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + if ( list && ( !fired || stack ) ) { + if ( firing ) { + stack.push( args ); + } else { + fire( args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; +jQuery.extend({ + + Deferred: function( func ) { + var tuples = [ + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], + [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], + [ "notify", "progress", jQuery.Callbacks("memory") ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred(function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var action = tuple[ 0 ], + fn = fns[ i ]; + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[1] ]( jQuery.isFunction( fn ) ? + function() { + var returned = fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); + } + } : + newDefer[ action ] + ); + }); + fns = null; + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[1] ] = list.add; + + // Handle state + if ( stateString ) { + list.add(function() { + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] = list.fire + deferred[ tuple[0] ] = list.fire; + deferred[ tuple[0] + "With" ] = list.fireWith; + }); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = core_slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; + if( values === progressValues ) { + deferred.notifyWith( contexts, values ); + } else if ( !( --remaining ) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ) + .progress( updateFunc( i, progressContexts, progressValues ) ); + } else { + --remaining; + } + } + } + + // if we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +}); +jQuery.support = (function() { + + var support, + all, + a, + select, + opt, + input, + fragment, + eventName, + i, + isSupported, + clickFn, + div = document.createElement("div"); + + // Setup + div.setAttribute( "className", "t" ); + div.innerHTML = "
    a"; + + // Support tests won't run in some limited or non-browser environments + all = div.getElementsByTagName("*"); + a = div.getElementsByTagName("a")[ 0 ]; + if ( !all || !a || !all.length ) { + return {}; + } + + // First batch of tests + select = document.createElement("select"); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName("input")[ 0 ]; + + a.style.cssText = "top:1px;float:left;opacity:.5"; + support = { + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: ( div.firstChild.nodeType === 3 ), + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName("tbody").length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName("link").length, + + // Get the style information from getAttribute + // (IE uses .cssText instead) + style: /top/.test( a.getAttribute("style") ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: ( a.getAttribute("href") === "/a" ), + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.5/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Make sure that if no value is specified for a checkbox + // that it defaults to "on". + // (WebKit defaults to "" instead) + checkOn: ( input.value === "on" ), + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: opt.selected, + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + getSetAttribute: div.className !== "t", + + // Tests for enctype support on a form (#6743) + enctype: !!document.createElement("form").enctype, + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>", + + // jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode + boxModel: ( document.compatMode === "CSS1Compat" ), + + // Will be defined later + submitBubbles: true, + changeBubbles: true, + focusinBubbles: false, + deleteExpando: true, + noCloneEvent: true, + inlineBlockNeedsLayout: false, + shrinkWrapBlocks: false, + reliableMarginRight: true, + boxSizingReliable: true, + pixelPosition: false + }; + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Test to see if it's possible to delete an expando from an element + // Fails in Internet Explorer + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { + div.attachEvent( "onclick", clickFn = function() { + // Cloning a node shouldn't copy over any + // bound event handlers (IE does this) + support.noCloneEvent = false; + }); + div.cloneNode( true ).fireEvent("onclick"); + div.detachEvent( "onclick", clickFn ); + } + + // Check if a radio maintains its value + // after being appended to the DOM + input = document.createElement("input"); + input.value = "t"; + input.setAttribute( "type", "radio" ); + support.radioValue = input.value === "t"; + + input.setAttribute( "checked", "checked" ); + + // #11217 - WebKit loses check when the name is after the checked attribute + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + fragment = document.createDocumentFragment(); + fragment.appendChild( div.lastChild ); + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + fragment.removeChild( input ); + fragment.appendChild( div ); + + // Technique from Juriy Zaytsev + // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ + // We only care about the case where non-standard event systems + // are used, namely in IE. Short-circuiting here helps us to + // avoid an eval call (in setAttribute) which can cause CSP + // to go haywire. See: https://developer.mozilla.org/en/Security/CSP + if ( div.attachEvent ) { + for ( i in { + submit: true, + change: true, + focusin: true + }) { + eventName = "on" + i; + isSupported = ( eventName in div ); + if ( !isSupported ) { + div.setAttribute( eventName, "return;" ); + isSupported = ( typeof div[ eventName ] === "function" ); + } + support[ i + "Bubbles" ] = isSupported; + } + } + + // Run tests that need a body at doc ready + jQuery(function() { + var container, div, tds, marginDiv, + divReset = "padding:0;margin:0;border:0;display:block;overflow:hidden;", + body = document.getElementsByTagName("body")[0]; + + if ( !body ) { + // Return for frameset docs that don't have a body + return; + } + + container = document.createElement("div"); + container.style.cssText = "visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px"; + body.insertBefore( container, body.firstChild ); + + // Construct the test element + div = document.createElement("div"); + container.appendChild( div ); + + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + // (only IE 8 fails this test) + div.innerHTML = "
    t
    "; + tds = div.getElementsByTagName("td"); + tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none"; + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Check if empty table cells still have offsetWidth/Height + // (IE <= 8 fail this test) + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + + // Check box-sizing and margin behavior + div.innerHTML = ""; + div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;"; + support.boxSizing = ( div.offsetWidth === 4 ); + support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 ); + + // NOTE: To any future maintainer, we've window.getComputedStyle + // because jsdom on node.js will break without it. + if ( window.getComputedStyle ) { + support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; + support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. For more + // info see bug #3333 + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + marginDiv = document.createElement("div"); + marginDiv.style.cssText = div.style.cssText = divReset; + marginDiv.style.marginRight = marginDiv.style.width = "0"; + div.style.width = "1px"; + div.appendChild( marginDiv ); + support.reliableMarginRight = + !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); + } + + if ( typeof div.style.zoom !== "undefined" ) { + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + // (IE < 8 does this) + div.innerHTML = ""; + div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1"; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); + + // Check if elements with layout shrink-wrap their children + // (IE 6 does this) + div.style.display = "block"; + div.style.overflow = "visible"; + div.innerHTML = "
    "; + div.firstChild.style.width = "5px"; + support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); + + container.style.zoom = 1; + } + + // Null elements to avoid leaks in IE + body.removeChild( container ); + container = div = tds = marginDiv = null; + }); + + // Null elements to avoid leaks in IE + fragment.removeChild( div ); + all = a = select = opt = input = fragment = div = null; + + return support; +})(); +var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, + rmultiDash = /([A-Z])/g; + +jQuery.extend({ + cache: {}, + + deletedIds: [], + + // Remove at next major release (1.9/2.0) + uuid: 0, + + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", + "applet": true + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, ret, + internalKey = jQuery.expando, + getByName = typeof name === "string", + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + elem[ internalKey ] = id = jQuery.deletedIds.pop() || jQuery.guid++; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + cache[ id ] = {}; + + // Avoids exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( getByName ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; + }, + + removeData: function( elem, name, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, i, l, + + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split(" "); + } + } + } + + for ( i = 0, l = name.length; i < l; i++ ) { + delete thisCache[ name[i] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject( cache[ id ] ) ) { + return; + } + } + + // Destroy the cache + if ( isNode ) { + jQuery.cleanData( [ elem ], true ); + + // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) + } else if ( jQuery.support.deleteExpando || cache != cache.window ) { + delete cache[ id ]; + + // When all else fails, null + } else { + cache[ id ] = null; + } + }, + + // For internal use only. + _data: function( elem, name, data ) { + return jQuery.data( elem, name, data, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ]; + + // nodes accept data unless otherwise specified; rejection can be conditional + return !noData || noData !== true && elem.getAttribute("classid") === noData; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var parts, part, attr, name, l, + elem = this[0], + i = 0, + data = null; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + attr = elem.attributes; + for ( l = attr.length; i < l; i++ ) { + name = attr[i].name; + + if ( !name.indexOf( "data-" ) ) { + name = jQuery.camelCase( name.substring(5) ); + + dataAttr( elem, name, data[ name ] ); + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + parts = key.split( ".", 2 ); + parts[1] = parts[1] ? "." + parts[1] : ""; + part = parts[1] + "!"; + + return jQuery.access( this, function( value ) { + + if ( value === undefined ) { + data = this.triggerHandler( "getData" + part, [ parts[0] ] ); + + // Try to fetch any internally stored data first + if ( data === undefined && elem ) { + data = jQuery.data( elem, key ); + data = dataAttr( elem, key, data ); + } + + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; + } + + parts[1] = value; + this.each(function() { + var self = jQuery( this ); + + self.triggerHandler( "setData" + part, parts ); + jQuery.data( this, key, value ); + self.triggerHandler( "changeData" + part, parts ); + }); + }, null, value, arguments.length > 1, null, false ); + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + var name; + for ( name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} +jQuery.extend({ + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray(data) ) { + queue = jQuery._data( elem, type, jQuery.makeArray(data) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // not intended for public consumption - generates a queueHooks object, or returns the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return jQuery._data( elem, key ) || jQuery._data( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + jQuery.removeData( elem, type + "queue", true ); + jQuery.removeData( elem, key, true ); + }) + }); + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + // ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while( i-- ) { + tmp = jQuery._data( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +}); +var nodeHook, boolHook, fixSpecified, + rclass = /[\t\r\n]/g, + rreturn = /\r/g, + rtype = /^(?:button|input)$/i, + rfocusable = /^(?:button|input|object|select|textarea)$/i, + rclickable = /^a(?:rea|)$/i, + rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, + getSetAttribute = jQuery.support.getSetAttribute; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classNames, i, l, elem, + setClass, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call(this, j, this.className) ); + }); + } + + if ( value && typeof value === "string" ) { + classNames = value.split( core_rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 ) { + if ( !elem.className && classNames.length === 1 ) { + elem.className = value; + + } else { + setClass = " " + elem.className + " "; + + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + if ( setClass.indexOf( " " + classNames[ c ] + " " ) < 0 ) { + setClass += classNames[ c ] + " "; + } + } + elem.className = jQuery.trim( setClass ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var removes, className, elem, c, cl, i, l; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call(this, j, this.className) ); + }); + } + if ( (value && typeof value === "string") || value === undefined ) { + removes = ( value || "" ).split( core_rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + if ( elem.nodeType === 1 && elem.className ) { + + className = (" " + elem.className + " ").replace( rclass, " " ); + + // loop over each item in the removal list + for ( c = 0, cl = removes.length; c < cl; c++ ) { + // Remove until there is nothing to remove, + while ( className.indexOf(" " + removes[ c ] + " ") >= 0 ) { + className = className.replace( " " + removes[ c ] + " " , " " ); + } + } + elem.className = value ? jQuery.trim( className ) : ""; + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.split( core_rspace ); + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space separated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + } else if ( type === "undefined" || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // toggle whole className + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var hooks, ret, isFunction, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var val, + self = jQuery(this); + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, self.val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // attributes.value is undefined in Blackberry 4.7 but + // uses .value. See #6932 + var val = elem.attributes.value; + return !val || val.specified ? elem.value : elem.text; + } + }, + select: { + get: function( elem ) { + var value, option, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one" || index < 0, + values = one ? null : [], + max = one ? index + 1 : options.length, + i = index < 0 ? + max : + one ? index : 0; + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // oldIE doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + // Don't return options that are disabled or in a disabled optgroup + ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && + ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var values = jQuery.makeArray( value ); + + jQuery(elem).find("option").each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + // Unused in 1.8, left in so attrFn-stabbers won't die; remove in 1.9 + attrFn: {}, + + attr: function( elem, name, value, pass ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( pass && jQuery.isFunction( jQuery.fn[ name ] ) ) { + return jQuery( elem )[ name ]( value ); + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( notxml ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + + } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, value + "" ); + return value; + } + + } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + + ret = elem.getAttribute( name ); + + // Non-existent attributes return null, we normalize to undefined + return ret === null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, value ) { + var propName, attrNames, name, isBool, + i = 0; + + if ( value && elem.nodeType === 1 ) { + + attrNames = value.split( core_rspace ); + + for ( ; i < attrNames.length; i++ ) { + name = attrNames[ i ]; + + if ( name ) { + propName = jQuery.propFix[ name ] || name; + isBool = rboolean.test( name ); + + // See #9699 for explanation of this approach (setting first, then removal) + // Do not do this for boolean attributes (see #10870) + if ( !isBool ) { + jQuery.attr( elem, name, "" ); + } + elem.removeAttribute( getSetAttribute ? name : propName ); + + // Set corresponding property to false for boolean attributes + if ( isBool && propName in elem ) { + elem[ propName ] = false; + } + } + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + // We can't allow the type property to be changed (since it causes problems in IE) + if ( rtype.test( elem.nodeName ) && elem.parentNode ) { + jQuery.error( "type property can't be changed" ); + } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to it's default in case type is set after value + // This is for element creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + }, + // Use the value property for back compat + // Use the nodeHook for button elements in IE6/7 (#1954) + value: { + get: function( elem, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.get( elem, name ); + } + return name in elem ? + elem.value : + null; + }, + set: function( elem, value, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.set( elem, value, name ); + } + // Does not return so that setAttribute is also used + elem.value = value; + } + } + }, + + propFix: { + tabindex: "tabIndex", + readonly: "readOnly", + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + cellpadding: "cellPadding", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder", + contenteditable: "contentEditable" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + return ( elem[ name ] = value ); + } + + } else { + if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + return elem[ name ]; + } + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + var attributeNode = elem.getAttributeNode("tabindex"); + + return attributeNode && attributeNode.specified ? + parseInt( attributeNode.value, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + } + } +}); + +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + // Align boolean attributes with corresponding properties + // Fall back to attribute presence where some booleans are not supported + var attrNode, + property = jQuery.prop( elem, name ); + return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ? + name.toLowerCase() : + undefined; + }, + set: function( elem, value, name ) { + var propName; + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + // value is true since we know at this point it's type boolean and not false + // Set boolean attributes to the same name and set the DOM property + propName = jQuery.propFix[ name ] || name; + if ( propName in elem ) { + // Only set the IDL specifically if it already exists on the element + elem[ propName ] = true; + } + + elem.setAttribute( name, name.toLowerCase() ); + } + return name; + } +}; + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !getSetAttribute ) { + + fixSpecified = { + name: true, + id: true, + coords: true + }; + + // Use this for any attribute in IE6/7 + // This fixes almost every IE6/7 issue + nodeHook = jQuery.valHooks.button = { + get: function( elem, name ) { + var ret; + ret = elem.getAttributeNode( name ); + return ret && ( fixSpecified[ name ] ? ret.value !== "" : ret.specified ) ? + ret.value : + undefined; + }, + set: function( elem, value, name ) { + // Set the existing or create a new attribute node + var ret = elem.getAttributeNode( name ); + if ( !ret ) { + ret = document.createAttribute( name ); + elem.setAttributeNode( ret ); + } + return ( ret.value = value + "" ); + } + }; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }); + }); + + // Set contenteditable to false on removals(#10429) + // Setting to empty string throws an error as an invalid value + jQuery.attrHooks.contenteditable = { + get: nodeHook.get, + set: function( elem, value, name ) { + if ( value === "" ) { + value = "false"; + } + nodeHook.set( elem, value, name ); + } + }; +} + + +// Some attributes require a special call on IE +if ( !jQuery.support.hrefNormalized ) { + jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + get: function( elem ) { + var ret = elem.getAttribute( name, 2 ); + return ret === null ? undefined : ret; + } + }); + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Normalize to lowercase since IE uppercases css property names + return elem.style.cssText.toLowerCase() || undefined; + }, + set: function( elem, value ) { + return ( elem.style.cssText = value + "" ); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + return null; + } + }); +} + +// IE6/7 call enctype encoding +if ( !jQuery.support.enctype ) { + jQuery.propFix.enctype = "encoding"; +} + +// Radios and checkboxes getter/setter +if ( !jQuery.support.checkOn ) { + jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + get: function( elem ) { + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + } + }; + }); +} +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + } + } + }); +}); +var rformElems = /^(?:textarea|input|select)$/i, + rtypenamespace = /^([^\.]*|)(?:\.(.+)|)$/, + rhoverHack = /(?:^|\s)hover(\.\S+|)\b/, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + hoverHack = function( events ) { + return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" ); + }; + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + add: function( elem, types, handler, data, selector ) { + + var elemData, eventHandle, events, + t, tns, type, namespaces, handleObj, + handleObjIn, handlers, special; + + // Don't attach events to noData or text/comment nodes (allow plain objects tho) + if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + events = elemData.events; + if ( !events ) { + elemData.events = events = {}; + } + eventHandle = elemData.handle; + if ( !eventHandle ) { + elemData.handle = eventHandle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = jQuery.trim( hoverHack(types) ).split( " " ); + for ( t = 0; t < types.length; t++ ) { + + tns = rtypenamespace.exec( types[t] ) || []; + type = tns[1]; + namespaces = ( tns[2] || "" ).split( "." ).sort(); + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: tns[1], + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + handlers = events[ type ]; + if ( !handlers ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + global: {}, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var t, tns, type, origType, namespaces, origCount, + j, events, special, eventType, handleObj, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ); + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = jQuery.trim( hoverHack( types || "" ) ).split(" "); + for ( t = 0; t < types.length; t++ ) { + tns = rtypenamespace.exec( types[t] ) || []; + type = origType = tns[1]; + namespaces = tns[2]; + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector? special.delegateType : special.bindType ) || type; + eventType = events[ type ] || []; + origCount = eventType.length; + namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.|)") + "(\\.|$)") : null; + + // Remove matching events + for ( j = 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !namespaces || namespaces.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + eventType.splice( j--, 1 ); + + if ( handleObj.selector ) { + eventType.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( eventType.length === 0 && origCount !== eventType.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery.removeData( elem, "events", true ); + } + }, + + // Events that are safe to short-circuit if no handlers are attached. + // Native DOM events should not be added, they may have inline handlers. + customEvent: { + "getData": true, + "setData": true, + "changeData": true + }, + + trigger: function( event, data, elem, onlyHandlers ) { + // Don't do events on text and comment nodes + if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) { + return; + } + + // Event object or event type + var cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType, + type = event.type || event, + namespaces = []; + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "!" ) >= 0 ) { + // Exclusive events trigger only for the exact event (no namespaces) + type = type.slice(0, -1); + exclusive = true; + } + + if ( type.indexOf( "." ) >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + + if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { + // No jQuery handlers for this event type, and it can't have inline handlers + return; + } + + // Caller can pass in an Event, Object, or just an event type string + event = typeof event === "object" ? + // jQuery.Event object + event[ jQuery.expando ] ? event : + // Object literal + new jQuery.Event( type, event ) : + // Just the event type (string) + new jQuery.Event( type ); + + event.type = type; + event.isTrigger = true; + event.exclusive = exclusive; + event.namespace = namespaces.join( "." ); + event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)") : null; + ontype = type.indexOf( ":" ) < 0 ? "on" + type : ""; + + // Handle a global trigger + if ( !elem ) { + + // TODO: Stop taunting the data cache; remove global events and always attach to document + cache = jQuery.cache; + for ( i in cache ) { + if ( cache[ i ].events && cache[ i ].events[ type ] ) { + jQuery.event.trigger( event, data, cache[ i ].handle.elem, true ); + } + } + return; + } + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data != null ? jQuery.makeArray( data ) : []; + data.unshift( event ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + eventPath = [[ elem, special.bindType || type ]]; + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode; + for ( old = elem; cur; cur = cur.parentNode ) { + eventPath.push([ cur, bubbleType ]); + old = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( old === (elem.ownerDocument || document) ) { + eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]); + } + } + + // Fire handlers on the event path + for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) { + + cur = eventPath[i][0]; + event.type = eventPath[i][1]; + + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + // Note that this is a bare JS function and not a jQuery handler + handle = ontype && cur[ ontype ]; + if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { + event.preventDefault(); + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && + !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + // IE<9 dies on focus/blur to hidden element (#1486) + if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + old = elem[ ontype ]; + + if ( old ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + elem[ type ](); + jQuery.event.triggered = undefined; + + if ( old ) { + elem[ ontype ] = old; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event || window.event ); + + var i, j, cur, ret, selMatch, matched, matches, handleObj, sel, related, + handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []), + delegateCount = handlers.delegateCount, + args = core_slice.call( arguments ), + run_all = !event.exclusive && !event.namespace, + special = jQuery.event.special[ event.type ] || {}, + handlerQueue = []; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers that should run if there are delegated events + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && !(event.button && event.type === "click") ) { + + for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { + + // Don't process clicks (ONLY) on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.disabled !== true || event.type !== "click" ) { + selMatch = {}; + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + sel = handleObj.selector; + + if ( selMatch[ sel ] === undefined ) { + selMatch[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( selMatch[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, matches: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( handlers.length > delegateCount ) { + handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) }); + } + + // Run delegates first; they may want to stop propagation beneath us + for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) { + matched = handlerQueue[ i ]; + event.currentTarget = matched.elem; + + for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) { + handleObj = matched.matches[ j ]; + + // Triggered event must either 1) be non-exclusive and have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) { + + event.data = handleObj.data; + event.handleObj = handleObj; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + event.result = ret; + if ( ret === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 *** + props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, + originalEvent = event, + fixHook = jQuery.event.fixHooks[ event.type ] || {}, + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = jQuery.Event( originalEvent ); + + for ( i = copy.length; i; ) { + prop = copy[ --i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Target should not be a text node (#504, Safari) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // For mouse/key events, metaKey==false if it's undefined (#3368, #11328; IE6/7/8) + event.metaKey = !!event.metaKey; + + return fixHook.filter? fixHook.filter( event, originalEvent ) : event; + }, + + special: { + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + + focus: { + delegateType: "focusin" + }, + blur: { + delegateType: "focusout" + }, + + beforeunload: { + setup: function( data, namespaces, eventHandle ) { + // We only want to do this special case on windows + if ( jQuery.isWindow( this ) ) { + this.onbeforeunload = eventHandle; + } + }, + + teardown: function( namespaces, eventHandle ) { + if ( this.onbeforeunload === eventHandle ) { + this.onbeforeunload = null; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +// Some plugins are using, but it's undocumented/deprecated and will be removed. +// The 1.7 special event interface should provide all the hooks needed now. +jQuery.event.handle = jQuery.event.dispatch; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + var name = "on" + type; + + if ( elem.detachEvent ) { + + // #8545, #7054, preventing memory leaks for custom events in IE6-8 + // detachEvent needed property on element, by name of that event, to properly expose it to GC + if ( typeof elem[ name ] === "undefined" ) { + elem[ name ] = null; + } + + elem.detachEvent( name, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +function returnFalse() { + return false; +} +function returnTrue() { + return true; +} + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + preventDefault: function() { + this.isDefaultPrevented = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + + // if preventDefault exists run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // otherwise set the returnValue property of the original event to false (IE) + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + this.isPropagationStopped = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + // if stopPropagation exists run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + // otherwise set the cancelBubble property of the original event to true (IE) + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj, + selector = handleObj.selector; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// IE submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !jQuery._data( form, "_submit_attached" ) ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submit_bubble = true; + }); + jQuery._data( form, "_submit_attached", true ); + } + }); + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + // If form was submitted by the user, bubble the event up the tree + if ( event._submit_bubble ) { + delete event._submit_bubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + } + }, + + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !jQuery.support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed && !event.isTrigger ) { + this._just_changed = false; + } + // Allow triggered, simulated change events (#11500) + jQuery.event.simulate( "change", this, event, true ); + }); + } + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "_change_attached" ) ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + jQuery._data( elem, "_change_attached", true ); + } + }); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return !rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0, + handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { // && selector != null + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + bind: function( types, data, fn ) { + return this.on( types, null, data, fn ); + }, + unbind: function( types, fn ) { + return this.off( types, null, fn ); + }, + + live: function( types, data, fn ) { + jQuery( this.context ).on( types, this.selector, data, fn ); + return this; + }, + die: function( types, fn ) { + jQuery( this.context ).off( types, this.selector || "**", fn ); + return this; + }, + + delegate: function( selector, types, data, fn ) { + return this.on( types, selector, data, fn ); + }, + undelegate: function( selector, types, fn ) { + // ( namespace ) or ( selector, types [, fn] ) + return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn ); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + if ( this[0] ) { + return jQuery.event.trigger( type, data, this[0], true ); + } + }, + + toggle: function( fn ) { + // Save reference to arguments for access in closure + var args = arguments, + guid = fn.guid || jQuery.guid++, + i = 0, + toggler = function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + }; + + // link all the functions, so any of them can unbind this click handler + toggler.guid = guid; + while ( i < args.length ) { + args[ i++ ].guid = guid; + } + + return this.click( toggler ); + }, + + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +}); + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.on( name, null, data, fn ) : + this.trigger( name ); + }; + + if ( rkeyEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks; + } + + if ( rmouseEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; + } +}); +/*! + * Sizzle CSS Selector Engine + * Copyright 2012 jQuery Foundation and other contributors + * Released under the MIT license + * http://sizzlejs.com/ + */ +(function( window, undefined ) { + +var cachedruns, + assertGetIdNotName, + Expr, + getText, + isXML, + contains, + compile, + sortOrder, + hasDuplicate, + outermostContext, + + baseHasDuplicate = true, + strundefined = "undefined", + + expando = ( "sizcache" + Math.random() ).replace( ".", "" ), + + Token = String, + document = window.document, + docElem = document.documentElement, + dirruns = 0, + done = 0, + pop = [].pop, + push = [].push, + slice = [].slice, + // Use a stripped-down indexOf if a native one is unavailable + indexOf = [].indexOf || function( elem ) { + var i = 0, + len = this.length; + for ( ; i < len; i++ ) { + if ( this[i] === elem ) { + return i; + } + } + return -1; + }, + + // Augment a function for special use by Sizzle + markFunction = function( fn, value ) { + fn[ expando ] = value == null || value; + return fn; + }, + + createCache = function() { + var cache = {}, + keys = []; + + return markFunction(function( key, value ) { + // Only keep the most recent entries + if ( keys.push( key ) > Expr.cacheLength ) { + delete cache[ keys.shift() ]; + } + + // Retrieve with (key + " ") to avoid collision with native Object.prototype properties (see Issue #157) + return (cache[ key + " " ] = value); + }, cache ); + }, + + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + + // Regex + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors) + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors + operators = "([*^$|!~]?=)", + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + + "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", + + // Prefer arguments not in parens/brackets, + // then attribute selectors and non-pseudos (denoted by :), + // then anything else + // These preferences are here to reduce the number of selectors + // needing tokenize in the PSEUDO preFilter + pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:" + attributes + ")|[^:]|\\\\.)*|.*))\\)|)", + + // For matchExpr.POS and matchExpr.needsContext + pos = ":(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ), + rpseudo = new RegExp( pseudos ), + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/, + + rnot = /^:not/, + rsibling = /[\x20\t\r\n\f]*[+~]/, + rendsWithNot = /:not\($/, + + rheader = /h\d/i, + rinputs = /input|select|textarea|button/i, + + rbackslash = /\\(?!\\)/g, + + matchExpr = { + "ID": new RegExp( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "POS": new RegExp( pos, "i" ), + "CHILD": new RegExp( "^:(only|nth|first|last)-child(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + // For use in libraries implementing .is() + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" ) + }, + + // Support + + // Used for testing something on an element + assert = function( fn ) { + var div = document.createElement("div"); + + try { + return fn( div ); + } catch (e) { + return false; + } finally { + // release memory in IE + div = null; + } + }, + + // Check if getElementsByTagName("*") returns only elements + assertTagNameNoComments = assert(function( div ) { + div.appendChild( document.createComment("") ); + return !div.getElementsByTagName("*").length; + }), + + // Check if getAttribute returns normalized href attributes + assertHrefNotNormalized = assert(function( div ) { + div.innerHTML = ""; + return div.firstChild && typeof div.firstChild.getAttribute !== strundefined && + div.firstChild.getAttribute("href") === "#"; + }), + + // Check if attributes should be retrieved by attribute nodes + assertAttributes = assert(function( div ) { + div.innerHTML = ""; + var type = typeof div.lastChild.getAttribute("multiple"); + // IE8 returns a string for some attributes even when not present + return type !== "boolean" && type !== "string"; + }), + + // Check if getElementsByClassName can be trusted + assertUsableClassName = assert(function( div ) { + // Opera can't find a second classname (in 9.6) + div.innerHTML = ""; + if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) { + return false; + } + + // Safari 3.2 caches class attributes and doesn't catch changes + div.lastChild.className = "e"; + return div.getElementsByClassName("e").length === 2; + }), + + // Check if getElementById returns elements by name + // Check if getElementsByName privileges form controls or returns elements by ID + assertUsableName = assert(function( div ) { + // Inject content + div.id = expando + 0; + div.innerHTML = "
    "; + docElem.insertBefore( div, docElem.firstChild ); + + // Test + var pass = document.getElementsByName && + // buggy browsers will return fewer than the correct 2 + document.getElementsByName( expando ).length === 2 + + // buggy browsers will return more than the correct 0 + document.getElementsByName( expando + 0 ).length; + assertGetIdNotName = !document.getElementById( expando ); + + // Cleanup + docElem.removeChild( div ); + + return pass; + }); + +// If slice is not available, provide a backup +try { + slice.call( docElem.childNodes, 0 )[0].nodeType; +} catch ( e ) { + slice = function( i ) { + var elem, + results = []; + for ( ; (elem = this[i]); i++ ) { + results.push( elem ); + } + return results; + }; +} + +function Sizzle( selector, context, results, seed ) { + results = results || []; + context = context || document; + var match, elem, xml, m, + nodeType = context.nodeType; + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + if ( nodeType !== 1 && nodeType !== 9 ) { + return []; + } + + xml = isXML( context ); + + if ( !xml && !seed ) { + if ( (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) { + push.apply( results, slice.call(context.getElementsByClassName( m ), 0) ); + return results; + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed, xml ); +} + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + return Sizzle( expr, null, null, [ elem ] ).length > 0; +}; + +// Returns a function to use in pseudos for input types +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +// Returns a function to use in pseudos for buttons +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +// Returns a function to use in pseudos for positionals +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( nodeType ) { + if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (see #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + } else { + + // If no nodeType, this is expected to be an array + for ( ; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } + return ret; +}; + +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +// Element contains another +contains = Sizzle.contains = docElem.contains ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) ); + } : + docElem.compareDocumentPosition ? + function( a, b ) { + return b && !!( a.compareDocumentPosition( b ) & 16 ); + } : + function( a, b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + return false; + }; + +Sizzle.attr = function( elem, name ) { + var val, + xml = isXML( elem ); + + if ( !xml ) { + name = name.toLowerCase(); + } + if ( (val = Expr.attrHandle[ name ]) ) { + return val( elem ); + } + if ( xml || assertAttributes ) { + return elem.getAttribute( name ); + } + val = elem.getAttributeNode( name ); + return val ? + typeof elem[ name ] === "boolean" ? + elem[ name ] ? name : null : + val.specified ? val.value : null : + null; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + // IE6/7 return a modified href + attrHandle: assertHrefNotNormalized ? + {} : + { + "href": function( elem ) { + return elem.getAttribute( "href", 2 ); + }, + "type": function( elem ) { + return elem.getAttribute("type"); + } + }, + + find: { + "ID": assertGetIdNotName ? + function( id, context, xml ) { + if ( typeof context.getElementById !== strundefined && !xml ) { + var m = context.getElementById( id ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + } : + function( id, context, xml ) { + if ( typeof context.getElementById !== strundefined && !xml ) { + var m = context.getElementById( id ); + + return m ? + m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ? + [m] : + undefined : + []; + } + }, + + "TAG": assertTagNameNoComments ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== strundefined ) { + return context.getElementsByTagName( tag ); + } + } : + function( tag, context ) { + var results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + var elem, + tmp = [], + i = 0; + + for ( ; (elem = results[i]); i++ ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }, + + "NAME": assertUsableName && function( tag, context ) { + if ( typeof context.getElementsByName !== strundefined ) { + return context.getElementsByName( name ); + } + }, + + "CLASS": assertUsableClassName && function( className, context, xml ) { + if ( typeof context.getElementsByClassName !== strundefined && !xml ) { + return context.getElementsByClassName( className ); + } + } + }, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( rbackslash, "" ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 3 xn-component of xn+y argument ([+-]?\d*n|) + 4 sign of xn-component + 5 x of xn-component + 6 sign of y-component + 7 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1] === "nth" ) { + // nth-child requires argument + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) ); + match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" ); + + // other types prohibit arguments + } else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var unquoted, excess; + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + if ( match[3] ) { + match[2] = match[3]; + } else if ( (unquoted = match[4]) ) { + // Only check arguments that contain a pseudo + if ( rpseudo.test(unquoted) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + unquoted = unquoted.slice( 0, excess ); + match[0] = match[0].slice( 0, excess ); + } + match[2] = unquoted; + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + "ID": assertGetIdNotName ? + function( id ) { + id = id.replace( rbackslash, "" ); + return function( elem ) { + return elem.getAttribute("id") === id; + }; + } : + function( id ) { + id = id.replace( rbackslash, "" ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + return node && node.value === id; + }; + }, + + "TAG": function( nodeName ) { + if ( nodeName === "*" ) { + return function() { return true; }; + } + nodeName = nodeName.replace( rbackslash, "" ).toLowerCase(); + + return function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ expando ][ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem, context ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.substr( result.length - check.length ) === check : + operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.substr( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, argument, first, last ) { + + if ( type === "nth" ) { + return function( elem ) { + var node, diff, + parent = elem.parentNode; + + if ( first === 1 && last === 0 ) { + return true; + } + + if ( parent ) { + diff = 0; + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + diff++; + if ( elem === node ) { + break; + } + } + } + } + + // Incorporate the offset (or cast to NaN), then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + }; + } + + return function( elem ) { + var node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + /* falls through */ + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf.call( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), + // not comment, processing instructions, or others + // Thanks to Diego Perini for the nodeName shortcut + // Greater than "@" means alpha characters (specifically not starting with "#" or "?") + var nodeType; + elem = elem.firstChild; + while ( elem ) { + if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) { + return false; + } + elem = elem.nextSibling; + } + return true; + }, + + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "text": function( elem ) { + var type, attr; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && + (type = elem.type) === "text" && + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type ); + }, + + // Input types + "radio": createInputPseudo("radio"), + "checkbox": createInputPseudo("checkbox"), + "file": createInputPseudo("file"), + "password": createInputPseudo("password"), + "image": createInputPseudo("image"), + + "submit": createButtonPseudo("submit"), + "reset": createButtonPseudo("reset"), + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "focus": function( elem ) { + var doc = elem.ownerDocument; + return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + "active": function( elem ) { + return elem === elem.ownerDocument.activeElement; + }, + + // Positional types + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + for ( var i = 0; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + for ( var i = 1; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + for ( var i = argument < 0 ? argument + length : argument; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + for ( var i = argument < 0 ? argument + length : argument; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +function siblingCheck( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; +} + +sortOrder = docElem.compareDocumentPosition ? + function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + return ( !a.compareDocumentPosition || !b.compareDocumentPosition ? + a.compareDocumentPosition : + a.compareDocumentPosition(b) & 4 + ) ? -1 : 1; + } : + function( a, b ) { + // The nodes are identical, we can exit early + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Fallback to using sourceIndex (in IE) if it's available on both nodes + } else if ( a.sourceIndex && b.sourceIndex ) { + return a.sourceIndex - b.sourceIndex; + } + + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + // If the nodes are siblings (or identical) we can do a quick check + if ( aup === bup ) { + return siblingCheck( a, b ); + + // If no parents were found then the nodes are disconnected + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + // Otherwise they're somewhere else in the tree so we need + // to build up a full list of the parentNodes for comparison + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + // Start walking down the tree looking for a discrepancy + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + // We ended someplace up the tree so do a sibling check + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + +// Always assume the presence of duplicates if sort doesn't +// pass them to our comparison function (as in Google Chrome). +[0, 0].sort( sortOrder ); +baseHasDuplicate = !hasDuplicate; + +// Document sorting and removing duplicates +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + i = 1, + j = 0; + + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( ; (elem = results[i]); i++ ) { + if ( elem === results[ i - 1 ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + return results; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +function tokenize( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ expando ][ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( tokens = [] ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + tokens.push( matched = new Token( match.shift() ) ); + soFar = soFar.slice( matched.length ); + + // Cast descendant combinators to space + matched.type = match[0].replace( rtrim, " " ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + + tokens.push( matched = new Token( match.shift() ) ); + soFar = soFar.slice( matched.length ); + matched.type = type; + matched.matches = match; + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && combinator.dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( checkNonElements || elem.nodeType === 1 ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if ( !xml ) { + var cache, + dirkey = dirruns + " " + doneName + " ", + cachedkey = dirkey + cachedruns; + while ( (elem = elem[ dir ]) ) { + if ( checkNonElements || elem.nodeType === 1 ) { + if ( (cache = elem[ expando ]) === cachedkey ) { + return elem.sizset; + } else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) { + if ( elem.sizset ) { + return elem; + } + } else { + elem[ expando ] = cachedkey; + if ( matcher( elem, context, xml ) ) { + elem.sizset = true; + return elem; + } + elem.sizset = false; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( checkNonElements || elem.nodeType === 1 ) { + if ( matcher( elem, context, xml ) ) { + return elem; + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && tokens.slice( 0, i - 1 ).join("").replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && tokens.join("") + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, expandContext ) { + var elem, j, matcher, + setMatched = [], + matchedCount = 0, + i = "0", + unmatched = seed && [], + outermost = expandContext != null, + contextBackup = outermostContext, + // We must always have either seed elements or context + elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), + // Nested matchers should use non-integer dirruns + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.E); + + if ( outermost ) { + outermostContext = context !== document && context; + cachedruns = superMatcher.el; + } + + // Add elements passing elementMatchers directly to results + for ( ; (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + for ( j = 0; (matcher = elementMatchers[j]); j++ ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + cachedruns = ++superMatcher.el; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + if ( bySet && i !== matchedCount ) { + for ( j = 0; (matcher = setMatchers[j]); j++ ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + superMatcher.el = 0; + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ expando ][ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !group ) { + group = tokenize( selector ); + } + i = group.length; + while ( i-- ) { + cached = matcherFromTokens( group[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + } + return cached; +}; + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function select( selector, context, results, seed, xml ) { + var i, tokens, token, type, find, + match = tokenize( selector ), + j = match.length; + + if ( !seed ) { + // Try to minimize operations if there is only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + context.nodeType === 9 && !xml && + Expr.relative[ tokens[1].type ] ) { + + context = Expr.find["ID"]( token.matches[0].replace( rbackslash, "" ), context, xml )[0]; + if ( !context ) { + return results; + } + + selector = selector.slice( tokens.shift().length ); + } + + // Fetch a seed set for right-to-left matching + for ( i = matchExpr["POS"].test( selector ) ? -1 : tokens.length - 1; i >= 0; i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( rbackslash, "" ), + rsibling.test( tokens[0].type ) && context.parentNode || context, + xml + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && tokens.join(""); + if ( !selector ) { + push.apply( results, slice.call( seed, 0 ) ); + return results; + } + + break; + } + } + } + } + } + + // Compile and execute a filtering function + // Provide `match` to avoid retokenization if we modified the selector above + compile( selector, match )( + seed, + context, + xml, + results, + rsibling.test( selector ) + ); + return results; +} + +if ( document.querySelectorAll ) { + (function() { + var disconnectedMatch, + oldSelect = select, + rescape = /'|\\/g, + rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g, + + // qSa(:focus) reports false when true (Chrome 21), no need to also add to buggyMatches since matches checks buggyQSA + // A support test would require too much code (would include document ready) + rbuggyQSA = [ ":focus" ], + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + // A support test would require too much code (would include document ready) + // just skip matchesSelector for :active + rbuggyMatches = [ ":active" ], + matches = docElem.matchesSelector || + docElem.mozMatchesSelector || + docElem.webkitMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector; + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explictly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = ""; + + // IE8 - Some boolean attributes are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here (do not put tests after this one) + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + }); + + assert(function( div ) { + + // Opera 10-12/IE9 - ^= $= *= and empty values + // Should not select anything + div.innerHTML = "

    "; + if ( div.querySelectorAll("[test^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here (do not put tests after this one) + div.innerHTML = ""; + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push(":enabled", ":disabled"); + } + }); + + // rbuggyQSA always contains :focus, so no need for a length check + rbuggyQSA = /* rbuggyQSA.length && */ new RegExp( rbuggyQSA.join("|") ); + + select = function( selector, context, results, seed, xml ) { + // Only use querySelectorAll when not filtering, + // when this is not xml, + // and when no QSA bugs apply + if ( !seed && !xml && !rbuggyQSA.test( selector ) ) { + var groups, i, + old = true, + nid = expando, + newContext = context, + newSelector = context.nodeType === 9 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\//JQUERY_SOURCE" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + groups[i].join(""); + } + newContext = rsibling.test( selector ) && context.parentNode || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, slice.call( newContext.querySelectorAll( + newSelector + ), 0 ) ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + context.removeAttribute("id"); + } + } + } + } + + return oldSelect( selector, context, results, seed, xml ); + }; + + if ( matches ) { + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + try { + matches.call( div, "[test!='']:sizzle" ); + rbuggyMatches.push( "!=", pseudos ); + } catch ( e ) {} + }); + + // rbuggyMatches always contains :active and :focus, so no need for a length check + rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") ); + + Sizzle.matchesSelector = function( elem, expr ) { + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + // rbuggyMatches always contains :active, so no need for an existence check + if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && !rbuggyQSA.test( expr ) ) { + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch(e) {} + } + + return Sizzle( expr, null, null, [ elem ] ).length > 0; + }; + } + })(); +} + +// Deprecated +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Back-compat +function setFilters() {} +Expr.filters = setFilters.prototype = Expr.pseudos; +Expr.setFilters = new setFilters(); + +// Override sizzle attribute retrieval +Sizzle.attr = jQuery.attr; +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.pseudos; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})( window ); +var runtil = /Until$/, + rparentsprev = /^(?:parents|prev(?:Until|All))/, + isSimple = /^.[^:#\[\.,]*$/, + rneedsContext = jQuery.expr.match.needsContext, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var i, l, length, n, r, ret, + self = this; + + if ( typeof selector !== "string" ) { + return jQuery( selector ).filter(function() { + for ( i = 0, l = self.length; i < l; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }); + } + + ret = this.pushStack( "", "find", selector ); + + for ( i = 0, l = this.length; i < l; i++ ) { + length = ret.length; + jQuery.find( selector, this[i], ret ); + + if ( i > 0 ) { + // Make sure that the results are unique + for ( n = length; n < ret.length; n++ ) { + for ( r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + ret.splice(n--, 1); + break; + } + } + } + } + } + + return ret; + }, + + has: function( target ) { + var i, + targets = jQuery( target, this ), + len = targets.length; + + return this.filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector, false), "not", selector); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }, + + is: function( selector ) { + return !!selector && ( + typeof selector === "string" ? + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + rneedsContext.test( selector ) ? + jQuery( selector, this.context ).index( this[0] ) >= 0 : + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + ret = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + cur = this[i]; + + while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) { + if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { + ret.push( cur ); + break; + } + cur = cur.parentNode; + } + } + + ret = ret.length > 1 ? jQuery.unique( ret ) : ret; + + return this.pushStack( ret, "closest", selectors ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? + all : + jQuery.unique( all ) ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter(selector) + ); + } +}); + +jQuery.fn.andSelf = jQuery.fn.addBack; + +// A painfully simple check to see if an element is disconnected +// from a document (should be improved, where feasible). +function isDisconnected( node ) { + return !node || !node.parentNode || node.parentNode.nodeType === 11; +} + +function sibling( cur, dir ) { + do { + cur = cur[ dir ]; + } while ( cur && cur.nodeType !== 1 ); + + return cur; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; + + if ( this.length > 1 && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret, name, core_slice.call( arguments ).join(",") ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 ? + jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : + jQuery.find.matches(expr, elems); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, keep ) { + + // Can't pass null or undefined to indexOf in Firefox 4 + // Set to 0 to skip string check + qualifier = qualifier || 0; + + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep(elements, function( elem, i ) { + var retVal = !!qualifier.call( elem, i, elem ); + return retVal === keep; + }); + + } else if ( qualifier.nodeType ) { + return jQuery.grep(elements, function( elem, i ) { + return ( elem === qualifier ) === keep; + }); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep(elements, function( elem ) { + return elem.nodeType === 1; + }); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter(qualifier, filtered, !keep); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep(elements, function( elem, i ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; + }); +} +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", + rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rtbody = /]", "i"), + rcheckableType = /^(?:checkbox|radio)$/, + // checked="checked" or checked + rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, + rscriptType = /\/(java|ecma)script/i, + rcleanScript = /^\s*\s*$/g, + wrapMap = { + option: [ 1, "" ], + legend: [ 1, "
    ", "
    " ], + thead: [ 1, "", "
    " ], + tr: [ 2, "", "
    " ], + td: [ 3, "", "
    " ], + col: [ 2, "", "
    " ], + area: [ 1, "", "" ], + _default: [ 0, "", "" ] + }, + safeFragment = createSafeFragment( document ), + fragmentDiv = safeFragment.appendChild( document.createElement("div") ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, +// unless wrapped in a div with non-breaking characters in front of it. +if ( !jQuery.support.htmlSerialize ) { + wrapMap._default = [ 1, "X
    ", "
    " ]; +} + +jQuery.fn.extend({ + text: function( value ) { + return jQuery.access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); + }, null, value, arguments.length ); + }, + + wrapAll: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapAll( html.call(this, i) ); + }); + } + + if ( this[0] ) { + // The elements to wrap the target around + var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true); + + if ( this[0].parentNode ) { + wrap.insertBefore( this[0] ); + } + + wrap.map(function() { + var elem = this; + + while ( elem.firstChild && elem.firstChild.nodeType === 1 ) { + elem = elem.firstChild; + } + + return elem; + }).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapInner( html.call(this, i) ); + }); + } + + return this.each(function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + }); + }, + + wrap: function( html ) { + var isFunction = jQuery.isFunction( html ); + + return this.each(function(i) { + jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); + }); + }, + + unwrap: function() { + return this.parent().each(function() { + if ( !jQuery.nodeName( this, "body" ) ) { + jQuery( this ).replaceWith( this.childNodes ); + } + }).end(); + }, + + append: function() { + return this.domManip(arguments, true, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 ) { + this.appendChild( elem ); + } + }); + }, + + prepend: function() { + return this.domManip(arguments, true, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 ) { + this.insertBefore( elem, this.firstChild ); + } + }); + }, + + before: function() { + if ( !isDisconnected( this[0] ) ) { + return this.domManip(arguments, false, function( elem ) { + this.parentNode.insertBefore( elem, this ); + }); + } + + if ( arguments.length ) { + var set = jQuery.clean( arguments ); + return this.pushStack( jQuery.merge( set, this ), "before", this.selector ); + } + }, + + after: function() { + if ( !isDisconnected( this[0] ) ) { + return this.domManip(arguments, false, function( elem ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + }); + } + + if ( arguments.length ) { + var set = jQuery.clean( arguments ); + return this.pushStack( jQuery.merge( this, set ), "after", this.selector ); + } + }, + + // keepData is for internal use only--do not document + remove: function( selector, keepData ) { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + if ( !selector || jQuery.filter( selector, [ elem ] ).length ) { + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( elem.getElementsByTagName("*") ); + jQuery.cleanData( [ elem ] ); + } + + if ( elem.parentNode ) { + elem.parentNode.removeChild( elem ); + } + } + } + + return this; + }, + + empty: function() { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( elem.getElementsByTagName("*") ); + } + + // Remove any remaining nodes + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function () { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }, + + html: function( value ) { + return jQuery.access( this, function( value ) { + var elem = this[0] || {}, + i = 0, + l = this.length; + + if ( value === undefined ) { + return elem.nodeType === 1 ? + elem.innerHTML.replace( rinlinejQuery, "" ) : + undefined; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) && + ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && + !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) { + + value = value.replace( rxhtmlTag, "<$1>" ); + + try { + for (; i < l; i++ ) { + // Remove element nodes and prevent memory leaks + elem = this[i] || {}; + if ( elem.nodeType === 1 ) { + jQuery.cleanData( elem.getElementsByTagName( "*" ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch(e) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function( value ) { + if ( !isDisconnected( this[0] ) ) { + // Make sure that the elements are removed from the DOM before they are inserted + // this can help fix replacing a parent with child elements + if ( jQuery.isFunction( value ) ) { + return this.each(function(i) { + var self = jQuery(this), old = self.html(); + self.replaceWith( value.call( this, i, old ) ); + }); + } + + if ( typeof value !== "string" ) { + value = jQuery( value ).detach(); + } + + return this.each(function() { + var next = this.nextSibling, + parent = this.parentNode; + + jQuery( this ).remove(); + + if ( next ) { + jQuery(next).before( value ); + } else { + jQuery(parent).append( value ); + } + }); + } + + return this.length ? + this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) : + this; + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, table, callback ) { + + // Flatten any nested arrays + args = [].concat.apply( [], args ); + + var results, first, fragment, iNoClone, + i = 0, + value = args[0], + scripts = [], + l = this.length; + + // We can't cloneNode fragments that contain checked, in WebKit + if ( !jQuery.support.checkClone && l > 1 && typeof value === "string" && rchecked.test( value ) ) { + return this.each(function() { + jQuery(this).domManip( args, table, callback ); + }); + } + + if ( jQuery.isFunction(value) ) { + return this.each(function(i) { + var self = jQuery(this); + args[0] = value.call( this, i, table ? self.html() : undefined ); + self.domManip( args, table, callback ); + }); + } + + if ( this[0] ) { + results = jQuery.buildFragment( args, this, scripts ); + fragment = results.fragment; + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + if ( first ) { + table = table && jQuery.nodeName( first, "tr" ); + + // Use the original fragment for the last item instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + // Fragments from the fragment cache must always be cloned and never used in place. + for ( iNoClone = results.cacheable || l - 1; i < l; i++ ) { + callback.call( + table && jQuery.nodeName( this[i], "table" ) ? + findOrAppend( this[i], "tbody" ) : + this[i], + i === iNoClone ? + fragment : + jQuery.clone( fragment, true, true ) + ); + } + } + + // Fix #11809: Avoid leaking memory + fragment = first = null; + + if ( scripts.length ) { + jQuery.each( scripts, function( i, elem ) { + if ( elem.src ) { + if ( jQuery.ajax ) { + jQuery.ajax({ + url: elem.src, + type: "GET", + dataType: "script", + async: false, + global: false, + "throws": true + }); + } else { + jQuery.error("no ajax"); + } + } else { + jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "" ) ); + } + + if ( elem.parentNode ) { + elem.parentNode.removeChild( elem ); + } + }); + } + } + + return this; + } +}); + +function findOrAppend( elem, tag ) { + return elem.getElementsByTagName( tag )[0] || elem.appendChild( elem.ownerDocument.createElement( tag ) ); +} + +function cloneCopyEvent( src, dest ) { + + if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { + return; + } + + var type, i, l, + oldData = jQuery._data( src ), + curData = jQuery._data( dest, oldData ), + events = oldData.events; + + if ( events ) { + delete curData.handle; + curData.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + + // make the cloned public data object a copy from the original + if ( curData.data ) { + curData.data = jQuery.extend( {}, curData.data ); + } +} + +function cloneFixAttributes( src, dest ) { + var nodeName; + + // We do not need to do anything for non-Elements + if ( dest.nodeType !== 1 ) { + return; + } + + // clearAttributes removes the attributes, which we don't want, + // but also removes the attachEvent events, which we *do* want + if ( dest.clearAttributes ) { + dest.clearAttributes(); + } + + // mergeAttributes, in contrast, only merges back on the + // original attributes, not the events + if ( dest.mergeAttributes ) { + dest.mergeAttributes( src ); + } + + nodeName = dest.nodeName.toLowerCase(); + + if ( nodeName === "object" ) { + // IE6-10 improperly clones children of object elements using classid. + // IE10 throws NoModificationAllowedError if parent is null, #12132. + if ( dest.parentNode ) { + dest.outerHTML = src.outerHTML; + } + + // This path appears unavoidable for IE9. When cloning an object + // element in IE9, the outerHTML strategy above is not sufficient. + // If the src has innerHTML and the destination does not, + // copy the src.innerHTML into the dest.innerHTML. #10324 + if ( jQuery.support.html5Clone && (src.innerHTML && !jQuery.trim(dest.innerHTML)) ) { + dest.innerHTML = src.innerHTML; + } + + } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + // IE6-8 fails to persist the checked state of a cloned checkbox + // or radio button. Worse, IE6-7 fail to give the cloned element + // a checked appearance if the defaultChecked value isn't also set + + dest.defaultChecked = dest.checked = src.checked; + + // IE6-7 get confused and end up setting the value of a cloned + // checkbox/radio button to an empty string instead of "on" + if ( dest.value !== src.value ) { + dest.value = src.value; + } + + // IE6-8 fails to return the selected option to the default selected + // state when cloning options + } else if ( nodeName === "option" ) { + dest.selected = src.defaultSelected; + + // IE6-8 fails to set the defaultValue to the correct value when + // cloning other types of input fields + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + + // IE blanks contents when cloning scripts + } else if ( nodeName === "script" && dest.text !== src.text ) { + dest.text = src.text; + } + + // Event data gets referenced instead of copied if the expando + // gets copied too + dest.removeAttribute( jQuery.expando ); +} + +jQuery.buildFragment = function( args, context, scripts ) { + var fragment, cacheable, cachehit, + first = args[ 0 ]; + + // Set context from what may come in as undefined or a jQuery collection or a node + // Updated to fix #12266 where accessing context[0] could throw an exception in IE9/10 & + // also doubles as fix for #8950 where plain objects caused createDocumentFragment exception + context = context || document; + context = !context.nodeType && context[0] || context; + context = context.ownerDocument || context; + + // Only cache "small" (1/2 KB) HTML strings that are associated with the main document + // Cloning options loses the selected state, so don't cache them + // IE 6 doesn't like it when you put or elements in a fragment + // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache + // Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501 + if ( args.length === 1 && typeof first === "string" && first.length < 512 && context === document && + first.charAt(0) === "<" && !rnocache.test( first ) && + (jQuery.support.checkClone || !rchecked.test( first )) && + (jQuery.support.html5Clone || !rnoshimcache.test( first )) ) { + + // Mark cacheable and look for a hit + cacheable = true; + fragment = jQuery.fragments[ first ]; + cachehit = fragment !== undefined; + } + + if ( !fragment ) { + fragment = context.createDocumentFragment(); + jQuery.clean( args, context, fragment, scripts ); + + // Update the cache, but only store false + // unless this is a second parsing of the same content + if ( cacheable ) { + jQuery.fragments[ first ] = cachehit && fragment; + } + } + + return { fragment: fragment, cacheable: cacheable }; +}; + +jQuery.fragments = {}; + +jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + i = 0, + ret = [], + insert = jQuery( selector ), + l = insert.length, + parent = this.length === 1 && this[0].parentNode; + + if ( (parent == null || parent && parent.nodeType === 11 && parent.childNodes.length === 1) && l === 1 ) { + insert[ original ]( this[0] ); + return this; + } else { + for ( ; i < l; i++ ) { + elems = ( i > 0 ? this.clone(true) : this ).get(); + jQuery( insert[i] )[ original ]( elems ); + ret = ret.concat( elems ); + } + + return this.pushStack( ret, name, insert.selector ); + } + }; +}); + +function getAll( elem ) { + if ( typeof elem.getElementsByTagName !== "undefined" ) { + return elem.getElementsByTagName( "*" ); + + } else if ( typeof elem.querySelectorAll !== "undefined" ) { + return elem.querySelectorAll( "*" ); + + } else { + return []; + } +} + +// Used in clean, fixes the defaultChecked property +function fixDefaultChecked( elem ) { + if ( rcheckableType.test( elem.type ) ) { + elem.defaultChecked = elem.checked; + } +} + +jQuery.extend({ + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var srcElements, + destElements, + i, + clone; + + if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { + clone = elem.cloneNode( true ); + + // IE<=8 does not properly clone detached, unknown element nodes + } else { + fragmentDiv.innerHTML = elem.outerHTML; + fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); + } + + if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && + (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { + // IE copies events bound via attachEvent when using cloneNode. + // Calling detachEvent on the clone will also remove the events + // from the original. In order to get around this, we use some + // proprietary methods to clear the events. Thanks to MooTools + // guys for this hotness. + + cloneFixAttributes( elem, clone ); + + // Using Sizzle here is crazy slow, so we use getElementsByTagName instead + srcElements = getAll( elem ); + destElements = getAll( clone ); + + // Weird iteration because IE will replace the length property + // with an element if you are cloning the body and one of the + // elements on the page has a name or id of "length" + for ( i = 0; srcElements[i]; ++i ) { + // Ensure that the destination node is not null; Fixes #9587 + if ( destElements[i] ) { + cloneFixAttributes( srcElements[i], destElements[i] ); + } + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + cloneCopyEvent( elem, clone ); + + if ( deepDataAndEvents ) { + srcElements = getAll( elem ); + destElements = getAll( clone ); + + for ( i = 0; srcElements[i]; ++i ) { + cloneCopyEvent( srcElements[i], destElements[i] ); + } + } + } + + srcElements = destElements = null; + + // Return the cloned set + return clone; + }, + + clean: function( elems, context, fragment, scripts ) { + var i, j, elem, tag, wrap, depth, div, hasBody, tbody, len, handleScript, jsTags, + safe = context === document && safeFragment, + ret = []; + + // Ensure that context is a document + if ( !context || typeof context.createDocumentFragment === "undefined" ) { + context = document; + } + + // Use the already-created safe fragment if context permits + for ( i = 0; (elem = elems[i]) != null; i++ ) { + if ( typeof elem === "number" ) { + elem += ""; + } + + if ( !elem ) { + continue; + } + + // Convert html string into DOM nodes + if ( typeof elem === "string" ) { + if ( !rhtml.test( elem ) ) { + elem = context.createTextNode( elem ); + } else { + // Ensure a safe container in which to render the html + safe = safe || createSafeFragment( context ); + div = context.createElement("div"); + safe.appendChild( div ); + + // Fix "XHTML"-style tags in all browsers + elem = elem.replace(rxhtmlTag, "<$1>"); + + // Go to html and back, then peel off extra wrappers + tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + depth = wrap[0]; + div.innerHTML = wrap[1] + elem + wrap[2]; + + // Move to the right depth + while ( depth-- ) { + div = div.lastChild; + } + + // Remove IE's autoinserted from table fragments + if ( !jQuery.support.tbody ) { + + // String was a , *may* have spurious + hasBody = rtbody.test(elem); + tbody = tag === "table" && !hasBody ? + div.firstChild && div.firstChild.childNodes : + + // String was a bare or + wrap[1] === "
    " && !hasBody ? + div.childNodes : + []; + + for ( j = tbody.length - 1; j >= 0 ; --j ) { + if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) { + tbody[ j ].parentNode.removeChild( tbody[ j ] ); + } + } + } + + // IE completely kills leading whitespace when innerHTML is used + if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { + div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild ); + } + + elem = div.childNodes; + + // Take out of fragment container (we need a fresh div each time) + div.parentNode.removeChild( div ); + } + } + + if ( elem.nodeType ) { + ret.push( elem ); + } else { + jQuery.merge( ret, elem ); + } + } + + // Fix #11356: Clear elements from safeFragment + if ( div ) { + elem = div = safe = null; + } + + // Reset defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + if ( !jQuery.support.appendChecked ) { + for ( i = 0; (elem = ret[i]) != null; i++ ) { + if ( jQuery.nodeName( elem, "input" ) ) { + fixDefaultChecked( elem ); + } else if ( typeof elem.getElementsByTagName !== "undefined" ) { + jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked ); + } + } + } + + // Append elements to a provided document fragment + if ( fragment ) { + // Special handling of each script element + handleScript = function( elem ) { + // Check if we consider it executable + if ( !elem.type || rscriptType.test( elem.type ) ) { + // Detach the script and store it in the scripts array (if provided) or the fragment + // Return truthy to indicate that it has been handled + return scripts ? + scripts.push( elem.parentNode ? elem.parentNode.removeChild( elem ) : elem ) : + fragment.appendChild( elem ); + } + }; + + for ( i = 0; (elem = ret[i]) != null; i++ ) { + // Check if we're done after handling an executable script + if ( !( jQuery.nodeName( elem, "script" ) && handleScript( elem ) ) ) { + // Append to fragment and handle embedded scripts + fragment.appendChild( elem ); + if ( typeof elem.getElementsByTagName !== "undefined" ) { + // handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration + jsTags = jQuery.grep( jQuery.merge( [], elem.getElementsByTagName("script") ), handleScript ); + + // Splice the scripts into ret after their former ancestor and advance our index beyond them + ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) ); + i += jsTags.length; + } + } + } + } + + return ret; + }, + + cleanData: function( elems, /* internal */ acceptData ) { + var data, id, elem, type, + i = 0, + internalKey = jQuery.expando, + cache = jQuery.cache, + deleteExpando = jQuery.support.deleteExpando, + special = jQuery.event.special; + + for ( ; (elem = elems[i]) != null; i++ ) { + + if ( acceptData || jQuery.acceptData( elem ) ) { + + id = elem[ internalKey ]; + data = id && cache[ id ]; + + if ( data ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Remove cache only if it was not already removed by jQuery.event.remove + if ( cache[ id ] ) { + + delete cache[ id ]; + + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( deleteExpando ) { + delete elem[ internalKey ]; + + } else if ( elem.removeAttribute ) { + elem.removeAttribute( internalKey ); + + } else { + elem[ internalKey ] = null; + } + + jQuery.deletedIds.push( id ); + } + } + } + } + } +}); +// Limit scope pollution from any deprecated API +(function() { + +var matched, browser; + +// Use of jQuery.browser is frowned upon. +// More details: http://api.jquery.com/jQuery.browser +// jQuery.uaMatch maintained for back-compat +jQuery.uaMatch = function( ua ) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) || + /(webkit)[ \/]([\w.]+)/.exec( ua ) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) || + /(msie) ([\w.]+)/.exec( ua ) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; +}; + +matched = jQuery.uaMatch( navigator.userAgent ); +browser = {}; + +if ( matched.browser ) { + browser[ matched.browser ] = true; + browser.version = matched.version; +} + +// Chrome is Webkit, but Webkit is also Safari. +if ( browser.chrome ) { + browser.webkit = true; +} else if ( browser.webkit ) { + browser.safari = true; +} + +jQuery.browser = browser; + +jQuery.sub = function() { + function jQuerySub( selector, context ) { + return new jQuerySub.fn.init( selector, context ); + } + jQuery.extend( true, jQuerySub, this ); + jQuerySub.superclass = this; + jQuerySub.fn = jQuerySub.prototype = this(); + jQuerySub.fn.constructor = jQuerySub; + jQuerySub.sub = this.sub; + jQuerySub.fn.init = function init( selector, context ) { + if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { + context = jQuerySub( context ); + } + + return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); + }; + jQuerySub.fn.init.prototype = jQuerySub.fn; + var rootjQuerySub = jQuerySub(document); + return jQuerySub; +}; + +})(); +var curCSS, iframe, iframeDoc, + ralpha = /alpha\([^)]*\)/i, + ropacity = /opacity=([^)]*)/, + rposition = /^(top|right|bottom|left)$/, + // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" + // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rmargin = /^margin/, + rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), + rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), + rrelNum = new RegExp( "^([-+])=(" + core_pnum + ")", "i" ), + elemdisplay = { BODY: "block" }, + + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: 0, + fontWeight: 400 + }, + + cssExpand = [ "Top", "Right", "Bottom", "Left" ], + cssPrefixes = [ "Webkit", "O", "Moz", "ms" ], + + eventsToggle = jQuery.fn.toggle; + +// return a css property mapped to a potentially vendor prefixed property +function vendorPropName( style, name ) { + + // shortcut for names that are not vendor prefixed + if ( name in style ) { + return name; + } + + // check for vendor prefixed names + var capName = name.charAt(0).toUpperCase() + name.slice(1), + origName = name, + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in style ) { + return name; + } + } + + return origName; +} + +function isHidden( elem, el ) { + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); +} + +function showHide( elements, show ) { + var elem, display, + values = [], + index = 0, + length = elements.length; + + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + values[ index ] = jQuery._data( elem, "olddisplay" ); + if ( show ) { + // Reset the inline display of this element to learn if it is + // being hidden by cascaded rules or not + if ( !values[ index ] && elem.style.display === "none" ) { + elem.style.display = ""; + } + + // Set elements which have been overridden with display: none + // in a stylesheet to whatever the default browser style is + // for such an element + if ( elem.style.display === "" && isHidden( elem ) ) { + values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) ); + } + } else { + display = curCSS( elem, "display" ); + + if ( !values[ index ] && display !== "none" ) { + jQuery._data( elem, "olddisplay", display ); + } + } + } + + // Set the display of most of the elements in a second loop + // to avoid the constant reflow + for ( index = 0; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + if ( !show || elem.style.display === "none" || elem.style.display === "" ) { + elem.style.display = show ? values[ index ] || "" : "none"; + } + } + + return elements; +} + +jQuery.fn.extend({ + css: function( name, value ) { + return jQuery.access( this, function( elem, name, value ) { + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + }, + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state, fn2 ) { + var bool = typeof state === "boolean"; + + if ( jQuery.isFunction( state ) && jQuery.isFunction( fn2 ) ) { + return eventsToggle.apply( this, arguments ); + } + + return this.each(function() { + if ( bool ? state : isHidden( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + }); + } +}); + +jQuery.extend({ + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + + } + } + } + }, + + // Exclude the following css properties to add px + cssNumber: { + "fillOpacity": true, + "fontWeight": true, + "lineHeight": true, + "opacity": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: { + // normalize float css property + "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat" + }, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = jQuery.camelCase( name ), + style = elem.style; + + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // convert relative number strings (+= or -=) to relative numbers. #7345 + if ( type === "string" && (ret = rrelNum.exec( value )) ) { + value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); + // Fixes bug #9237 + type = "number"; + } + + // Make sure that NaN and null values aren't set. See: #7116 + if ( value == null || type === "number" && isNaN( value ) ) { + return; + } + + // If a number was passed in, add 'px' to the (except for certain CSS properties) + if ( type === "number" && !jQuery.cssNumber[ origName ] ) { + value += "px"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) { + // Wrapped to prevent IE from throwing errors when 'invalid' values are provided + // Fixes bug #5509 + try { + style[ name ] = value; + } catch(e) {} + } + + } else { + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, numeric, extra ) { + var val, num, hooks, + origName = jQuery.camelCase( name ); + + // Make sure that we're working with the right name + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name ); + } + + //convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Return, converting to number if forced or a qualifier was provided and val looks numeric + if ( numeric || extra !== undefined ) { + num = parseFloat( val ); + return numeric || jQuery.isNumeric( num ) ? num || 0 : val; + } + return val; + }, + + // A method for quickly swapping in/out CSS properties to get correct calculations + swap: function( elem, options, callback ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.call( elem ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; + } +}); + +// NOTE: To any future maintainer, we've window.getComputedStyle +// because jsdom on node.js will break without it. +if ( window.getComputedStyle ) { + curCSS = function( elem, name ) { + var ret, width, minWidth, maxWidth, + computed = window.getComputedStyle( elem, null ), + style = elem.style; + + if ( computed ) { + + // getPropertyValue is only needed for .css('filter') in IE9, see #12537 + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right + // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels + // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values + if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret; + }; +} else if ( document.documentElement.currentStyle ) { + curCSS = function( elem, name ) { + var left, rsLeft, + ret = elem.currentStyle && elem.currentStyle[ name ], + style = elem.style; + + // Avoid setting ret to empty string here + // so we don't default to auto + if ( ret == null && style && style[ name ] ) { + ret = style[ name ]; + } + + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + // but not position css attributes, as those are proportional to the parent element instead + // and we can't measure the parent instead because it might trigger a "stacking dolls" problem + if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) { + + // Remember the original values + left = style.left; + rsLeft = elem.runtimeStyle && elem.runtimeStyle.left; + + // Put in the new values to get a computed value out + if ( rsLeft ) { + elem.runtimeStyle.left = elem.currentStyle.left; + } + style.left = name === "fontSize" ? "1em" : ret; + ret = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + if ( rsLeft ) { + elem.runtimeStyle.left = rsLeft; + } + } + + return ret === "" ? "auto" : ret; + }; +} + +function setPositiveNumber( elem, value, subtract ) { + var matches = rnumsplit.exec( value ); + return matches ? + Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : + value; +} + +function augmentWidthOrHeight( elem, name, extra, isBorderBox ) { + var i = extra === ( isBorderBox ? "border" : "content" ) ? + // If we already have the right measurement, avoid augmentation + 4 : + // Otherwise initialize for horizontal or vertical properties + name === "width" ? 1 : 0, + + val = 0; + + for ( ; i < 4; i += 2 ) { + // both box models exclude margin, so add it if we want it + if ( extra === "margin" ) { + // we use jQuery.css instead of curCSS here + // because of the reliableMarginRight CSS hook! + val += jQuery.css( elem, extra + cssExpand[ i ], true ); + } + + // From this point on we use curCSS for maximum performance (relevant in animations) + if ( isBorderBox ) { + // border-box includes padding, so remove it if we want content + if ( extra === "content" ) { + val -= parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0; + } + + // at this point, extra isn't border nor margin, so remove border + if ( extra !== "margin" ) { + val -= parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0; + } + } else { + // at this point, extra isn't content, so add padding + val += parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0; + + // at this point, extra isn't content nor padding, so add border + if ( extra !== "padding" ) { + val += parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0; + } + } + } + + return val; +} + +function getWidthOrHeight( elem, name, extra ) { + + // Start with offset property, which is equivalent to the border-box value + var val = name === "width" ? elem.offsetWidth : elem.offsetHeight, + valueIsBorderBox = true, + isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box"; + + // some non-html elements return undefined for offsetWidth, so check for null/undefined + // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 + // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 + if ( val <= 0 || val == null ) { + // Fall back to computed then uncomputed css if necessary + val = curCSS( elem, name ); + if ( val < 0 || val == null ) { + val = elem.style[ name ]; + } + + // Computed unit is not pixels. Stop here and return. + if ( rnumnonpx.test(val) ) { + return val; + } + + // we need the check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] ); + + // Normalize "", auto, and prepare for extra + val = parseFloat( val ) || 0; + } + + // use the active box-sizing model to add/subtract irrelevant styles + return ( val + + augmentWidthOrHeight( + elem, + name, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox + ) + ) + "px"; +} + + +// Try to determine the default display value of an element +function css_defaultDisplay( nodeName ) { + if ( elemdisplay[ nodeName ] ) { + return elemdisplay[ nodeName ]; + } + + var elem = jQuery( "<" + nodeName + ">" ).appendTo( document.body ), + display = elem.css("display"); + elem.remove(); + + // If the simple way fails, + // get element's real default display by attaching it to a temp iframe + if ( display === "none" || display === "" ) { + // Use the already-created iframe if possible + iframe = document.body.appendChild( + iframe || jQuery.extend( document.createElement("iframe"), { + frameBorder: 0, + width: 0, + height: 0 + }) + ); + + // Create a cacheable copy of the iframe document on first call. + // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML + // document to it; WebKit & Firefox won't allow reusing the iframe document. + if ( !iframeDoc || !iframe.createElement ) { + iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document; + iframeDoc.write(""); + iframeDoc.close(); + } + + elem = iframeDoc.body.appendChild( iframeDoc.createElement(nodeName) ); + + display = curCSS( elem, "display" ); + document.body.removeChild( iframe ); + } + + // Store the correct default display + elemdisplay[ nodeName ] = display; + + return display; +} + +jQuery.each([ "height", "width" ], function( i, name ) { + jQuery.cssHooks[ name ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + // certain elements can have dimension info if we invisibly show them + // however, it must have a current display style that would benefit from this + if ( elem.offsetWidth === 0 && rdisplayswap.test( curCSS( elem, "display" ) ) ) { + return jQuery.swap( elem, cssShow, function() { + return getWidthOrHeight( elem, name, extra ); + }); + } else { + return getWidthOrHeight( elem, name, extra ); + } + } + }, + + set: function( elem, value, extra ) { + return setPositiveNumber( elem, value, extra ? + augmentWidthOrHeight( + elem, + name, + extra, + jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box" + ) : 0 + ); + } + }; +}); + +if ( !jQuery.support.opacity ) { + jQuery.cssHooks.opacity = { + get: function( elem, computed ) { + // IE uses filters for opacity + return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ? + ( 0.01 * parseFloat( RegExp.$1 ) ) + "" : + computed ? "1" : ""; + }, + + set: function( elem, value ) { + var style = elem.style, + currentStyle = elem.currentStyle, + opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "", + filter = currentStyle && currentStyle.filter || style.filter || ""; + + // IE has trouble with opacity if it does not have layout + // Force it by setting the zoom level + style.zoom = 1; + + // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652 + if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" && + style.removeAttribute ) { + + // Setting style.filter to null, "" & " " still leave "filter:" in the cssText + // if "filter:" is present at all, clearType is disabled, we want to avoid this + // style.removeAttribute is IE Only, but so apparently is this code path... + style.removeAttribute( "filter" ); + + // if there there is no filter style applied in a css rule, we are done + if ( currentStyle && !currentStyle.filter ) { + return; + } + } + + // otherwise, set new filter values + style.filter = ralpha.test( filter ) ? + filter.replace( ralpha, opacity ) : + filter + " " + opacity; + } + }; +} + +// These hooks cannot be added until DOM ready because the support test +// for it is not run until after DOM ready +jQuery(function() { + if ( !jQuery.support.reliableMarginRight ) { + jQuery.cssHooks.marginRight = { + get: function( elem, computed ) { + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + // Work around by temporarily setting element display to inline-block + return jQuery.swap( elem, { "display": "inline-block" }, function() { + if ( computed ) { + return curCSS( elem, "marginRight" ); + } + }); + } + }; + } + + // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 + // getComputedStyle returns percent when specified for top/left/bottom/right + // rather than make the css module depend on the offset module, we just check for it here + if ( !jQuery.support.pixelPosition && jQuery.fn.position ) { + jQuery.each( [ "top", "left" ], function( i, prop ) { + jQuery.cssHooks[ prop ] = { + get: function( elem, computed ) { + if ( computed ) { + var ret = curCSS( elem, prop ); + // if curCSS returns percentage, fallback to offset + return rnumnonpx.test( ret ) ? jQuery( elem ).position()[ prop ] + "px" : ret; + } + } + }; + }); + } + +}); + +if ( jQuery.expr && jQuery.expr.filters ) { + jQuery.expr.filters.hidden = function( elem ) { + return ( elem.offsetWidth === 0 && elem.offsetHeight === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || curCSS( elem, "display" )) === "none"); + }; + + jQuery.expr.filters.visible = function( elem ) { + return !jQuery.expr.filters.hidden( elem ); + }; +} + +// These hooks are used by animate to expand properties +jQuery.each({ + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i, + + // assumes a single number if not a string + parts = typeof value === "string" ? value.split(" ") : [ value ], + expanded = {}; + + for ( i = 0; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( !rmargin.test( prefix ) ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +}); +var r20 = /%20/g, + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i, + rselectTextarea = /^(?:select|textarea)/i; + +jQuery.fn.extend({ + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map(function(){ + return this.elements ? jQuery.makeArray( this.elements ) : this; + }) + .filter(function(){ + return this.name && !this.disabled && + ( this.checked || rselectTextarea.test( this.nodeName ) || + rinput.test( this.type ) ); + }) + .map(function( i, elem ){ + var val = jQuery( this ).val(); + + return val == null ? + null : + jQuery.isArray( val ) ? + jQuery.map( val, function( val, i ){ + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }) : + { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }).get(); + } +}); + +//Serialize an array of form elements or a set of +//key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, value ) { + // If value is a function, invoke it and return its value + value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value ); + s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value ); + }; + + // Set traditional to true for jQuery <= 1.3.2 behavior. + if ( traditional === undefined ) { + traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + }); + + } else { + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ).replace( r20, "+" ); +}; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( jQuery.isArray( obj ) ) { + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + // If array item is non-scalar (array or object), encode its + // numeric index to resolve deserialization ambiguity issues. + // Note that rack (as of 1.0.0) can't currently deserialize + // nested arrays properly, and attempting to do so may cause + // a server error. Possible fixes are to modify rack's + // deserialization algorithm or to provide an option or flag + // to force array serialization to be shallow. + buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add ); + } + }); + + } else if ( !traditional && jQuery.type( obj ) === "object" ) { + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + // Serialize scalar item. + add( prefix, obj ); + } +} +var + // Document location + ajaxLocParts, + ajaxLocation, + + rhash = /#.*$/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + rquery = /\?/, + rscript = /)<[^<]*)*<\/script>/gi, + rts = /([?&])_=[^&]*/, + rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/, + + // Keep a copy of the old load method + _load = jQuery.fn.load, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = ["*/"] + ["*"]; + +// #8138, IE may throw an exception when accessing +// a field from window.location if document.domain has been set +try { + ajaxLocation = location.href; +} catch( e ) { + // Use the href attribute of an A element + // since IE will modify it given document.location + ajaxLocation = document.createElement( "a" ); + ajaxLocation.href = ""; + ajaxLocation = ajaxLocation.href; +} + +// Segment location into parts +ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, list, placeBefore, + dataTypes = dataTypeExpression.toLowerCase().split( core_rspace ), + i = 0, + length = dataTypes.length; + + if ( jQuery.isFunction( func ) ) { + // For each dataType in the dataTypeExpression + for ( ; i < length; i++ ) { + dataType = dataTypes[ i ]; + // We control if we're asked to add before + // any existing element + placeBefore = /^\+/.test( dataType ); + if ( placeBefore ) { + dataType = dataType.substr( 1 ) || "*"; + } + list = structure[ dataType ] = structure[ dataType ] || []; + // then we add to the structure accordingly + list[ placeBefore ? "unshift" : "push" ]( func ); + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR, + dataType /* internal */, inspected /* internal */ ) { + + dataType = dataType || options.dataTypes[ 0 ]; + inspected = inspected || {}; + + inspected[ dataType ] = true; + + var selection, + list = structure[ dataType ], + i = 0, + length = list ? list.length : 0, + executeOnly = ( structure === prefilters ); + + for ( ; i < length && ( executeOnly || !selection ); i++ ) { + selection = list[ i ]( options, originalOptions, jqXHR ); + // If we got redirected to another dataType + // we try there if executing only and not done already + if ( typeof selection === "string" ) { + if ( !executeOnly || inspected[ selection ] ) { + selection = undefined; + } else { + options.dataTypes.unshift( selection ); + selection = inspectPrefiltersOrTransports( + structure, options, originalOptions, jqXHR, selection, inspected ); + } + } + } + // If we're only executing or nothing was selected + // we try the catchall dataType if not done already + if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) { + selection = inspectPrefiltersOrTransports( + structure, options, originalOptions, jqXHR, "*", inspected ); + } + // unnecessary when only executing (prefilters) + // but it'll be ignored by the caller in that case + return selection; +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } +} + +jQuery.fn.load = function( url, params, callback ) { + if ( typeof url !== "string" && _load ) { + return _load.apply( this, arguments ); + } + + // Don't do a request if no elements are being requested + if ( !this.length ) { + return this; + } + + var selector, type, response, + self = this, + off = url.indexOf(" "); + + if ( off >= 0 ) { + selector = url.slice( off, url.length ); + url = url.slice( 0, off ); + } + + // If it's a function + if ( jQuery.isFunction( params ) ) { + + // We assume that it's the callback + callback = params; + params = undefined; + + // Otherwise, build a param string + } else if ( params && typeof params === "object" ) { + type = "POST"; + } + + // Request the remote document + jQuery.ajax({ + url: url, + + // if "type" variable is undefined, then "GET" method will be used + type: type, + dataType: "html", + data: params, + complete: function( jqXHR, status ) { + if ( callback ) { + self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] ); + } + } + }).done(function( responseText ) { + + // Save response for use in complete callback + response = arguments; + + // See if a selector was specified + self.html( selector ? + + // Create a dummy div to hold the results + jQuery("
    ") + + // inject the contents of the document in, removing the scripts + // to avoid any 'Permission Denied' errors in IE + .append( responseText.replace( rscript, "" ) ) + + // Locate the specified elements + .find( selector ) : + + // If not, just inject the full result + responseText ); + + }); + + return this; +}; + +// Attach a bunch of functions for handling common AJAX events +jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){ + jQuery.fn[ o ] = function( f ){ + return this.on( o, f ); + }; +}); + +jQuery.each( [ "get", "post" ], function( i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + // shift arguments if data argument was omitted + if ( jQuery.isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + return jQuery.ajax({ + type: method, + url: url, + data: data, + success: callback, + dataType: type + }); + }; +}); + +jQuery.extend({ + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + if ( settings ) { + // Building a settings object + ajaxExtend( target, jQuery.ajaxSettings ); + } else { + // Extending ajaxSettings + settings = target; + target = jQuery.ajaxSettings; + } + ajaxExtend( target, settings ); + return target; + }, + + ajaxSettings: { + url: ajaxLocation, + isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ), + global: true, + type: "GET", + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + processData: true, + async: true, + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + xml: "application/xml, text/xml", + html: "text/html", + text: "text/plain", + json: "application/json, text/javascript", + "*": allTypes + }, + + contents: { + xml: /xml/, + html: /html/, + json: /json/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText" + }, + + // List of data converters + // 1) key format is "source_type destination_type" (a single space in-between) + // 2) the catchall symbol "*" can be used for source_type + converters: { + + // Convert anything to text + "* text": window.String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": jQuery.parseJSON, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + context: true, + url: true + } + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var // ifModified key + ifModifiedKey, + // Response headers + responseHeadersString, + responseHeaders, + // transport + transport, + // timeout handle + timeoutTimer, + // Cross-domain detection vars + parts, + // To know if global events are to be dispatched + fireGlobals, + // Loop variable + i, + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + // Callbacks context + callbackContext = s.context || s, + // Context for global events + // It's the callbackContext if one was provided in the options + // and if it's a DOM node or a jQuery collection + globalEventContext = callbackContext !== s && + ( callbackContext.nodeType || callbackContext instanceof jQuery ) ? + jQuery( callbackContext ) : jQuery.event, + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + // Status-dependent callbacks + statusCode = s.statusCode || {}, + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + // The jqXHR state + state = 0, + // Default abort message + strAbort = "canceled", + // Fake xhr + jqXHR = { + + readyState: 0, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( !state ) { + var lname = name.toLowerCase(); + name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Raw string + getAllResponseHeaders: function() { + return state === 2 ? responseHeadersString : null; + }, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( state === 2 ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[1].toLowerCase() ] = match[ 2 ]; + } + } + match = responseHeaders[ key.toLowerCase() ]; + } + return match === undefined ? null : match; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( !state ) { + s.mimeType = type; + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + statusText = statusText || strAbort; + if ( transport ) { + transport.abort( statusText ); + } + done( 0, statusText ); + return this; + } + }; + + // Callback for when everything is done + // It is defined here because jslint complains if it is declared + // at the end of the function (which would be more logical and readable) + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Called once + if ( state === 2 ) { + return; + } + + // State is "done" now + state = 2; + + // Clear timeout if it exists + if ( timeoutTimer ) { + clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // If successful, handle type chaining + if ( status >= 200 && status < 300 || status === 304 ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + + modified = jqXHR.getResponseHeader("Last-Modified"); + if ( modified ) { + jQuery.lastModified[ ifModifiedKey ] = modified; + } + modified = jqXHR.getResponseHeader("Etag"); + if ( modified ) { + jQuery.etag[ ifModifiedKey ] = modified; + } + } + + // If not modified + if ( status === 304 ) { + + statusText = "notmodified"; + isSuccess = true; + + // If we have data + } else { + + isSuccess = ajaxConvert( s, response ); + statusText = isSuccess.state; + success = isSuccess.data; + error = isSuccess.error; + isSuccess = !error; + } + } else { + // We extract error from statusText + // then normalize statusText and status for non-aborts + error = statusText; + if ( !statusText || status ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ), + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + // Attach deferreds + deferred.promise( jqXHR ); + jqXHR.success = jqXHR.done; + jqXHR.error = jqXHR.fail; + jqXHR.complete = completeDeferred.add; + + // Status-dependent callbacks + jqXHR.statusCode = function( map ) { + if ( map ) { + var tmp; + if ( state < 2 ) { + for ( tmp in map ) { + statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ]; + } + } else { + tmp = map[ jqXHR.status ]; + jqXHR.always( tmp ); + } + } + return this; + }; + + // Remove hash character (#7531: and string promotion) + // Add protocol if not provided (#5866: IE7 issue with protocol-less urls) + // We also use the url parameter if available + s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" ); + + // Extract dataTypes list + s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( core_rspace ); + + // A cross-domain request is in order when we have a protocol:host:port mismatch + if ( s.crossDomain == null ) { + parts = rurl.exec( s.url.toLowerCase() ); + s.crossDomain = !!( parts && + ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] || + ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) != + ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) ) + ); + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( state === 2 ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + fireGlobals = s.global; + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // If data is available, append data to url + if ( s.data ) { + s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data; + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Get ifModifiedKey before adding the anti-cache parameter + ifModifiedKey = s.url; + + // Add anti-cache in url if needed + if ( s.cache === false ) { + + var ts = jQuery.now(), + // try replacing _= if it is there + ret = s.url.replace( rts, "$1_=" + ts ); + + // if nothing was replaced, add timestamp to the end + s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + ifModifiedKey = ifModifiedKey || s.url; + if ( jQuery.lastModified[ ifModifiedKey ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] ); + } + if ( jQuery.etag[ ifModifiedKey ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] ); + } + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ? + s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) { + // Abort if not done already and return + return jqXHR.abort(); + + } + + // aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + for ( i in { success: 1, error: 1, complete: 1 } ) { + jqXHR[ i ]( s[ i ] ); + } + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = setTimeout( function(){ + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + state = 1; + transport.send( requestHeaders, done ); + } catch (e) { + // Propagate exception as error if not done + if ( state < 2 ) { + done( -1, e ); + // Simply rethrow otherwise + } else { + throw e; + } + } + } + + return jqXHR; + }, + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {} + +}); + +/* Handles responses to an ajax request: + * - sets all responseXXX fields accordingly + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes, + responseFields = s.responseFields; + + // Fill responseXXX fields + for ( type in responseFields ) { + if ( type in responses ) { + jqXHR[ responseFields[type] ] = responses[ type ]; + } + } + + // Remove auto dataType and get content-type in the process + while( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "content-type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +// Chain conversions given the request and the original response +function ajaxConvert( s, response ) { + + var conv, conv2, current, tmp, + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(), + prev = dataTypes[ 0 ], + converters = {}, + i = 0; + + // Apply the dataFilter if provided + if ( s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + // Convert to each sequential dataType, tolerating list modification + for ( ; (current = dataTypes[++i]); ) { + + // There's only work to do if current dataType is non-auto + if ( current !== "*" ) { + + // Convert response if prev dataType is non-auto and differs from current + if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split(" "); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.splice( i--, 0, current ); + } + + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s["throws"] ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current }; + } + } + } + } + + // Update prev for next iteration + prev = current; + } + } + + return { state: "success", data: response }; +} +var oldCallbacks = [], + rquestion = /\?/, + rjsonp = /(=)\?(?=&|$)|\?\?/, + nonce = jQuery.now(); + +// Default jsonp settings +jQuery.ajaxSetup({ + jsonp: "callback", + jsonpCallback: function() { + var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) ); + this[ callback ] = true; + return callback; + } +}); + +// Detect, normalize options and install callbacks for jsonp requests +jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { + + var callbackName, overwritten, responseContainer, + data = s.data, + url = s.url, + hasCallback = s.jsonp !== false, + replaceInUrl = hasCallback && rjsonp.test( url ), + replaceInData = hasCallback && !replaceInUrl && typeof data === "string" && + !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && + rjsonp.test( data ); + + // Handle iff the expected data type is "jsonp" or we have a parameter to set + if ( s.dataTypes[ 0 ] === "jsonp" || replaceInUrl || replaceInData ) { + + // Get callback name, remembering preexisting value associated with it + callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ? + s.jsonpCallback() : + s.jsonpCallback; + overwritten = window[ callbackName ]; + + // Insert callback into url or form data + if ( replaceInUrl ) { + s.url = url.replace( rjsonp, "$1" + callbackName ); + } else if ( replaceInData ) { + s.data = data.replace( rjsonp, "$1" + callbackName ); + } else if ( hasCallback ) { + s.url += ( rquestion.test( url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName; + } + + // Use data converter to retrieve json after script execution + s.converters["script json"] = function() { + if ( !responseContainer ) { + jQuery.error( callbackName + " was not called" ); + } + return responseContainer[ 0 ]; + }; + + // force json dataType + s.dataTypes[ 0 ] = "json"; + + // Install callback + window[ callbackName ] = function() { + responseContainer = arguments; + }; + + // Clean-up function (fires after converters) + jqXHR.always(function() { + // Restore preexisting value + window[ callbackName ] = overwritten; + + // Save back as free + if ( s[ callbackName ] ) { + // make sure that re-using the options doesn't screw things around + s.jsonpCallback = originalSettings.jsonpCallback; + + // save the callback name for future use + oldCallbacks.push( callbackName ); + } + + // Call if it was a function and we have a response + if ( responseContainer && jQuery.isFunction( overwritten ) ) { + overwritten( responseContainer[ 0 ] ); + } + + responseContainer = overwritten = undefined; + }); + + // Delegate to script + return "script"; + } +}); +// Install script dataType +jQuery.ajaxSetup({ + accepts: { + script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /javascript|ecmascript/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +}); + +// Handle cache's special case and global +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + s.global = false; + } +}); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function(s) { + + // This transport only deals with cross domain requests + if ( s.crossDomain ) { + + var script, + head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement; + + return { + + send: function( _, callback ) { + + script = document.createElement( "script" ); + + script.async = "async"; + + if ( s.scriptCharset ) { + script.charset = s.scriptCharset; + } + + script.src = s.url; + + // Attach handlers for all browsers + script.onload = script.onreadystatechange = function( _, isAbort ) { + + if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) { + + // Handle memory leak in IE + script.onload = script.onreadystatechange = null; + + // Remove the script + if ( head && script.parentNode ) { + head.removeChild( script ); + } + + // Dereference the script + script = undefined; + + // Callback if not abort + if ( !isAbort ) { + callback( 200, "success" ); + } + } + }; + // Use insertBefore instead of appendChild to circumvent an IE6 bug. + // This arises when a base node is used (#2709 and #4378). + head.insertBefore( script, head.firstChild ); + }, + + abort: function() { + if ( script ) { + script.onload( 0, 1 ); + } + } + }; + } +}); +var xhrCallbacks, + // #5280: Internet Explorer will keep connections alive if we don't abort on unload + xhrOnUnloadAbort = window.ActiveXObject ? function() { + // Abort all pending requests + for ( var key in xhrCallbacks ) { + xhrCallbacks[ key ]( 0, 1 ); + } + } : false, + xhrId = 0; + +// Functions to create xhrs +function createStandardXHR() { + try { + return new window.XMLHttpRequest(); + } catch( e ) {} +} + +function createActiveXHR() { + try { + return new window.ActiveXObject( "Microsoft.XMLHTTP" ); + } catch( e ) {} +} + +// Create the request object +// (This is still attached to ajaxSettings for backward compatibility) +jQuery.ajaxSettings.xhr = window.ActiveXObject ? + /* Microsoft failed to properly + * implement the XMLHttpRequest in IE7 (can't request local files), + * so we use the ActiveXObject when it is available + * Additionally XMLHttpRequest can be disabled in IE7/IE8 so + * we need a fallback. + */ + function() { + return !this.isLocal && createStandardXHR() || createActiveXHR(); + } : + // For all other browsers, use the standard XMLHttpRequest object + createStandardXHR; + +// Determine support properties +(function( xhr ) { + jQuery.extend( jQuery.support, { + ajax: !!xhr, + cors: !!xhr && ( "withCredentials" in xhr ) + }); +})( jQuery.ajaxSettings.xhr() ); + +// Create transport if the browser can provide an xhr +if ( jQuery.support.ajax ) { + + jQuery.ajaxTransport(function( s ) { + // Cross domain only allowed if supported through XMLHttpRequest + if ( !s.crossDomain || jQuery.support.cors ) { + + var callback; + + return { + send: function( headers, complete ) { + + // Get a new xhr + var handle, i, + xhr = s.xhr(); + + // Open the socket + // Passing null username, generates a login popup on Opera (#2865) + if ( s.username ) { + xhr.open( s.type, s.url, s.async, s.username, s.password ); + } else { + xhr.open( s.type, s.url, s.async ); + } + + // Apply custom fields if provided + if ( s.xhrFields ) { + for ( i in s.xhrFields ) { + xhr[ i ] = s.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( s.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( s.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !s.crossDomain && !headers["X-Requested-With"] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Need an extra try/catch for cross domain requests in Firefox 3 + try { + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + } catch( _ ) {} + + // Do send the request + // This may raise an exception which is actually + // handled in jQuery.ajax (so no try/catch here) + xhr.send( ( s.hasContent && s.data ) || null ); + + // Listener + callback = function( _, isAbort ) { + + var status, + statusText, + responseHeaders, + responses, + xml; + + // Firefox throws exceptions when accessing properties + // of an xhr when a network error occurred + // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE) + try { + + // Was never called and is aborted or complete + if ( callback && ( isAbort || xhr.readyState === 4 ) ) { + + // Only called once + callback = undefined; + + // Do not keep as active anymore + if ( handle ) { + xhr.onreadystatechange = jQuery.noop; + if ( xhrOnUnloadAbort ) { + delete xhrCallbacks[ handle ]; + } + } + + // If it's an abort + if ( isAbort ) { + // Abort it manually if needed + if ( xhr.readyState !== 4 ) { + xhr.abort(); + } + } else { + status = xhr.status; + responseHeaders = xhr.getAllResponseHeaders(); + responses = {}; + xml = xhr.responseXML; + + // Construct response list + if ( xml && xml.documentElement /* #4958 */ ) { + responses.xml = xml; + } + + // When requesting binary data, IE6-9 will throw an exception + // on any attempt to access responseText (#11426) + try { + responses.text = xhr.responseText; + } catch( e ) { + } + + // Firefox throws an exception when accessing + // statusText for faulty cross-domain requests + try { + statusText = xhr.statusText; + } catch( e ) { + // We normalize with Webkit giving an empty statusText + statusText = ""; + } + + // Filter status for non standard behaviors + + // If the request is local and we have data: assume a success + // (success with no data won't get notified, that's the best we + // can do given current implementations) + if ( !status && s.isLocal && !s.crossDomain ) { + status = responses.text ? 200 : 404; + // IE - #1450: sometimes returns 1223 when it should be 204 + } else if ( status === 1223 ) { + status = 204; + } + } + } + } catch( firefoxAccessException ) { + if ( !isAbort ) { + complete( -1, firefoxAccessException ); + } + } + + // Call complete if needed + if ( responses ) { + complete( status, statusText, responses, responseHeaders ); + } + }; + + if ( !s.async ) { + // if we're in sync mode we fire the callback + callback(); + } else if ( xhr.readyState === 4 ) { + // (IE6 & IE7) if it's in cache and has been + // retrieved directly we need to fire the callback + setTimeout( callback, 0 ); + } else { + handle = ++xhrId; + if ( xhrOnUnloadAbort ) { + // Create the active xhrs callbacks list if needed + // and attach the unload handler + if ( !xhrCallbacks ) { + xhrCallbacks = {}; + jQuery( window ).unload( xhrOnUnloadAbort ); + } + // Add to list of active xhrs callbacks + xhrCallbacks[ handle ] = callback; + } + xhr.onreadystatechange = callback; + } + }, + + abort: function() { + if ( callback ) { + callback(0,1); + } + } + }; + } + }); +} +var fxNow, timerId, + rfxtypes = /^(?:toggle|show|hide)$/, + rfxnum = new RegExp( "^(?:([-+])=|)(" + core_pnum + ")([a-z%]*)$", "i" ), + rrun = /queueHooks$/, + animationPrefilters = [ defaultPrefilter ], + tweeners = { + "*": [function( prop, value ) { + var end, unit, + tween = this.createTween( prop, value ), + parts = rfxnum.exec( value ), + target = tween.cur(), + start = +target || 0, + scale = 1, + maxIterations = 20; + + if ( parts ) { + end = +parts[2]; + unit = parts[3] || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + + // We need to compute starting value + if ( unit !== "px" && start ) { + // Iteratively approximate from a nonzero starting point + // Prefer the current property, because this process will be trivial if it uses the same units + // Fallback to end or a simple constant + start = jQuery.css( tween.elem, prop, true ) || end || 1; + + do { + // If previous iteration zeroed out, double until we get *something* + // Use a string for doubling factor so we don't accidentally see scale as unchanged below + scale = scale || ".5"; + + // Adjust and apply + start = start / scale; + jQuery.style( tween.elem, prop, start + unit ); + + // Update scale, tolerating zero or NaN from tween.cur() + // And breaking the loop if scale is unchanged or perfect, or if we've just had enough + } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations ); + } + + tween.unit = unit; + tween.start = start; + // If a +=/-= token was provided, we're doing a relative animation + tween.end = parts[1] ? start + ( parts[1] + 1 ) * end : end; + } + return tween; + }] + }; + +// Animations created synchronously will run synchronously +function createFxNow() { + setTimeout(function() { + fxNow = undefined; + }, 0 ); + return ( fxNow = jQuery.now() ); +} + +function createTweens( animation, props ) { + jQuery.each( props, function( prop, value ) { + var collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( collection[ index ].call( animation, prop, value ) ) { + + // we're done with this property + return; + } + } + }); +} + +function Animation( elem, properties, options ) { + var result, + index = 0, + tweenerIndex = 0, + length = animationPrefilters.length, + deferred = jQuery.Deferred().always( function() { + // don't match elem in the :animated selector + delete tick.elem; + }), + tick = function() { + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length ; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ]); + + if ( percent < 1 && length ) { + return remaining; + } else { + deferred.resolveWith( elem, [ animation ] ); + return false; + } + }, + animation = deferred.promise({ + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { specialEasing: {} }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end, easing ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + // if we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + + for ( ; index < length ; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // resolve when we played the last frame + // otherwise, reject + if ( gotoEnd ) { + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + }), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length ; index++ ) { + result = animationPrefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + return result; + } + } + + createTweens( animation, props ); + + if ( jQuery.isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + jQuery.fx.timer( + jQuery.extend( tick, { + anim: animation, + queue: animation.opts.queue, + elem: elem + }) + ); + + // attach callbacks from options + return animation.progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = jQuery.camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( jQuery.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // not quite $.extend, this wont overwrite keys already present. + // also - reusing 'index' from above because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweener: function( props, callback ) { + if ( jQuery.isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.split(" "); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length ; index++ ) { + prop = props[ index ]; + tweeners[ prop ] = tweeners[ prop ] || []; + tweeners[ prop ].unshift( callback ); + } + }, + + prefilter: function( callback, prepend ) { + if ( prepend ) { + animationPrefilters.unshift( callback ); + } else { + animationPrefilters.push( callback ); + } + } +}); + +function defaultPrefilter( elem, props, opts ) { + var index, prop, value, length, dataShow, toggle, tween, hooks, oldfire, + anim = this, + style = elem.style, + orig = {}, + handled = [], + hidden = elem.nodeType && isHidden( elem ); + + // handle queue: false promises + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always(function() { + // doing this makes sure that the complete handler will be called + // before this completes + anim.always(function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + }); + }); + } + + // height/width overflow pass + if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) { + // Make sure that nothing sneaks out + // Record all 3 overflow attributes because IE does not + // change the overflow attribute when overflowX and + // overflowY are set to the same value + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Set display property to inline-block for height/width + // animations on inline elements that are having width/height animated + if ( jQuery.css( elem, "display" ) === "inline" && + jQuery.css( elem, "float" ) === "none" ) { + + // inline-level elements accept inline-block; + // block-level elements need to be inline with layout + if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) { + style.display = "inline-block"; + + } else { + style.zoom = 1; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + if ( !jQuery.support.shrinkWrapBlocks ) { + anim.done(function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + }); + } + } + + + // show/hide pass + for ( index in props ) { + value = props[ index ]; + if ( rfxtypes.exec( value ) ) { + delete props[ index ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + continue; + } + handled.push( index ); + } + } + + length = handled.length; + if ( length ) { + dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} ); + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + + // store state if its toggle - enables .stop().toggle() to "reverse" + if ( toggle ) { + dataShow.hidden = !hidden; + } + if ( hidden ) { + jQuery( elem ).show(); + } else { + anim.done(function() { + jQuery( elem ).hide(); + }); + } + anim.done(function() { + var prop; + jQuery.removeData( elem, "fxshow", true ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + }); + for ( index = 0 ; index < length ; index++ ) { + prop = handled[ index ]; + tween = anim.createTween( prop, hidden ? dataShow[ prop ] : 0 ); + orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop ); + + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = tween.start; + if ( hidden ) { + tween.end = tween.start; + tween.start = prop === "width" || prop === "height" ? 1 : 0; + } + } + } + } +} + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || "swing"; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + if ( tween.elem[ tween.prop ] != null && + (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) { + return tween.elem[ tween.prop ]; + } + + // passing any value as a 4th parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails + // so, simple values such as "10px" are parsed to Float. + // complex values such as "rotate(1rad)" are returned as is. + result = jQuery.css( tween.elem, tween.prop, false, "" ); + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + // use step hook for back compat - use cssHook if its there - use .style if its + // available and use plain properties where available + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Remove in 2.0 - this supports IE8's panic based approach +// to setting things on disconnected nodes + +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.each([ "toggle", "show", "hide" ], function( i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" || + // special check for .toggle( handler, handler, ... ) + ( !i && jQuery.isFunction( speed ) && jQuery.isFunction( easing ) ) ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +}); + +jQuery.fn.extend({ + fadeTo: function( speed, to, easing, callback ) { + + // show any hidden elements after setting opacity to 0 + return this.filter( isHidden ).css( "opacity", 0 ).show() + + // animate to the value specified + .end().animate({ opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations resolve immediately + if ( empty ) { + anim.stop( true ); + } + }; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue && type !== false ) { + this.queue( type || "fx", [] ); + } + + return this.each(function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = jQuery._data( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) { + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // start the next in the queue if the last step wasn't forced + // timers currently will call their complete callbacks, which will dequeue + // but only if they were gotoEnd + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + }); + } +}); + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + attrs = { height: type }, + i = 0; + + // if we include width, step value is 1 to do all cssExpand values, + // if we don't include width, step value is 2 to skip over Left and Right + includeWidth = includeWidth? 1 : 0; + for( ; i < 4 ; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +// Generate shortcuts for custom animations +jQuery.each({ + slideDown: genFx("show"), + slideUp: genFx("hide"), + slideToggle: genFx("toggle"), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +}); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + jQuery.isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing + }; + + opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration : + opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default; + + // normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( jQuery.isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p*Math.PI ) / 2; + } +}; + +jQuery.timers = []; +jQuery.fx = Tween.prototype.init; +jQuery.fx.tick = function() { + var timer, + timers = jQuery.timers, + i = 0; + + fxNow = jQuery.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + // Checks the timer has not already been removed + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + if ( timer() && jQuery.timers.push( timer ) && !timerId ) { + timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval ); + } +}; + +jQuery.fx.interval = 13; + +jQuery.fx.stop = function() { + clearInterval( timerId ); + timerId = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + // Default speed + _default: 400 +}; + +// Back Compat <1.8 extension point +jQuery.fx.step = {}; + +if ( jQuery.expr && jQuery.expr.filters ) { + jQuery.expr.filters.animated = function( elem ) { + return jQuery.grep(jQuery.timers, function( fn ) { + return elem === fn.elem; + }).length; + }; +} +var rroot = /^(?:body|html)$/i; + +jQuery.fn.offset = function( options ) { + if ( arguments.length ) { + return options === undefined ? + this : + this.each(function( i ) { + jQuery.offset.setOffset( this, options, i ); + }); + } + + var docElem, body, win, clientTop, clientLeft, scrollTop, scrollLeft, + box = { top: 0, left: 0 }, + elem = this[ 0 ], + doc = elem && elem.ownerDocument; + + if ( !doc ) { + return; + } + + if ( (body = doc.body) === elem ) { + return jQuery.offset.bodyOffset( elem ); + } + + docElem = doc.documentElement; + + // Make sure it's not a disconnected DOM node + if ( !jQuery.contains( docElem, elem ) ) { + return box; + } + + // If we don't have gBCR, just use 0,0 rather than error + // BlackBerry 5, iOS 3 (original iPhone) + if ( typeof elem.getBoundingClientRect !== "undefined" ) { + box = elem.getBoundingClientRect(); + } + win = getWindow( doc ); + clientTop = docElem.clientTop || body.clientTop || 0; + clientLeft = docElem.clientLeft || body.clientLeft || 0; + scrollTop = win.pageYOffset || docElem.scrollTop; + scrollLeft = win.pageXOffset || docElem.scrollLeft; + return { + top: box.top + scrollTop - clientTop, + left: box.left + scrollLeft - clientLeft + }; +}; + +jQuery.offset = { + + bodyOffset: function( body ) { + var top = body.offsetTop, + left = body.offsetLeft; + + if ( jQuery.support.doesNotIncludeMarginInBodyOffset ) { + top += parseFloat( jQuery.css(body, "marginTop") ) || 0; + left += parseFloat( jQuery.css(body, "marginLeft") ) || 0; + } + + return { top: top, left: left }; + }, + + setOffset: function( elem, options, i ) { + var position = jQuery.css( elem, "position" ); + + // set position first, in-case top/left are set even on static elem + if ( position === "static" ) { + elem.style.position = "relative"; + } + + var curElem = jQuery( elem ), + curOffset = curElem.offset(), + curCSSTop = jQuery.css( elem, "top" ), + curCSSLeft = jQuery.css( elem, "left" ), + calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1, + props = {}, curPosition = {}, curTop, curLeft; + + // need to be able to calculate position if either top or left is auto and position is either absolute or fixed + if ( calculatePosition ) { + curPosition = curElem.position(); + curTop = curPosition.top; + curLeft = curPosition.left; + } else { + curTop = parseFloat( curCSSTop ) || 0; + curLeft = parseFloat( curCSSLeft ) || 0; + } + + if ( jQuery.isFunction( options ) ) { + options = options.call( elem, i, curOffset ); + } + + if ( options.top != null ) { + props.top = ( options.top - curOffset.top ) + curTop; + } + if ( options.left != null ) { + props.left = ( options.left - curOffset.left ) + curLeft; + } + + if ( "using" in options ) { + options.using.call( elem, props ); + } else { + curElem.css( props ); + } + } +}; + + +jQuery.fn.extend({ + + position: function() { + if ( !this[0] ) { + return; + } + + var elem = this[0], + + // Get *real* offsetParent + offsetParent = this.offsetParent(), + + // Get correct offsets + offset = this.offset(), + parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset(); + + // Subtract element margins + // note: when an element has margin: auto the offsetLeft and marginLeft + // are the same in Safari causing offset.left to incorrectly be 0 + offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0; + offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0; + + // Add offsetParent borders + parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0; + parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0; + + // Subtract the two offsets + return { + top: offset.top - parentOffset.top, + left: offset.left - parentOffset.left + }; + }, + + offsetParent: function() { + return this.map(function() { + var offsetParent = this.offsetParent || document.body; + while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) { + offsetParent = offsetParent.offsetParent; + } + return offsetParent || document.body; + }); + } +}); + + +// Create scrollLeft and scrollTop methods +jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) { + var top = /Y/.test( prop ); + + jQuery.fn[ method ] = function( val ) { + return jQuery.access( this, function( elem, method, val ) { + var win = getWindow( elem ); + + if ( val === undefined ) { + return win ? (prop in win) ? win[ prop ] : + win.document.documentElement[ method ] : + elem[ method ]; + } + + if ( win ) { + win.scrollTo( + !top ? val : jQuery( win ).scrollLeft(), + top ? val : jQuery( win ).scrollTop() + ); + + } else { + elem[ method ] = val; + } + }, method, val, arguments.length, null ); + }; +}); + +function getWindow( elem ) { + return jQuery.isWindow( elem ) ? + elem : + elem.nodeType === 9 ? + elem.defaultView || elem.parentWindow : + false; +} +// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods +jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { + jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) { + // margin is only for outerHeight, outerWidth + jQuery.fn[ funcName ] = function( margin, value ) { + var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ), + extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" ); + + return jQuery.access( this, function( elem, type, value ) { + var doc; + + if ( jQuery.isWindow( elem ) ) { + // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there + // isn't a whole lot we can do. See pull request at this URL for discussion: + // https://github.com/jquery/jquery/pull/764 + return elem.document.documentElement[ "client" + name ]; + } + + // Get document width or height + if ( elem.nodeType === 9 ) { + doc = elem.documentElement; + + // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest + // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it. + return Math.max( + elem.body[ "scroll" + name ], doc[ "scroll" + name ], + elem.body[ "offset" + name ], doc[ "offset" + name ], + doc[ "client" + name ] + ); + } + + return value === undefined ? + // Get width or height on the element, requesting but not forcing parseFloat + jQuery.css( elem, type, value, extra ) : + + // Set width or height on the element + jQuery.style( elem, type, value, extra ); + }, type, chainable ? margin : undefined, chainable, null ); + }; + }); +}); +// Expose jQuery to the global object +window.jQuery = window.$ = jQuery; + +// Expose jQuery as an AMD module, but only for AMD loaders that +// understand the issues with loading multiple versions of jQuery +// in a page that all might call define(). The loader will indicate +// they have special allowances for multiple jQuery versions by +// specifying define.amd.jQuery = true. Register as a named module, +// since jQuery can be concatenated with other files that may use define, +// but not use a proper concatenation script that understands anonymous +// AMD modules. A named AMD is safest and most robust way to register. +// Lowercase jquery is used because AMD module names are derived from +// file names, and jQuery is normally delivered in a lowercase file name. +// Do this after creating the global so that if an AMD module wants to call +// noConflict to hide this version of jQuery, it will work. +if ( typeof define === "function" && define.amd && define.amd.jQuery ) { + define( "jquery", [], function () { return jQuery; } ); +} + +})( window ); + + + window.jQuery.noConflict(); + return window.jQuery; +} +module.exports = create('undefined' === typeof window ? undefined : window); +module.exports.create = create; +}()); diff --git a/server/node_modules/jquery/node_modules/contextify/.npmignore b/server/node_modules/jquery/node_modules/contextify/.npmignore new file mode 100644 index 0000000..4c0b0bb --- /dev/null +++ b/server/node_modules/jquery/node_modules/contextify/.npmignore @@ -0,0 +1,12 @@ +Makefile +.lock-wscript +node_modules +build +*.swp +*.swo +TODO +Makefile.gyp +*.Makefile +*.target.gyp.mk +gyp-mac-tool +out diff --git a/server/node_modules/jquery/node_modules/contextify/LICENSE.txt b/server/node_modules/jquery/node_modules/contextify/LICENSE.txt new file mode 100644 index 0000000..49fd917 --- /dev/null +++ b/server/node_modules/jquery/node_modules/contextify/LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) 2011 Brian McDaniel + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/server/node_modules/jquery/node_modules/contextify/README.md b/server/node_modules/jquery/node_modules/contextify/README.md new file mode 100644 index 0000000..6a713a1 --- /dev/null +++ b/server/node_modules/jquery/node_modules/contextify/README.md @@ -0,0 +1,106 @@ +# Contextify + +For Windows issues, see here: https://github.com/brianmcd/contextify/wiki/Windows-Installation-Guide + +Please add to the wiki if you find new issues/solutions. + +Turn an object into a V8 execution context. A contextified object acts as the global 'this' when executing scripts in its context. Contextify adds 3 methods to the contextified object: run(code, filename), getGlobal(), and dispose(). The main difference between Contextify and Node's vm methods is that Contextify allows asynchronous functions to continue executing in the Contextified object's context. See vm vs. Contextify below for more discussion. + +## Examples +```javascript +var Contextify = require('contextify'); +var sandbox = { console : console, prop1 : 'prop1'}; +Contextify(sandbox); +sandbox.run('console.log(prop1);'); +sandbox.dispose(); // free the resources allocated for the context. +``` + +```javascript +var sandbox = Contextify(); // returns an empty contextified object. +sandbox.run('var x = 3;'); +console.log(sandbox.x); // prints 3 +sandbox.dispose(); +``` + +```javascript +var sandbox = Contextify({setTimeout : setTimeout}); +sandbox.run("setTimeout(function () { x = 3; }, 5);"); +console.log(sandbox.x); // prints undefined +setTimeout(function () { + console.log(sandbox.x); // prints 3 + sandbox.dispose(); +}, 10); +``` +## Details + +**Contextify([sandbox])** + + sandbox - The object to contextify, which will be modified as described below + If no sandbox is specified, an empty object will be allocated and used instead. + + Returns the contextified object. It doesn't make a copy, so if you already have a reference + to the sandbox, you don't need to catch the return value. + +A Contextified object has 2 methods added to it: + +**run(code, [filename])** + + code - string containing JavaScript to execute + filename - an optional filename for debugging. + + Runs the code in the Contextified object's context. + +**getGlobal()** + +Returns the actual global object for the V8 context. The global object is initialized with interceptors (discussed below) which forward accesses on it to the contextified object. This means the contextified object acts like the global object in most cases. Sometimes, though, you need to make a reference to the actual global object. + +For example: + +```javascript +var window = Contextify({console : console}); +window.window = window; +window.run("console.log(window === this);"); +// prints false. +``` + +```javascript +var window = Contextify({console : console}); +window.window = window.getGlobal(); +window.run("console.log(window === this);"); +// prints true +``` + +The global object returned by getGlobal() can be treated like the contextified sandbox object, except that defining getters/setters will not work on it. Define getters and setters on the actual sandbox object instead. + +**dispose()** + +Frees the memory allocated for the underlying V8 context. If you don't call this when you're done, the V8 context memory will leak, as will the sandbox memory, since the context's global stores a strong reference to the sandbox object. You can still use your sandbox object after calling dispose(), but it's unsafe to use a global previously returned from getGlobal(). run, getGlobal, and dispose will be removed from the sandbox object. + +## Install + + npm install contextify + +## require('vm') vs. Contextify + +Node's vm functions (runInContext etc) work by copying the values from the sandbox object onto a context's global object, executing the passed in script, then copying the results back. This means that scripts that create asynchronous functions (using mechanisms like setTimeout) won't have see the results of executing those functions, since the copying in/out only occurs during an explicit call to runInContext and friends. + +Contextify creates a V8 context, and uses interceptors (see: http://code.google.com/apis/v8/embed.html#interceptors) to forward global object accesses to the sandbox object. This means there is no copying in or out, so asynchronous functions have the expected effect on the sandbox object. + +## Tests + +Testing is done with nodeunit. Run the tests with + + nodeunit test/ + +Output: + + OK: 92 assertions (27ms) + + +## Building + + node-waf configure build + +## Acknowledgments + +Inspiration taken from Assaf's Zombie.js context solution: https://github.com/assaf/zombie diff --git a/server/node_modules/jquery/node_modules/contextify/binding.gyp b/server/node_modules/jquery/node_modules/contextify/binding.gyp new file mode 100644 index 0000000..2106404 --- /dev/null +++ b/server/node_modules/jquery/node_modules/contextify/binding.gyp @@ -0,0 +1,8 @@ +{ + 'targets': [ + { + 'target_name': 'contextify', + 'sources': [ 'src/contextify.cc' ] + } + ] +} diff --git a/server/node_modules/jquery/node_modules/contextify/build/Makefile b/server/node_modules/jquery/node_modules/contextify/build/Makefile new file mode 100644 index 0000000..4b51697 --- /dev/null +++ b/server/node_modules/jquery/node_modules/contextify/build/Makefile @@ -0,0 +1,332 @@ +# We borrow heavily from the kernel build setup, though we are simpler since +# we don't have Kconfig tweaking settings on us. + +# The implicit make rules have it looking for RCS files, among other things. +# We instead explicitly write all the rules we care about. +# It's even quicker (saves ~200ms) to pass -r on the command line. +MAKEFLAGS=-r + +# The source directory tree. +srcdir := .. +abs_srcdir := $(abspath $(srcdir)) + +# The name of the builddir. +builddir_name ?= . + +# The V=1 flag on command line makes us verbosely print command lines. +ifdef V + quiet= +else + quiet=quiet_ +endif + +# Specify BUILDTYPE=Release on the command line for a release build. +BUILDTYPE ?= Release + +# Directory all our build output goes into. +# Note that this must be two directories beneath src/ for unit tests to pass, +# as they reach into the src/ directory for data with relative paths. +builddir ?= $(builddir_name)/$(BUILDTYPE) +abs_builddir := $(abspath $(builddir)) +depsdir := $(builddir)/.deps + +# Object output directory. +obj := $(builddir)/obj +abs_obj := $(abspath $(obj)) + +# We build up a list of every single one of the targets so we can slurp in the +# generated dependency rule Makefiles in one pass. +all_deps := + + + +# C++ apps need to be linked with g++. +# +# Note: flock is used to seralize linking. Linking is a memory-intensive +# process so running parallel links can often lead to thrashing. To disable +# the serialization, override LINK via an envrionment variable as follows: +# +# export LINK=g++ +# +# This will allow make to invoke N linker processes as specified in -jN. +LINK ?= flock $(builddir)/linker.lock $(CXX.target) + +CC.target ?= $(CC) +CFLAGS.target ?= $(CFLAGS) +CXX.target ?= $(CXX) +CXXFLAGS.target ?= $(CXXFLAGS) +LINK.target ?= $(LINK) +LDFLAGS.target ?= $(LDFLAGS) +AR.target ?= $(AR) + +# TODO(evan): move all cross-compilation logic to gyp-time so we don't need +# to replicate this environment fallback in make as well. +CC.host ?= gcc +CFLAGS.host ?= +CXX.host ?= g++ +CXXFLAGS.host ?= +LINK.host ?= g++ +LDFLAGS.host ?= +AR.host ?= ar + +# Define a dir function that can handle spaces. +# http://www.gnu.org/software/make/manual/make.html#Syntax-of-Functions +# "leading spaces cannot appear in the text of the first argument as written. +# These characters can be put into the argument value by variable substitution." +empty := +space := $(empty) $(empty) + +# http://stackoverflow.com/questions/1189781/using-make-dir-or-notdir-on-a-path-with-spaces +replace_spaces = $(subst $(space),?,$1) +unreplace_spaces = $(subst ?,$(space),$1) +dirx = $(call unreplace_spaces,$(dir $(call replace_spaces,$1))) + +# Flags to make gcc output dependency info. Note that you need to be +# careful here to use the flags that ccache and distcc can understand. +# We write to a dep file on the side first and then rename at the end +# so we can't end up with a broken dep file. +depfile = $(depsdir)/$(call replace_spaces,$@).d +DEPFLAGS = -MMD -MF $(depfile).raw + +# We have to fixup the deps output in a few ways. +# (1) the file output should mention the proper .o file. +# ccache or distcc lose the path to the target, so we convert a rule of +# the form: +# foobar.o: DEP1 DEP2 +# into +# path/to/foobar.o: DEP1 DEP2 +# (2) we want missing files not to cause us to fail to build. +# We want to rewrite +# foobar.o: DEP1 DEP2 \ +# DEP3 +# to +# DEP1: +# DEP2: +# DEP3: +# so if the files are missing, they're just considered phony rules. +# We have to do some pretty insane escaping to get those backslashes +# and dollar signs past make, the shell, and sed at the same time. +# Doesn't work with spaces, but that's fine: .d files have spaces in +# their names replaced with other characters. +define fixup_dep +# The depfile may not exist if the input file didn't have any #includes. +touch $(depfile).raw +# Fixup path as in (1). +sed -e "s|^$(notdir $@)|$@|" $(depfile).raw >> $(depfile) +# Add extra rules as in (2). +# We remove slashes and replace spaces with new lines; +# remove blank lines; +# delete the first line and append a colon to the remaining lines. +sed -e 's|\\||' -e 'y| |\n|' $(depfile).raw |\ + grep -v '^$$' |\ + sed -e 1d -e 's|$$|:|' \ + >> $(depfile) +rm $(depfile).raw +endef + +# Command definitions: +# - cmd_foo is the actual command to run; +# - quiet_cmd_foo is the brief-output summary of the command. + +quiet_cmd_cc = CC($(TOOLSET)) $@ +cmd_cc = $(CC.$(TOOLSET)) $(GYP_CFLAGS) $(DEPFLAGS) $(CFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_cxx = CXX($(TOOLSET)) $@ +cmd_cxx = $(CXX.$(TOOLSET)) $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_touch = TOUCH $@ +cmd_touch = touch $@ + +quiet_cmd_copy = COPY $@ +# send stderr to /dev/null to ignore messages when linking directories. +cmd_copy = rm -rf "$@" && cp -af "$<" "$@" + +quiet_cmd_alink = AR($(TOOLSET)) $@ +cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) crs $@ $(filter %.o,$^) + +quiet_cmd_alink_thin = AR($(TOOLSET)) $@ +cmd_alink_thin = rm -f $@ && $(AR.$(TOOLSET)) crsT $@ $(filter %.o,$^) + +# Due to circular dependencies between libraries :(, we wrap the +# special "figure out circular dependencies" flags around the entire +# input list during linking. +quiet_cmd_link = LINK($(TOOLSET)) $@ +cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ -Wl,--start-group $(LD_INPUTS) -Wl,--end-group $(LIBS) + +# We support two kinds of shared objects (.so): +# 1) shared_library, which is just bundling together many dependent libraries +# into a link line. +# 2) loadable_module, which is generating a module intended for dlopen(). +# +# They differ only slightly: +# In the former case, we want to package all dependent code into the .so. +# In the latter case, we want to package just the API exposed by the +# outermost module. +# This means shared_library uses --whole-archive, while loadable_module doesn't. +# (Note that --whole-archive is incompatible with the --start-group used in +# normal linking.) + +# Other shared-object link notes: +# - Set SONAME to the library filename so our binaries don't reference +# the local, absolute paths used on the link command-line. +quiet_cmd_solink = SOLINK($(TOOLSET)) $@ +cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--whole-archive $(LD_INPUTS) -Wl,--no-whole-archive $(LIBS) + +quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ +cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--start-group $(filter-out FORCE_DO_CMD, $^) -Wl,--end-group $(LIBS) + + +# Define an escape_quotes function to escape single quotes. +# This allows us to handle quotes properly as long as we always use +# use single quotes and escape_quotes. +escape_quotes = $(subst ','\'',$(1)) +# This comment is here just to include a ' to unconfuse syntax highlighting. +# Define an escape_vars function to escape '$' variable syntax. +# This allows us to read/write command lines with shell variables (e.g. +# $LD_LIBRARY_PATH), without triggering make substitution. +escape_vars = $(subst $$,$$$$,$(1)) +# Helper that expands to a shell command to echo a string exactly as it is in +# make. This uses printf instead of echo because printf's behaviour with respect +# to escape sequences is more portable than echo's across different shells +# (e.g., dash, bash). +exact_echo = printf '%s\n' '$(call escape_quotes,$(1))' + +# Helper to compare the command we're about to run against the command +# we logged the last time we ran the command. Produces an empty +# string (false) when the commands match. +# Tricky point: Make has no string-equality test function. +# The kernel uses the following, but it seems like it would have false +# positives, where one string reordered its arguments. +# arg_check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \ +# $(filter-out $(cmd_$@), $(cmd_$(1)))) +# We instead substitute each for the empty string into the other, and +# say they're equal if both substitutions produce the empty string. +# .d files contain ? instead of spaces, take that into account. +command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\ + $(subst $(cmd_$(call replace_spaces,$@)),,$(cmd_$(1)))) + +# Helper that is non-empty when a prerequisite changes. +# Normally make does this implicitly, but we force rules to always run +# so we can check their command lines. +# $? -- new prerequisites +# $| -- order-only dependencies +prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?)) + +# Helper that executes all postbuilds until one fails. +define do_postbuilds + @E=0;\ + for p in $(POSTBUILDS); do\ + eval $$p;\ + E=$$?;\ + if [ $$E -ne 0 ]; then\ + break;\ + fi;\ + done;\ + if [ $$E -ne 0 ]; then\ + rm -rf "$@";\ + exit $$E;\ + fi +endef + +# do_cmd: run a command via the above cmd_foo names, if necessary. +# Should always run for a given target to handle command-line changes. +# Second argument, if non-zero, makes it do asm/C/C++ dependency munging. +# Third argument, if non-zero, makes it do POSTBUILDS processing. +# Note: We intentionally do NOT call dirx for depfile, since it contains ? for +# spaces already and dirx strips the ? characters. +define do_cmd +$(if $(or $(command_changed),$(prereq_changed)), + @$(call exact_echo, $($(quiet)cmd_$(1))) + @mkdir -p "$(call dirx,$@)" "$(dir $(depfile))" + $(if $(findstring flock,$(word 1,$(cmd_$1))), + @$(cmd_$(1)) + @echo " $(quiet_cmd_$(1)): Finished", + @$(cmd_$(1)) + ) + @$(call exact_echo,$(call escape_vars,cmd_$(call replace_spaces,$@) := $(cmd_$(1)))) > $(depfile) + @$(if $(2),$(fixup_dep)) + $(if $(and $(3), $(POSTBUILDS)), + $(call do_postbuilds) + ) +) +endef + +# Declare the "all" target first so it is the default, +# even though we don't have the deps yet. +.PHONY: all +all: + +# make looks for ways to re-generate included makefiles, but in our case, we +# don't have a direct way. Explicitly telling make that it has nothing to do +# for them makes it go faster. +%.d: ; + +# Use FORCE_DO_CMD to force a target to run. Should be coupled with +# do_cmd. +.PHONY: FORCE_DO_CMD +FORCE_DO_CMD: + +TOOLSET := target +# Suffix rules, putting all outputs into $(obj). +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + + +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,contextify.target.mk)))),) + include contextify.target.mk +endif + +quiet_cmd_regen_makefile = ACTION Regenerating $@ +cmd_regen_makefile = /usr/local/lib/node_modules/npm/node_modules/node-gyp/gyp/gyp -fmake --ignore-environment "--toplevel-dir=." -I/var/www/FlexSurvey/server/node_modules/jquery/node_modules/contextify/build/config.gypi -I/usr/local/lib/node_modules/npm/node_modules/node-gyp/addon.gypi -I/home/dev/.node-gyp/0.10.22/common.gypi "--depth=." "-Goutput_dir=." "--generator-output=build" "-Dlibrary=shared_library" "-Dvisibility=default" "-Dnode_root_dir=/home/dev/.node-gyp/0.10.22" "-Dmodule_root_dir=/var/www/FlexSurvey/server/node_modules/jquery/node_modules/contextify" binding.gyp +Makefile: $(srcdir)/../../../../../../../../home/dev/.node-gyp/0.10.22/common.gypi $(srcdir)/build/config.gypi $(srcdir)/binding.gyp $(srcdir)/../../../../../../../../usr/local/lib/node_modules/npm/node_modules/node-gyp/addon.gypi + $(call do_cmd,regen_makefile) + +# "all" is a concatenation of the "all" targets from all the included +# sub-makefiles. This is just here to clarify. +all: + +# Add in dependency-tracking rules. $(all_deps) is the list of every single +# target in our tree. Only consider the ones with .d (dependency) info: +d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d)) +ifneq ($(d_files),) + include $(d_files) +endif diff --git a/server/node_modules/jquery/node_modules/contextify/build/Release/.deps/Release/contextify.node.d b/server/node_modules/jquery/node_modules/contextify/build/Release/.deps/Release/contextify.node.d new file mode 100644 index 0000000..71f6671 --- /dev/null +++ b/server/node_modules/jquery/node_modules/contextify/build/Release/.deps/Release/contextify.node.d @@ -0,0 +1 @@ +cmd_Release/contextify.node := rm -rf "Release/contextify.node" && cp -af "Release/obj.target/contextify.node" "Release/contextify.node" diff --git a/server/node_modules/jquery/node_modules/contextify/build/Release/.deps/Release/obj.target/contextify.node.d b/server/node_modules/jquery/node_modules/contextify/build/Release/.deps/Release/obj.target/contextify.node.d new file mode 100644 index 0000000..acc2125 --- /dev/null +++ b/server/node_modules/jquery/node_modules/contextify/build/Release/.deps/Release/obj.target/contextify.node.d @@ -0,0 +1 @@ +cmd_Release/obj.target/contextify.node := flock ./Release/linker.lock g++ -shared -pthread -rdynamic -m64 -Wl,-soname=contextify.node -o Release/obj.target/contextify.node -Wl,--start-group Release/obj.target/contextify/src/contextify.o -Wl,--end-group diff --git a/server/node_modules/jquery/node_modules/contextify/build/Release/.deps/Release/obj.target/contextify/src/contextify.o.d b/server/node_modules/jquery/node_modules/contextify/build/Release/.deps/Release/obj.target/contextify/src/contextify.o.d new file mode 100644 index 0000000..e02208a --- /dev/null +++ b/server/node_modules/jquery/node_modules/contextify/build/Release/.deps/Release/obj.target/contextify/src/contextify.o.d @@ -0,0 +1,23 @@ +cmd_Release/obj.target/contextify/src/contextify.o := g++ '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-DBUILDING_NODE_EXTENSION' -I/home/dev/.node-gyp/0.10.22/src -I/home/dev/.node-gyp/0.10.22/deps/uv/include -I/home/dev/.node-gyp/0.10.22/deps/v8/include -fPIC -Wall -Wextra -Wno-unused-parameter -pthread -m64 -O2 -fno-strict-aliasing -fno-tree-vrp -fno-rtti -fno-exceptions -MMD -MF ./Release/.deps/Release/obj.target/contextify/src/contextify.o.d.raw -c -o Release/obj.target/contextify/src/contextify.o ../src/contextify.cc +Release/obj.target/contextify/src/contextify.o: ../src/contextify.cc \ + /home/dev/.node-gyp/0.10.22/src/node.h \ + /home/dev/.node-gyp/0.10.22/deps/uv/include/uv.h \ + /home/dev/.node-gyp/0.10.22/deps/uv/include/uv-private/uv-unix.h \ + /home/dev/.node-gyp/0.10.22/deps/uv/include/uv-private/ngx-queue.h \ + /home/dev/.node-gyp/0.10.22/deps/uv/include/uv-private/uv-linux.h \ + /home/dev/.node-gyp/0.10.22/deps/v8/include/v8.h \ + /home/dev/.node-gyp/0.10.22/deps/v8/include/v8stdint.h \ + /home/dev/.node-gyp/0.10.22/src/node_object_wrap.h \ + /home/dev/.node-gyp/0.10.22/src/node.h \ + /home/dev/.node-gyp/0.10.22/src/node_version.h +../src/contextify.cc: +/home/dev/.node-gyp/0.10.22/src/node.h: +/home/dev/.node-gyp/0.10.22/deps/uv/include/uv.h: +/home/dev/.node-gyp/0.10.22/deps/uv/include/uv-private/uv-unix.h: +/home/dev/.node-gyp/0.10.22/deps/uv/include/uv-private/ngx-queue.h: +/home/dev/.node-gyp/0.10.22/deps/uv/include/uv-private/uv-linux.h: +/home/dev/.node-gyp/0.10.22/deps/v8/include/v8.h: +/home/dev/.node-gyp/0.10.22/deps/v8/include/v8stdint.h: +/home/dev/.node-gyp/0.10.22/src/node_object_wrap.h: +/home/dev/.node-gyp/0.10.22/src/node.h: +/home/dev/.node-gyp/0.10.22/src/node_version.h: diff --git a/server/node_modules/jquery/node_modules/contextify/build/Release/contextify.node b/server/node_modules/jquery/node_modules/contextify/build/Release/contextify.node new file mode 100644 index 0000000..8072e73 Binary files /dev/null and b/server/node_modules/jquery/node_modules/contextify/build/Release/contextify.node differ diff --git a/server/node_modules/jquery/node_modules/contextify/build/Release/linker.lock b/server/node_modules/jquery/node_modules/contextify/build/Release/linker.lock new file mode 100644 index 0000000..e69de29 diff --git a/server/node_modules/jquery/node_modules/contextify/build/Release/obj.target/contextify.node b/server/node_modules/jquery/node_modules/contextify/build/Release/obj.target/contextify.node new file mode 100644 index 0000000..8072e73 Binary files /dev/null and b/server/node_modules/jquery/node_modules/contextify/build/Release/obj.target/contextify.node differ diff --git a/server/node_modules/jquery/node_modules/contextify/build/binding.Makefile b/server/node_modules/jquery/node_modules/contextify/build/binding.Makefile new file mode 100644 index 0000000..d65e328 --- /dev/null +++ b/server/node_modules/jquery/node_modules/contextify/build/binding.Makefile @@ -0,0 +1,6 @@ +# This file is generated by gyp; do not edit. + +export builddir_name ?= build/./. +.PHONY: all +all: + $(MAKE) contextify diff --git a/server/node_modules/jquery/node_modules/contextify/build/config.gypi b/server/node_modules/jquery/node_modules/contextify/build/config.gypi new file mode 100644 index 0000000..ee3cea4 --- /dev/null +++ b/server/node_modules/jquery/node_modules/contextify/build/config.gypi @@ -0,0 +1,114 @@ +# Do not edit. File was generated by node-gyp's "configure" step +{ + "target_defaults": { + "cflags": [], + "default_configuration": "Release", + "defines": [], + "include_dirs": [], + "libraries": [] + }, + "variables": { + "clang": 0, + "gcc_version": 46, + "host_arch": "x64", + "node_install_npm": "true", + "node_prefix": "", + "node_shared_cares": "false", + "node_shared_http_parser": "false", + "node_shared_libuv": "false", + "node_shared_openssl": "false", + "node_shared_v8": "false", + "node_shared_zlib": "false", + "node_tag": "", + "node_unsafe_optimizations": 0, + "node_use_dtrace": "false", + "node_use_etw": "false", + "node_use_openssl": "true", + "node_use_perfctr": "false", + "node_use_systemtap": "false", + "python": "/usr/bin/python", + "target_arch": "x64", + "v8_enable_gdbjit": 0, + "v8_no_strict_aliasing": 1, + "v8_use_snapshot": "true", + "nodedir": "/home/dev/.node-gyp/0.10.22", + "copy_dev_lib": "true", + "standalone_static_library": 1, + "save_dev": "", + "browser": "", + "viewer": "man", + "rollback": "true", + "usage": "", + "globalignorefile": "/usr/local/etc/npmignore", + "init_author_url": "", + "shell": "/bin/bash", + "parseable": "", + "shrinkwrap": "true", + "userignorefile": "/home/dev/.npmignore", + "cache_max": "null", + "init_author_email": "", + "sign_git_tag": "", + "ignore": "", + "long": "", + "registry": "https://registry.npmjs.org/", + "fetch_retries": "2", + "npat": "", + "message": "%s", + "versions": "", + "globalconfig": "/usr/local/etc/npmrc", + "always_auth": "", + "cache_lock_retries": "10", + "fetch_retry_mintimeout": "10000", + "proprietary_attribs": "true", + "coverage": "", + "json": "", + "pre": "", + "description": "true", + "engine_strict": "", + "https_proxy": "", + "init_module": "/home/dev/.npm-init.js", + "userconfig": "/home/dev/.npmrc", + "npaturl": "http://npat.npmjs.org/", + "node_version": "v0.10.22", + "user": "", + "editor": "vi", + "save": "", + "tag": "latest", + "global": "", + "optional": "true", + "username": "", + "bin_links": "true", + "force": "", + "searchopts": "", + "depth": "null", + "rebuild_bundle": "true", + "searchsort": "name", + "unicode": "true", + "yes": "", + "fetch_retry_maxtimeout": "60000", + "strict_ssl": "true", + "dev": "", + "fetch_retry_factor": "10", + "group": "1000", + "cache_lock_stale": "60000", + "version": "", + "cache_min": "10", + "cache": "/home/dev/.npm", + "searchexclude": "", + "color": "true", + "save_optional": "", + "user_agent": "node/v0.10.22 linux x64", + "cache_lock_wait": "10000", + "production": "", + "save_bundle": "", + "init_version": "0.0.0", + "umask": "18", + "git": "git", + "init_author_name": "", + "onload_script": "", + "tmp": "/home/dev/tmp", + "unsafe_perm": "true", + "link": "", + "prefix": "/usr/local" + } +} diff --git a/server/node_modules/jquery/node_modules/contextify/build/contextify.target.mk b/server/node_modules/jquery/node_modules/contextify/build/contextify.target.mk new file mode 100644 index 0000000..1f8d28c --- /dev/null +++ b/server/node_modules/jquery/node_modules/contextify/build/contextify.target.mk @@ -0,0 +1,129 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := contextify +DEFS_Debug := \ + '-D_LARGEFILE_SOURCE' \ + '-D_FILE_OFFSET_BITS=64' \ + '-DBUILDING_NODE_EXTENSION' \ + '-DDEBUG' \ + '-D_DEBUG' + +# Flags passed to all source files. +CFLAGS_Debug := \ + -fPIC \ + -Wall \ + -Wextra \ + -Wno-unused-parameter \ + -pthread \ + -m64 \ + -g \ + -O0 + +# Flags passed to only C files. +CFLAGS_C_Debug := + +# Flags passed to only C++ files. +CFLAGS_CC_Debug := \ + -fno-rtti \ + -fno-exceptions + +INCS_Debug := \ + -I/home/dev/.node-gyp/0.10.22/src \ + -I/home/dev/.node-gyp/0.10.22/deps/uv/include \ + -I/home/dev/.node-gyp/0.10.22/deps/v8/include + +DEFS_Release := \ + '-D_LARGEFILE_SOURCE' \ + '-D_FILE_OFFSET_BITS=64' \ + '-DBUILDING_NODE_EXTENSION' + +# Flags passed to all source files. +CFLAGS_Release := \ + -fPIC \ + -Wall \ + -Wextra \ + -Wno-unused-parameter \ + -pthread \ + -m64 \ + -O2 \ + -fno-strict-aliasing \ + -fno-tree-vrp + +# Flags passed to only C files. +CFLAGS_C_Release := + +# Flags passed to only C++ files. +CFLAGS_CC_Release := \ + -fno-rtti \ + -fno-exceptions + +INCS_Release := \ + -I/home/dev/.node-gyp/0.10.22/src \ + -I/home/dev/.node-gyp/0.10.22/deps/uv/include \ + -I/home/dev/.node-gyp/0.10.22/deps/v8/include + +OBJS := \ + $(obj).target/$(TARGET)/src/contextify.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Debug := \ + -pthread \ + -rdynamic \ + -m64 + +LDFLAGS_Release := \ + -pthread \ + -rdynamic \ + -m64 + +LIBS := + +$(obj).target/contextify.node: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(obj).target/contextify.node: LIBS := $(LIBS) +$(obj).target/contextify.node: TOOLSET := $(TOOLSET) +$(obj).target/contextify.node: $(OBJS) FORCE_DO_CMD + $(call do_cmd,solink_module) + +all_deps += $(obj).target/contextify.node +# Add target alias +.PHONY: contextify +contextify: $(builddir)/contextify.node + +# Copy this to the executable output path. +$(builddir)/contextify.node: TOOLSET := $(TOOLSET) +$(builddir)/contextify.node: $(obj).target/contextify.node FORCE_DO_CMD + $(call do_cmd,copy) + +all_deps += $(builddir)/contextify.node +# Short alias for building this executable. +.PHONY: contextify.node +contextify.node: $(obj).target/contextify.node $(builddir)/contextify.node + +# Add executable to "all" target. +.PHONY: all +all: $(builddir)/contextify.node + diff --git a/server/node_modules/jquery/node_modules/contextify/changelog b/server/node_modules/jquery/node_modules/contextify/changelog new file mode 100644 index 0000000..244764c --- /dev/null +++ b/server/node_modules/jquery/node_modules/contextify/changelog @@ -0,0 +1,47 @@ +0.1.6 + * Fix broken build with node >= 0.11.0 (Jamie Kirkpatrick - @jpk) +0.1.5 + * Fix broken builds on 0.9.11 and above (tomgco - #61) +0.1.4 + * Fix segfault on Node >= 0.9.6. (pyokagan) + * Allow pre-compiling scripts. (mroch) +0.1.3 + * Remove PrintException. +0.1.2 + * Renamed bindings.gyp to binding.gyp (Rex Morgan) + * More fixes for OS X build. +0.1.1 + * Add node-gyp build support (Nathan Rajlich) + * Fix build for OS X (stolen from einaros's work on ws :)). + * Better exception reporting (print error message and stack trace). +0.1.0 + * Fix: #13 - Can't use global.eval as function. + * Added [Named|Indexed]SecurityCallbacks to global ObjectTemplate. + * No longer detaching the global proxy. + * Refactored to use node::ObjectWrap. +0.0.7 + * Fix: #11 - Declared global variables treated as undefined + +0.0.6 + * Fix: potential segfault when looking up properties on sandbox. + +0.0.5 + * Better error feedback when the module isn't built on the current node + version. + * All builds now build to the Release directory, regardless of Node + version. + +0.0.4 + * Defend against calling Contextify methods on the global after dispose() + has been called. + * Fix: npm install fails on Node 0.5.x. + +0.0.3 + * Fix: segfault due to premature garbage collection of sandbox. + * Added dispose() method to clean up context. + +0.0.2 + * Fix: memory leak due to creating unnecessary function instances. + +0.0.1 + * Initial release diff --git a/server/node_modules/jquery/node_modules/contextify/lib/contextify.js b/server/node_modules/jquery/node_modules/contextify/lib/contextify.js new file mode 100644 index 0000000..01020e0 --- /dev/null +++ b/server/node_modules/jquery/node_modules/contextify/lib/contextify.js @@ -0,0 +1,48 @@ +var binding = require('bindings')('contextify'); +var ContextifyContext = binding.ContextifyContext; +var ContextifyScript = binding.ContextifyScript; + +function Contextify (sandbox) { + if (typeof sandbox != 'object') { + sandbox = {}; + } + var ctx = new ContextifyContext(sandbox); + + sandbox.run = function () { + return ctx.run.apply(ctx, arguments); + }; + + sandbox.getGlobal = function () { + return ctx.getGlobal(); + } + + sandbox.dispose = function () { + sandbox.run = function () { + throw new Error("Called run() after dispose()."); + }; + sandbox.getGlobal = function () { + throw new Error("Called getGlobal() after dispose()."); + }; + sandbox.dispose = function () { + throw new Error("Called dispose() after dispose()."); + }; + ctx = null; + } + return sandbox; +} + +Contextify.createContext = function (sandbox) { + if (typeof sandbox != 'object') { + sandbox = {}; + } + return new ContextifyContext(sandbox); +}; + +Contextify.createScript = function (code, filename) { + if (typeof code != 'string') { + throw new TypeError('Code argument is required'); + } + return new ContextifyScript(code, filename); +}; + +module.exports = Contextify; diff --git a/server/node_modules/jquery/node_modules/contextify/node_modules/bindings/README.md b/server/node_modules/jquery/node_modules/contextify/node_modules/bindings/README.md new file mode 100644 index 0000000..585cf51 --- /dev/null +++ b/server/node_modules/jquery/node_modules/contextify/node_modules/bindings/README.md @@ -0,0 +1,97 @@ +node-bindings +============= +### Helper module for loading your native module's .node file + +This is a helper module for authors of Node.js native addon modules. +It is basically the "swiss army knife" of `require()`ing your native module's +`.node` file. + +Throughout the course of Node's native addon history, addons have ended up being +compiled in a variety of different places, depending on which build tool and which +version of node was used. To make matters worse, now the _gyp_ build tool can +produce either a _Release_ or _Debug_ build, each being built into different +locations. + +This module checks _all_ the possible locations that a native addon would be built +at, and returns the first one that loads successfully. + + +Installation +------------ + +Install with `npm`: + +``` bash +$ npm install bindings +``` + +Or add it to the `"dependencies"` section of your _package.json_ file. + + +Example +------- + +`require()`ing the proper bindings file for the current node version, platform +and architecture is as simple as: + +``` js +var bindings = require('bindings')('binding.node') + +// Use your bindings defined in your C files +bindings.your_c_function() +``` + + +Nice Error Output +----------------- + +When the `.node` file could not be loaded, `node-bindings` throws an Error with +a nice error message telling you exactly what was tried. You can also check the +`err.tries` Array property. + +``` +Error: Could not load the bindings file. Tried: + → /Users/nrajlich/ref/build/binding.node + → /Users/nrajlich/ref/build/Debug/binding.node + → /Users/nrajlich/ref/build/Release/binding.node + → /Users/nrajlich/ref/out/Debug/binding.node + → /Users/nrajlich/ref/Debug/binding.node + → /Users/nrajlich/ref/out/Release/binding.node + → /Users/nrajlich/ref/Release/binding.node + → /Users/nrajlich/ref/build/default/binding.node + → /Users/nrajlich/ref/compiled/0.8.2/darwin/x64/binding.node + at bindings (/Users/nrajlich/ref/node_modules/bindings/bindings.js:84:13) + at Object. (/Users/nrajlich/ref/lib/ref.js:5:47) + at Module._compile (module.js:449:26) + at Object.Module._extensions..js (module.js:467:10) + at Module.load (module.js:356:32) + at Function.Module._load (module.js:312:12) + ... +``` + + +License +------- + +(The MIT License) + +Copyright (c) 2012 Nathan Rajlich <nathan@tootallnate.net> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/server/node_modules/jquery/node_modules/contextify/node_modules/bindings/bindings.js b/server/node_modules/jquery/node_modules/contextify/node_modules/bindings/bindings.js new file mode 100644 index 0000000..a40029b --- /dev/null +++ b/server/node_modules/jquery/node_modules/contextify/node_modules/bindings/bindings.js @@ -0,0 +1,159 @@ + +/** + * Module dependencies. + */ + +var fs = require('fs') + , path = require('path') + , join = path.join + , dirname = path.dirname + , exists = fs.existsSync || path.existsSync + , defaults = { + arrow: process.env.NODE_BINDINGS_ARROW || ' → ' + , compiled: process.env.NODE_BINDINGS_COMPILED_DIR || 'compiled' + , platform: process.platform + , arch: process.arch + , version: process.versions.node + , bindings: 'bindings.node' + , try: [ + // node-gyp's linked version in the "build" dir + [ 'module_root', 'build', 'bindings' ] + // node-waf and gyp_addon (a.k.a node-gyp) + , [ 'module_root', 'build', 'Debug', 'bindings' ] + , [ 'module_root', 'build', 'Release', 'bindings' ] + // Debug files, for development (legacy behavior, remove for node v0.9) + , [ 'module_root', 'out', 'Debug', 'bindings' ] + , [ 'module_root', 'Debug', 'bindings' ] + // Release files, but manually compiled (legacy behavior, remove for node v0.9) + , [ 'module_root', 'out', 'Release', 'bindings' ] + , [ 'module_root', 'Release', 'bindings' ] + // Legacy from node-waf, node <= 0.4.x + , [ 'module_root', 'build', 'default', 'bindings' ] + // Production "Release" buildtype binary (meh...) + , [ 'module_root', 'compiled', 'version', 'platform', 'arch', 'bindings' ] + ] + } + +/** + * The main `bindings()` function loads the compiled bindings for a given module. + * It uses V8's Error API to determine the parent filename that this function is + * being invoked from, which is then used to find the root directory. + */ + +function bindings (opts) { + + // Argument surgery + if (typeof opts == 'string') { + opts = { bindings: opts } + } else if (!opts) { + opts = {} + } + opts.__proto__ = defaults + + // Get the module root + if (!opts.module_root) { + opts.module_root = exports.getRoot(exports.getFileName()) + } + + // Ensure the given bindings name ends with .node + if (path.extname(opts.bindings) != '.node') { + opts.bindings += '.node' + } + + var tries = [] + , i = 0 + , l = opts.try.length + , n + , b + , err + + for (; i (/Users/nrajlich/ref/lib/ref.js:5:47)\n at Module._compile (module.js:449:26)\n at Object.Module._extensions..js (module.js:467:10)\n at Module.load (module.js:356:32)\n at Function.Module._load (module.js:312:12)\n ...\n```\n\n\nLicense\n-------\n\n(The MIT License)\n\nCopyright (c) 2012 Nathan Rajlich <nathan@tootallnate.net>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/TooTallNate/node-bindings/issues" + }, + "homepage": "https://github.com/TooTallNate/node-bindings", + "_id": "bindings@1.1.1", + "dist": { + "shasum": "6c2ef22cb53563447cdf1823d7e8a44cda2ef813" + }, + "_from": "bindings@*", + "_resolved": "https://registry.npmjs.org/bindings/-/bindings-1.1.1.tgz" +} diff --git a/server/node_modules/jquery/node_modules/contextify/package.json b/server/node_modules/jquery/node_modules/contextify/package.json new file mode 100644 index 0000000..8a5a1d0 --- /dev/null +++ b/server/node_modules/jquery/node_modules/contextify/package.json @@ -0,0 +1,57 @@ +{ + "name": "contextify", + "version": "0.1.6", + "description": "Turn an object into a persistent execution context.", + "author": { + "name": "Brian McDaniel", + "email": "brianmcd05@gmail.com" + }, + "contributors": [ + { + "name": "Assaf Arkin", + "email": "assaf@labnotes.org", + "url": "http://labnotes.org/" + } + ], + "keywords": [ + "context", + "vm" + ], + "repository": { + "type": "git", + "url": "https://github.com/brianmcd/contextify.git" + }, + "main": "./lib/contextify", + "scripts": { + "test": "nodeunit test/", + "install": "node-gyp rebuild" + }, + "engines": { + "node": ">=0.4.0" + }, + "licenses": [ + { + "type": "MIT", + "url": "http://github.com/brianmcd/contextify/blob/master/LICENSE.txt" + } + ], + "dependencies": { + "bindings": "*" + }, + "devDependencies": { + "nodeunit": ">=0.5.x" + }, + "gypfile": true, + "readme": "# Contextify\n\nFor Windows issues, see here: https://github.com/brianmcd/contextify/wiki/Windows-Installation-Guide\n\nPlease add to the wiki if you find new issues/solutions.\n\nTurn an object into a V8 execution context. A contextified object acts as the global 'this' when executing scripts in its context. Contextify adds 3 methods to the contextified object: run(code, filename), getGlobal(), and dispose(). The main difference between Contextify and Node's vm methods is that Contextify allows asynchronous functions to continue executing in the Contextified object's context. See vm vs. Contextify below for more discussion.\n\n## Examples\n```javascript\nvar Contextify = require('contextify');\nvar sandbox = { console : console, prop1 : 'prop1'};\nContextify(sandbox);\nsandbox.run('console.log(prop1);');\nsandbox.dispose(); // free the resources allocated for the context.\n```\n\n```javascript\nvar sandbox = Contextify(); // returns an empty contextified object.\nsandbox.run('var x = 3;');\nconsole.log(sandbox.x); // prints 3\nsandbox.dispose();\n```\n\n```javascript\nvar sandbox = Contextify({setTimeout : setTimeout});\nsandbox.run(\"setTimeout(function () { x = 3; }, 5);\");\nconsole.log(sandbox.x); // prints undefined\nsetTimeout(function () {\n console.log(sandbox.x); // prints 3\n sandbox.dispose();\n}, 10);\n```\n## Details\n\n**Contextify([sandbox])**\n\n sandbox - The object to contextify, which will be modified as described below\n If no sandbox is specified, an empty object will be allocated and used instead.\n\n Returns the contextified object. It doesn't make a copy, so if you already have a reference\n to the sandbox, you don't need to catch the return value.\n\nA Contextified object has 2 methods added to it:\n\n**run(code, [filename])**\n\n code - string containing JavaScript to execute\n filename - an optional filename for debugging.\n\n Runs the code in the Contextified object's context.\n\n**getGlobal()**\n\nReturns the actual global object for the V8 context. The global object is initialized with interceptors (discussed below) which forward accesses on it to the contextified object. This means the contextified object acts like the global object in most cases. Sometimes, though, you need to make a reference to the actual global object.\n\nFor example:\n\n```javascript\nvar window = Contextify({console : console});\nwindow.window = window;\nwindow.run(\"console.log(window === this);\");\n// prints false.\n```\n\n```javascript\nvar window = Contextify({console : console});\nwindow.window = window.getGlobal();\nwindow.run(\"console.log(window === this);\");\n// prints true\n```\n\nThe global object returned by getGlobal() can be treated like the contextified sandbox object, except that defining getters/setters will not work on it. Define getters and setters on the actual sandbox object instead.\n\n**dispose()**\n\nFrees the memory allocated for the underlying V8 context. If you don't call this when you're done, the V8 context memory will leak, as will the sandbox memory, since the context's global stores a strong reference to the sandbox object. You can still use your sandbox object after calling dispose(), but it's unsafe to use a global previously returned from getGlobal(). run, getGlobal, and dispose will be removed from the sandbox object.\n\n## Install\n\n npm install contextify\n\n## require('vm') vs. Contextify\n\nNode's vm functions (runInContext etc) work by copying the values from the sandbox object onto a context's global object, executing the passed in script, then copying the results back. This means that scripts that create asynchronous functions (using mechanisms like setTimeout) won't have see the results of executing those functions, since the copying in/out only occurs during an explicit call to runInContext and friends. \n\nContextify creates a V8 context, and uses interceptors (see: http://code.google.com/apis/v8/embed.html#interceptors) to forward global object accesses to the sandbox object. This means there is no copying in or out, so asynchronous functions have the expected effect on the sandbox object. \n\n## Tests\n\nTesting is done with nodeunit. Run the tests with\n\n nodeunit test/\n\nOutput: \n\n OK: 92 assertions (27ms)\n\n\n## Building\n\n node-waf configure build\n\n## Acknowledgments\n\nInspiration taken from Assaf's Zombie.js context solution: https://github.com/assaf/zombie\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/brianmcd/contextify/issues" + }, + "homepage": "https://github.com/brianmcd/contextify", + "_id": "contextify@0.1.6", + "dist": { + "shasum": "e70808d06d829c58e23caf8d3d2fc24194d14a2a" + }, + "_from": "contextify@~0.1.3", + "_resolved": "https://registry.npmjs.org/contextify/-/contextify-0.1.6.tgz" +} diff --git a/server/node_modules/jquery/node_modules/contextify/src/contextify.cc b/server/node_modules/jquery/node_modules/contextify/src/contextify.cc new file mode 100644 index 0000000..78402aa --- /dev/null +++ b/server/node_modules/jquery/node_modules/contextify/src/contextify.cc @@ -0,0 +1,312 @@ +#include "node.h" +#include "node_version.h" +#include +using namespace v8; +using namespace node; + +// For some reason this needs to be out of the object or node won't load the +// library. +static Persistent dataWrapperTmpl; +static Persistent dataWrapperCtor; + +class ContextifyContext : ObjectWrap { +public: + Persistent context; + Persistent sandbox; + Persistent proxyGlobal; + + static Persistent jsTmpl; + + ContextifyContext(Local sbox) { + HandleScope scope; + sandbox = Persistent::New(sbox); + } + + ~ContextifyContext() { + context.Dispose(); + context.Clear(); + proxyGlobal.Dispose(); + proxyGlobal.Clear(); + sandbox.Dispose(); + sandbox.Clear(); + } + + // We override ObjectWrap::Wrap so that we can create our context after + // we have a reference to our "host" JavaScript object. If we try to use + // handle_ in the ContextifyContext constructor, it will be empty since it's + // set in ObjectWrap::Wrap. + inline void Wrap(Handle handle) { + ObjectWrap::Wrap(handle); + context = createV8Context(); + proxyGlobal = Persistent::New(context->Global()); + } + + // This is an object that just keeps an internal pointer to this + // ContextifyContext. It's passed to the NamedPropertyHandler. If we + // pass the main JavaScript context object we're embedded in, then the + // NamedPropertyHandler will store a reference to it forever and keep it + // from getting gc'd. + Local createDataWrapper () { + HandleScope scope; + Local wrapper = dataWrapperCtor->NewInstance(); +#if NODE_MAJOR_VERSION > 0 || (NODE_MINOR_VERSION == 9 && (NODE_PATCH_VERSION >= 6 && NODE_PATCH_VERSION <= 10)) || NODE_MINOR_VERSION >= 11 + wrapper->SetAlignedPointerInInternalField(0, this); +#else + wrapper->SetPointerInInternalField(0, this); +#endif + return scope.Close(wrapper); + } + + Persistent createV8Context() { + HandleScope scope; + Local ftmpl = FunctionTemplate::New(); + ftmpl->SetHiddenPrototype(true); + ftmpl->SetClassName(sandbox->GetConstructorName()); + Local otmpl = ftmpl->InstanceTemplate(); + otmpl->SetNamedPropertyHandler(GlobalPropertyGetter, + GlobalPropertySetter, + GlobalPropertyQuery, + GlobalPropertyDeleter, + GlobalPropertyEnumerator, + createDataWrapper()); + otmpl->SetAccessCheckCallbacks(GlobalPropertyNamedAccessCheck, + GlobalPropertyIndexedAccessCheck); + return Context::New(NULL, otmpl); + } + + static void Init(Handle target) { + HandleScope scope; + dataWrapperTmpl = Persistent::New(FunctionTemplate::New()); + dataWrapperTmpl->InstanceTemplate()->SetInternalFieldCount(1); + dataWrapperCtor = Persistent::New(dataWrapperTmpl->GetFunction()); + + jsTmpl = Persistent::New(FunctionTemplate::New(New)); + jsTmpl->InstanceTemplate()->SetInternalFieldCount(1); + jsTmpl->SetClassName(String::NewSymbol("ContextifyContext")); + + NODE_SET_PROTOTYPE_METHOD(jsTmpl, "run", ContextifyContext::Run); + NODE_SET_PROTOTYPE_METHOD(jsTmpl, "getGlobal", ContextifyContext::GetGlobal); + + target->Set(String::NewSymbol("ContextifyContext"), jsTmpl->GetFunction()); + } + + // args[0] = the sandbox object + static Handle New(const Arguments& args) { + HandleScope scope; + if (args.Length() < 1) { + Local msg = String::New("Wrong number of arguments passed to ContextifyContext constructor"); + return ThrowException(Exception::Error(msg)); + } + if (!args[0]->IsObject()) { + Local msg = String::New("Argument to ContextifyContext constructor must be an object."); + return ThrowException(Exception::Error(msg)); + } + ContextifyContext* ctx = new ContextifyContext(args[0]->ToObject()); + ctx->Wrap(args.This()); + return args.This(); + } + + static Handle Run(const Arguments& args) { + HandleScope scope; + if (args.Length() == 0) { + Local msg = String::New("Must supply at least 1 argument to run"); + return ThrowException(Exception::Error(msg)); + } + if (!args[0]->IsString()) { + Local msg = String::New("First argument to run must be a String."); + return ThrowException(Exception::Error(msg)); + } + ContextifyContext* ctx = ObjectWrap::Unwrap(args.This()); + Persistent context = ctx->context; + context->Enter(); + Local code = args[0]->ToString(); + TryCatch trycatch; + Handle + , Style: "style" //Special tag + , Tag: "tag" //Any tag that isn't special +} + +function Parser (handler, options) { + this._options = options ? options : { }; + if (this._options.includeLocation == undefined) { + this._options.includeLocation = false; //Do not track element position in document by default + } + + this.validateHandler(handler); + this._handler = handler; + this.reset(); +} + + //**"Static"**// + //Regular expressions used for cleaning up and parsing (stateless) + Parser._reTrim = /(^\s+|\s+$)/g; //Trim leading/trailing whitespace + Parser._reTrimComment = /(^\!--|--$)/g; //Remove comment tag markup from comment contents + Parser._reWhitespace = /\s/g; //Used to find any whitespace to split on + Parser._reTagName = /^\s*(\/?)\s*([^\s\/]+)/; //Used to find the tag name for an element + + //Regular expressions used for parsing (stateful) + Parser._reAttrib = //Find attributes in a tag + /([^=<>\"\'\s]+)\s*=\s*"([^"]*)"|([^=<>\"\'\s]+)\s*=\s*'([^']*)'|([^=<>\"\'\s]+)\s*=\s*([^'"\s]+)|([^=<>\"\'\s\/]+)/g; + Parser._reTags = /[\<\>]/g; //Find tag markers + + //**Public**// + //Methods// + //Parses a complete HTML and pushes it to the handler + Parser.prototype.parseComplete = function Parser$parseComplete (data) { + this.reset(); + this.parseChunk(data); + this.done(); + } + + //Parses a piece of an HTML document + Parser.prototype.parseChunk = function Parser$parseChunk (data) { + if (this._done) + this.handleError(new Error("Attempted to parse chunk after parsing already done")); + this._buffer += data; //FIXME: this can be a bottleneck + this.parseTags(); + } + + //Tells the parser that the HTML being parsed is complete + Parser.prototype.done = function Parser$done () { + if (this._done) + return; + this._done = true; + + //Push any unparsed text into a final element in the element list + if (this._buffer.length) { + var rawData = this._buffer; + this._buffer = ""; + var element = { + raw: rawData + , data: (this._parseState == ElementType.Text) ? rawData : rawData.replace(Parser._reTrim, "") + , type: this._parseState + }; + if (this._parseState == ElementType.Tag || this._parseState == ElementType.Script || this._parseState == ElementType.Style) + element.name = this.parseTagName(element.data); + this.parseAttribs(element); + this._elements.push(element); + } + + this.writeHandler(); + this._handler.done(); + } + + //Resets the parser to a blank state, ready to parse a new HTML document + Parser.prototype.reset = function Parser$reset () { + this._buffer = ""; + this._done = false; + this._elements = []; + this._elementsCurrent = 0; + this._current = 0; + this._next = 0; + this._location = { + row: 0 + , col: 0 + , charOffset: 0 + , inBuffer: 0 + }; + this._parseState = ElementType.Text; + this._prevTagSep = ''; + this._tagStack = []; + this._handler.reset(); + } + + //**Private**// + //Properties// + Parser.prototype._options = null; //Parser options for how to behave + Parser.prototype._handler = null; //Handler for parsed elements + Parser.prototype._buffer = null; //Buffer of unparsed data + Parser.prototype._done = false; //Flag indicating whether parsing is done + Parser.prototype._elements = null; //Array of parsed elements + Parser.prototype._elementsCurrent = 0; //Pointer to last element in _elements that has been processed + Parser.prototype._current = 0; //Position in data that has already been parsed + Parser.prototype._next = 0; //Position in data of the next tag marker (<>) + Parser.prototype._location = null; //Position tracking for elements in a stream + Parser.prototype._parseState = ElementType.Text; //Current type of element being parsed + Parser.prototype._prevTagSep = ''; //Previous tag marker found + //Stack of element types previously encountered; keeps track of when + //parsing occurs inside a script/comment/style tag + Parser.prototype._tagStack = null; + + //Methods// + //Takes an array of elements and parses any found attributes + Parser.prototype.parseTagAttribs = function Parser$parseTagAttribs (elements) { + var idxEnd = elements.length; + var idx = 0; + + while (idx < idxEnd) { + var element = elements[idx++]; + if (element.type == ElementType.Tag || element.type == ElementType.Script || element.type == ElementType.style) + this.parseAttribs(element); + } + + return(elements); + } + + //Takes an element and adds an "attribs" property for any element attributes found + Parser.prototype.parseAttribs = function Parser$parseAttribs (element) { + //Only parse attributes for tags + if (element.type != ElementType.Script && element.type != ElementType.Style && element.type != ElementType.Tag) + return; + + var tagName = element.data.split(Parser._reWhitespace, 1)[0]; + var attribRaw = element.data.substring(tagName.length); + if (attribRaw.length < 1) + return; + + var match; + Parser._reAttrib.lastIndex = 0; + while (match = Parser._reAttrib.exec(attribRaw)) { + if (element.attribs == undefined) + element.attribs = {}; + + if (typeof match[1] == "string" && match[1].length) { + element.attribs[match[1]] = match[2]; + } else if (typeof match[3] == "string" && match[3].length) { + element.attribs[match[3].toString()] = match[4].toString(); + } else if (typeof match[5] == "string" && match[5].length) { + element.attribs[match[5]] = match[6]; + } else if (typeof match[7] == "string" && match[7].length) { + element.attribs[match[7]] = match[7]; + } + } + } + + //Extracts the base tag name from the data value of an element + Parser.prototype.parseTagName = function Parser$parseTagName (data) { + if (data == null || data == "") + return(""); + var match = Parser._reTagName.exec(data); + if (!match) + return(""); + return((match[1] ? "/" : "") + match[2]); + } + + //Parses through HTML text and returns an array of found elements + //I admit, this function is rather large but splitting up had an noticeable impact on speed + Parser.prototype.parseTags = function Parser$parseTags () { + var bufferEnd = this._buffer.length - 1; + while (Parser._reTags.test(this._buffer)) { + this._next = Parser._reTags.lastIndex - 1; + var tagSep = this._buffer.charAt(this._next); //The currently found tag marker + var rawData = this._buffer.substring(this._current, this._next); //The next chunk of data to parse + + //A new element to eventually be appended to the element list + var element = { + raw: rawData + , data: (this._parseState == ElementType.Text) ? rawData : rawData.replace(Parser._reTrim, "") + , type: this._parseState + }; + + var elementName = this.parseTagName(element.data); + + //This section inspects the current tag stack and modifies the current + //element if we're actually parsing a special area (script/comment/style tag) + if (this._tagStack.length) { //We're parsing inside a script/comment/style tag + if (this._tagStack[this._tagStack.length - 1] == ElementType.Script) { //We're currently in a script tag + if (elementName.toLowerCase() == "/script") //Actually, we're no longer in a script tag, so pop it off the stack + this._tagStack.pop(); + else { //Not a closing script tag + if (element.raw.indexOf("!--") != 0) { //Make sure we're not in a comment + //All data from here to script close is now a text element + element.type = ElementType.Text; + //If the previous element is text, append the current text to it + if (this._elements.length && this._elements[this._elements.length - 1].type == ElementType.Text) { + var prevElement = this._elements[this._elements.length - 1]; + prevElement.raw = prevElement.data = prevElement.raw + this._prevTagSep + element.raw; + element.raw = element.data = ""; //This causes the current element to not be added to the element list + } + } + } + } + else if (this._tagStack[this._tagStack.length - 1] == ElementType.Style) { //We're currently in a style tag + if (elementName.toLowerCase() == "/style") //Actually, we're no longer in a style tag, so pop it off the stack + this._tagStack.pop(); + else { + if (element.raw.indexOf("!--") != 0) { //Make sure we're not in a comment + //All data from here to style close is now a text element + element.type = ElementType.Text; + //If the previous element is text, append the current text to it + if (this._elements.length && this._elements[this._elements.length - 1].type == ElementType.Text) { + var prevElement = this._elements[this._elements.length - 1]; + if (element.raw != "") { + prevElement.raw = prevElement.data = prevElement.raw + this._prevTagSep + element.raw; + element.raw = element.data = ""; //This causes the current element to not be added to the element list + } else { //Element is empty, so just append the last tag marker found + prevElement.raw = prevElement.data = prevElement.raw + this._prevTagSep; + } + } else { //The previous element was not text + if (element.raw != "") { + element.raw = element.data = element.raw; + } + } + } + } + } + else if (this._tagStack[this._tagStack.length - 1] == ElementType.Comment) { //We're currently in a comment tag + var rawLen = element.raw.length; + if (element.raw.charAt(rawLen - 2) == "-" && element.raw.charAt(rawLen - 1) == "-" && tagSep == ">") { + //Actually, we're no longer in a style tag, so pop it off the stack + this._tagStack.pop(); + //If the previous element is a comment, append the current text to it + if (this._elements.length && this._elements[this._elements.length - 1].type == ElementType.Comment) { + var prevElement = this._elements[this._elements.length - 1]; + prevElement.raw = prevElement.data = (prevElement.raw + element.raw).replace(Parser._reTrimComment, ""); + element.raw = element.data = ""; //This causes the current element to not be added to the element list + element.type = ElementType.Text; + } + else //Previous element not a comment + element.type = ElementType.Comment; //Change the current element's type to a comment + } + else { //Still in a comment tag + element.type = ElementType.Comment; + //If the previous element is a comment, append the current text to it + if (this._elements.length && this._elements[this._elements.length - 1].type == ElementType.Comment) { + var prevElement = this._elements[this._elements.length - 1]; + prevElement.raw = prevElement.data = prevElement.raw + element.raw + tagSep; + element.raw = element.data = ""; //This causes the current element to not be added to the element list + element.type = ElementType.Text; + } + else + element.raw = element.data = element.raw + tagSep; + } + } + } + + //Processing of non-special tags + if (element.type == ElementType.Tag) { + element.name = elementName; + var elementNameCI = elementName.toLowerCase(); + + if (element.raw.indexOf("!--") == 0) { //This tag is really comment + element.type = ElementType.Comment; + delete element["name"]; + var rawLen = element.raw.length; + //Check if the comment is terminated in the current element + if (element.raw.charAt(rawLen - 1) == "-" && element.raw.charAt(rawLen - 2) == "-" && tagSep == ">") + element.raw = element.data = element.raw.replace(Parser._reTrimComment, ""); + else { //It's not so push the comment onto the tag stack + element.raw += tagSep; + this._tagStack.push(ElementType.Comment); + } + } + else if (element.raw.indexOf("!") == 0 || element.raw.indexOf("?") == 0) { + element.type = ElementType.Directive; + //TODO: what about CDATA? + } + else if (elementNameCI == "script") { + element.type = ElementType.Script; + //Special tag, push onto the tag stack if not terminated + if (element.data.charAt(element.data.length - 1) != "/") + this._tagStack.push(ElementType.Script); + } + else if (elementNameCI == "/script") + element.type = ElementType.Script; + else if (elementNameCI == "style") { + element.type = ElementType.Style; + //Special tag, push onto the tag stack if not terminated + if (element.data.charAt(element.data.length - 1) != "/") + this._tagStack.push(ElementType.Style); + } + else if (elementNameCI == "/style") + element.type = ElementType.Style; + if (element.name && element.name.charAt(0) == "/") + element.data = element.name; + } + + //Add all tags and non-empty text elements to the element list + if (element.raw != "" || element.type != ElementType.Text) { + if (this._options.includeLocation && !element.location) { + element.location = this.getLocation(element.type == ElementType.Tag); + } + this.parseAttribs(element); + this._elements.push(element); + //If tag self-terminates, add an explicit, separate closing tag + if ( + element.type != ElementType.Text + && + element.type != ElementType.Comment + && + element.type != ElementType.Directive + && + element.data.charAt(element.data.length - 1) == "/" + ) + this._elements.push({ + raw: "/" + element.name + , data: "/" + element.name + , name: "/" + element.name + , type: element.type + }); + } + this._parseState = (tagSep == "<") ? ElementType.Tag : ElementType.Text; + this._current = this._next + 1; + this._prevTagSep = tagSep; + } + + if (this._options.includeLocation) { + this.getLocation(); + this._location.row += this._location.inBuffer; + this._location.inBuffer = 0; + this._location.charOffset = 0; + } + this._buffer = (this._current <= bufferEnd) ? this._buffer.substring(this._current) : ""; + this._current = 0; + + this.writeHandler(); + } + + Parser.prototype.getLocation = function Parser$getLocation (startTag) { + var c, + l = this._location, + end = this._current - (startTag ? 1 : 0), + chunk = startTag && l.charOffset == 0 && this._current == 0; + + for (; l.charOffset < end; l.charOffset++) { + c = this._buffer.charAt(l.charOffset); + if (c == '\n') { + l.inBuffer++; + l.col = 0; + } else if (c != '\r') { + l.col++; + } + } + return { + line: l.row + l.inBuffer + 1 + , col: l.col + (chunk ? 0: 1) + }; + } + + //Checks the handler to make it is an object with the right "interface" + Parser.prototype.validateHandler = function Parser$validateHandler (handler) { + if ((typeof handler) != "object") + throw new Error("Handler is not an object"); + if ((typeof handler.reset) != "function") + throw new Error("Handler method 'reset' is invalid"); + if ((typeof handler.done) != "function") + throw new Error("Handler method 'done' is invalid"); + if ((typeof handler.writeTag) != "function") + throw new Error("Handler method 'writeTag' is invalid"); + if ((typeof handler.writeText) != "function") + throw new Error("Handler method 'writeText' is invalid"); + if ((typeof handler.writeComment) != "function") + throw new Error("Handler method 'writeComment' is invalid"); + if ((typeof handler.writeDirective) != "function") + throw new Error("Handler method 'writeDirective' is invalid"); + } + + //Writes parsed elements out to the handler + Parser.prototype.writeHandler = function Parser$writeHandler (forceFlush) { + forceFlush = !!forceFlush; + if (this._tagStack.length && !forceFlush) + return; + while (this._elements.length) { + var element = this._elements.shift(); + switch (element.type) { + case ElementType.Comment: + this._handler.writeComment(element); + break; + case ElementType.Directive: + this._handler.writeDirective(element); + break; + case ElementType.Text: + this._handler.writeText(element); + break; + default: + this._handler.writeTag(element); + break; + } + } + } + + Parser.prototype.handleError = function Parser$handleError (error) { + if ((typeof this._handler.error) == "function") + this._handler.error(error); + else + throw error; + } + +//TODO: make this a trully streamable handler +function RssHandler (callback) { + RssHandler.super_.call(this, callback, { ignoreWhitespace: true, verbose: false, enforceEmptyTags: false }); +} +inherits(RssHandler, DefaultHandler); + + RssHandler.prototype.done = function RssHandler$done () { + var feed = { }; + var feedRoot; + + var found = DomUtils.getElementsByTagName(function (value) { return(value == "rss" || value == "feed"); }, this.dom, false); + if (found.length) { + feedRoot = found[0]; + } + if (feedRoot) { + if (feedRoot.name == "rss") { + feed.type = "rss"; + feedRoot = feedRoot.children[0]; // + feed.id = ""; + try { + feed.title = DomUtils.getElementsByTagName("title", feedRoot.children, false)[0].children[0].data; + } catch (ex) { } + try { + feed.link = DomUtils.getElementsByTagName("link", feedRoot.children, false)[0].children[0].data; + } catch (ex) { } + try { + feed.description = DomUtils.getElementsByTagName("description", feedRoot.children, false)[0].children[0].data; + } catch (ex) { } + try { + feed.updated = new Date(DomUtils.getElementsByTagName("lastBuildDate", feedRoot.children, false)[0].children[0].data); + } catch (ex) { } + try { + feed.author = DomUtils.getElementsByTagName("managingEditor", feedRoot.children, false)[0].children[0].data; + } catch (ex) { } + feed.items = []; + DomUtils.getElementsByTagName("item", feedRoot.children).forEach(function (item, index, list) { + var entry = {}; + try { + entry.id = DomUtils.getElementsByTagName("guid", item.children, false)[0].children[0].data; + } catch (ex) { } + try { + entry.title = DomUtils.getElementsByTagName("title", item.children, false)[0].children[0].data; + } catch (ex) { } + try { + entry.link = DomUtils.getElementsByTagName("link", item.children, false)[0].children[0].data; + } catch (ex) { } + try { + entry.description = DomUtils.getElementsByTagName("description", item.children, false)[0].children[0].data; + } catch (ex) { } + try { + entry.pubDate = new Date(DomUtils.getElementsByTagName("pubDate", item.children, false)[0].children[0].data); + } catch (ex) { } + feed.items.push(entry); + }); + } else { + feed.type = "atom"; + try { + feed.id = DomUtils.getElementsByTagName("id", feedRoot.children, false)[0].children[0].data; + } catch (ex) { } + try { + feed.title = DomUtils.getElementsByTagName("title", feedRoot.children, false)[0].children[0].data; + } catch (ex) { } + try { + feed.link = DomUtils.getElementsByTagName("link", feedRoot.children, false)[0].attribs.href; + } catch (ex) { } + try { + feed.description = DomUtils.getElementsByTagName("subtitle", feedRoot.children, false)[0].children[0].data; + } catch (ex) { } + try { + feed.updated = new Date(DomUtils.getElementsByTagName("updated", feedRoot.children, false)[0].children[0].data); + } catch (ex) { } + try { + feed.author = DomUtils.getElementsByTagName("email", feedRoot.children, true)[0].children[0].data; + } catch (ex) { } + feed.items = []; + DomUtils.getElementsByTagName("entry", feedRoot.children).forEach(function (item, index, list) { + var entry = {}; + try { + entry.id = DomUtils.getElementsByTagName("id", item.children, false)[0].children[0].data; + } catch (ex) { } + try { + entry.title = DomUtils.getElementsByTagName("title", item.children, false)[0].children[0].data; + } catch (ex) { } + try { + entry.link = DomUtils.getElementsByTagName("link", item.children, false)[0].attribs.href; + } catch (ex) { } + try { + entry.description = DomUtils.getElementsByTagName("summary", item.children, false)[0].children[0].data; + } catch (ex) { } + try { + entry.pubDate = new Date(DomUtils.getElementsByTagName("updated", item.children, false)[0].children[0].data); + } catch (ex) { } + feed.items.push(entry); + }); + } + + this.dom = feed; + } + RssHandler.super_.prototype.done.call(this); + } + +/////////////////////////////////////////////////// + +function DefaultHandler (callback, options) { + this.reset(); + this._options = options ? options : { }; + if (this._options.ignoreWhitespace == undefined) + this._options.ignoreWhitespace = false; //Keep whitespace-only text nodes + if (this._options.verbose == undefined) + this._options.verbose = true; //Keep data property for tags and raw property for all + if (this._options.enforceEmptyTags == undefined) + this._options.enforceEmptyTags = true; //Don't allow children for HTML tags defined as empty in spec + if ((typeof callback) == "function") + this._callback = callback; +} + + //**"Static"**// + //HTML Tags that shouldn't contain child nodes + DefaultHandler._emptyTags = { + area: 1 + , base: 1 + , basefont: 1 + , br: 1 + , col: 1 + , frame: 1 + , hr: 1 + , img: 1 + , input: 1 + , isindex: 1 + , link: 1 + , meta: 1 + , param: 1 + , embed: 1 + } + //Regex to detect whitespace only text nodes + DefaultHandler.reWhitespace = /^\s*$/; + + //**Public**// + //Properties// + DefaultHandler.prototype.dom = null; //The hierarchical object containing the parsed HTML + //Methods// + //Resets the handler back to starting state + DefaultHandler.prototype.reset = function DefaultHandler$reset() { + this.dom = []; + this._done = false; + this._tagStack = []; + this._tagStack.last = function DefaultHandler$_tagStack$last () { + return(this.length ? this[this.length - 1] : null); + } + } + //Signals the handler that parsing is done + DefaultHandler.prototype.done = function DefaultHandler$done () { + this._done = true; + this.handleCallback(null); + } + DefaultHandler.prototype.writeTag = function DefaultHandler$writeTag (element) { + this.handleElement(element); + } + DefaultHandler.prototype.writeText = function DefaultHandler$writeText (element) { + if (this._options.ignoreWhitespace) + if (DefaultHandler.reWhitespace.test(element.data)) + return; + this.handleElement(element); + } + DefaultHandler.prototype.writeComment = function DefaultHandler$writeComment (element) { + this.handleElement(element); + } + DefaultHandler.prototype.writeDirective = function DefaultHandler$writeDirective (element) { + this.handleElement(element); + } + DefaultHandler.prototype.error = function DefaultHandler$error (error) { + this.handleCallback(error); + } + + //**Private**// + //Properties// + DefaultHandler.prototype._options = null; //Handler options for how to behave + DefaultHandler.prototype._callback = null; //Callback to respond to when parsing done + DefaultHandler.prototype._done = false; //Flag indicating whether handler has been notified of parsing completed + DefaultHandler.prototype._tagStack = null; //List of parents to the currently element being processed + //Methods// + DefaultHandler.prototype.handleCallback = function DefaultHandler$handleCallback (error) { + if ((typeof this._callback) != "function") + if (error) + throw error; + else + return; + this._callback(error, this.dom); + } + + DefaultHandler.prototype.isEmptyTag = function(element) { + var name = element.name.toLowerCase(); + if (name.charAt(0) == '/') { + name = name.substring(1); + } + return this._options.enforceEmptyTags && !!DefaultHandler._emptyTags[name]; + }; + + DefaultHandler.prototype.handleElement = function DefaultHandler$handleElement (element) { + if (this._done) + this.handleCallback(new Error("Writing to the handler after done() called is not allowed without a reset()")); + if (!this._options.verbose) { +// element.raw = null; //FIXME: Not clean + //FIXME: Serious performance problem using delete + delete element.raw; + if (element.type == "tag" || element.type == "script" || element.type == "style") + delete element.data; + } + if (!this._tagStack.last()) { //There are no parent elements + //If the element can be a container, add it to the tag stack and the top level list + if (element.type != ElementType.Text && element.type != ElementType.Comment && element.type != ElementType.Directive) { + if (element.name.charAt(0) != "/") { //Ignore closing tags that obviously don't have an opening tag + this.dom.push(element); + if (!this.isEmptyTag(element)) { //Don't add tags to the tag stack that can't have children + this._tagStack.push(element); + } + } + } + else //Otherwise just add to the top level list + this.dom.push(element); + } + else { //There are parent elements + //If the element can be a container, add it as a child of the element + //on top of the tag stack and then add it to the tag stack + if (element.type != ElementType.Text && element.type != ElementType.Comment && element.type != ElementType.Directive) { + if (element.name.charAt(0) == "/") { + //This is a closing tag, scan the tagStack to find the matching opening tag + //and pop the stack up to the opening tag's parent + var baseName = element.name.substring(1); + if (!this.isEmptyTag(element)) { + var pos = this._tagStack.length - 1; + while (pos > -1 && this._tagStack[pos--].name != baseName) { } + if (pos > -1 || this._tagStack[0].name == baseName) + while (pos < this._tagStack.length - 1) + this._tagStack.pop(); + } + } + else { //This is not a closing tag + if (!this._tagStack.last().children) + this._tagStack.last().children = []; + this._tagStack.last().children.push(element); + if (!this.isEmptyTag(element)) //Don't add tags to the tag stack that can't have children + this._tagStack.push(element); + } + } + else { //This is not a container element + if (!this._tagStack.last().children) + this._tagStack.last().children = []; + this._tagStack.last().children.push(element); + } + } + } + + var DomUtils = { + testElement: function DomUtils$testElement (options, element) { + if (!element) { + return false; + } + + for (var key in options) { + if (key == "tag_name") { + if (element.type != "tag" && element.type != "script" && element.type != "style") { + return false; + } + if (!options["tag_name"](element.name)) { + return false; + } + } else if (key == "tag_type") { + if (!options["tag_type"](element.type)) { + return false; + } + } else if (key == "tag_contains") { + if (element.type != "text" && element.type != "comment" && element.type != "directive") { + return false; + } + if (!options["tag_contains"](element.data)) { + return false; + } + } else { + if (!element.attribs || !options[key](element.attribs[key])) { + return false; + } + } + } + + return true; + } + + , getElements: function DomUtils$getElements (options, currentElement, recurse, limit) { + recurse = (recurse === undefined || recurse === null) || !!recurse; + limit = isNaN(parseInt(limit)) ? -1 : parseInt(limit); + + if (!currentElement) { + return([]); + } + + var found = []; + var elementList; + + function getTest (checkVal) { + return(function (value) { return(value == checkVal); }); + } + for (var key in options) { + if ((typeof options[key]) != "function") { + options[key] = getTest(options[key]); + } + } + + if (DomUtils.testElement(options, currentElement)) { + found.push(currentElement); + } + + if (limit >= 0 && found.length >= limit) { + return(found); + } + + if (recurse && currentElement.children) { + elementList = currentElement.children; + } else if (currentElement instanceof Array) { + elementList = currentElement; + } else { + return(found); + } + + for (var i = 0; i < elementList.length; i++) { + found = found.concat(DomUtils.getElements(options, elementList[i], recurse, limit)); + if (limit >= 0 && found.length >= limit) { + break; + } + } + + return(found); + } + + , getElementById: function DomUtils$getElementById (id, currentElement, recurse) { + var result = DomUtils.getElements({ id: id }, currentElement, recurse, 1); + return(result.length ? result[0] : null); + } + + , getElementsByTagName: function DomUtils$getElementsByTagName (name, currentElement, recurse, limit) { + return(DomUtils.getElements({ tag_name: name }, currentElement, recurse, limit)); + } + + , getElementsByTagType: function DomUtils$getElementsByTagType (type, currentElement, recurse, limit) { + return(DomUtils.getElements({ tag_type: type }, currentElement, recurse, limit)); + } + } + + function inherits (ctor, superCtor) { + var tempCtor = function(){}; + tempCtor.prototype = superCtor.prototype; + ctor.super_ = superCtor; + ctor.prototype = new tempCtor(); + ctor.prototype.constructor = ctor; + } + +exports.Parser = Parser; + +exports.DefaultHandler = DefaultHandler; + +exports.RssHandler = RssHandler; + +exports.ElementType = ElementType; + +exports.DomUtils = DomUtils; + +})(); diff --git a/server/node_modules/jquery/node_modules/htmlparser/lib/htmlparser.min.js b/server/node_modules/jquery/node_modules/htmlparser/lib/htmlparser.min.js new file mode 100644 index 0000000..c1281c5 --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/lib/htmlparser.min.js @@ -0,0 +1,22 @@ +/*********************************************** +Copyright 2010, 2011, Chris Winberry . All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +***********************************************/ +/* v1.7.6 */ +(function(){function e(a,c){this._options=c?c:{};void 0==this._options.includeLocation&&(this._options.includeLocation=!1);this.validateHandler(a);this._handler=a;this.reset()}function j(a){j.super_.call(this,a,{ignoreWhitespace:!0,verbose:!1,enforceEmptyTags:!1})}function g(a,c){this.reset();this._options=c?c:{};void 0==this._options.ignoreWhitespace&&(this._options.ignoreWhitespace=!1);void 0==this._options.verbose&&(this._options.verbose=!0);void 0==this._options.enforceEmptyTags&&(this._options.enforceEmptyTags= !0);"function"==typeof a&&(this._callback=a)}if(!("function"==typeof require&&"object"==typeof exports&&"object"==typeof module&&"string"==typeof __filename&&"string"==typeof __dirname)){if(this.Tautologistics){if(this.Tautologistics.NodeHtmlParser)return}else this.Tautologistics={};this.Tautologistics.NodeHtmlParser={};exports=this.Tautologistics.NodeHtmlParser}var d={Text:"text",Directive:"directive",Comment:"comment",Script:"script",Style:"style",Tag:"tag"};e._reTrim=/(^\s+|\s+$)/g;e._reTrimComment= /(^\!--|--$)/g;e._reWhitespace=/\s/g;e._reTagName=/^\s*(\/?)\s*([^\s\/]+)/;e._reAttrib=/([^=<>\"\'\s]+)\s*=\s*"([^"]*)"|([^=<>\"\'\s]+)\s*=\s*'([^']*)'|([^=<>\"\'\s]+)\s*=\s*([^'"\s]+)|([^=<>\"\'\s\/]+)/g;e._reTags=/[\<\>]/g;e.prototype.parseComplete=function(a){this.reset();this.parseChunk(a);this.done()};e.prototype.parseChunk=function(a){this._done&&this.handleError(Error("Attempted to parse chunk after parsing already done"));this._buffer+=a;this.parseTags()};e.prototype.done=function(){if(!this._done){this._done= !0;if(this._buffer.length){var a=this._buffer;this._buffer="";a={raw:a,data:this._parseState==d.Text?a:a.replace(e._reTrim,""),type:this._parseState};if(this._parseState==d.Tag||this._parseState==d.Script||this._parseState==d.Style)a.name=this.parseTagName(a.data);this.parseAttribs(a);this._elements.push(a)}this.writeHandler();this._handler.done()}};e.prototype.reset=function(){this._buffer="";this._done=!1;this._elements=[];this._next=this._current=this._elementsCurrent=0;this._location={row:0,col:0, charOffset:0,inBuffer:0};this._parseState=d.Text;this._prevTagSep="";this._tagStack=[];this._handler.reset()};e.prototype._options=null;e.prototype._handler=null;e.prototype._buffer=null;e.prototype._done=!1;e.prototype._elements=null;e.prototype._elementsCurrent=0;e.prototype._current=0;e.prototype._next=0;e.prototype._location=null;e.prototype._parseState=d.Text;e.prototype._prevTagSep="";e.prototype._tagStack=null;e.prototype.parseTagAttribs=function(a){for(var c=a.length,b=0;bc.length)){var b;for(e._reAttrib.lastIndex=0;b=e._reAttrib.exec(c);)void 0==a.attribs&&(a.attribs={}),"string"==typeof b[1]&&b[1].length?a.attribs[b[1]]=b[2]:"string"==typeof b[3]&&b[3].length?a.attribs[b[3].toString()]=b[4].toString():"string"==typeof b[5]&& b[5].length?a.attribs[b[5]]=b[6]:"string"==typeof b[7]&&b[7].length&&(a.attribs[b[7]]=b[7])}}};e.prototype.parseTagName=function(a){if(null==a||""==a)return"";a=e._reTagName.exec(a);return!a?"":(a[1]?"/":"")+a[2]};e.prototype.parseTags=function(){for(var a=this._buffer.length-1;e._reTags.test(this._buffer);){this._next=e._reTags.lastIndex-1;var c=this._buffer.charAt(this._next),b=this._buffer.substring(this._current,this._next),b={raw:b,data:this._parseState==d.Text?b:b.replace(e._reTrim,""),type:this._parseState}, f=this.parseTagName(b.data);if(this._tagStack.length)if(this._tagStack[this._tagStack.length-1]==d.Script)if("/script"==f.toLowerCase())this._tagStack.pop();else{if(0!=b.raw.indexOf("!--")&&(b.type=d.Text,this._elements.length&&this._elements[this._elements.length-1].type==d.Text)){var h=this._elements[this._elements.length-1];h.raw=h.data=h.raw+this._prevTagSep+b.raw;b.raw=b.data=""}}else this._tagStack[this._tagStack.length-1]==d.Style?"/style"==f.toLowerCase()?this._tagStack.pop():0!=b.raw.indexOf("!--")&& (b.type=d.Text,this._elements.length&&this._elements[this._elements.length-1].type==d.Text?(h=this._elements[this._elements.length-1],""!=b.raw?(h.raw=h.data=h.raw+this._prevTagSep+b.raw,b.raw=b.data=""):h.raw=h.data=h.raw+this._prevTagSep):""!=b.raw&&(b.raw=b.data=b.raw)):this._tagStack[this._tagStack.length-1]==d.Comment&&(h=b.raw.length,"-"==b.raw.charAt(h-2)&&"-"==b.raw.charAt(h-1)&&">"==c?(this._tagStack.pop(),this._elements.length&&this._elements[this._elements.length-1].type==d.Comment?(h= this._elements[this._elements.length-1],h.raw=h.data=(h.raw+b.raw).replace(e._reTrimComment,""),b.raw=b.data="",b.type=d.Text):b.type=d.Comment):(b.type=d.Comment,this._elements.length&&this._elements[this._elements.length-1].type==d.Comment?(h=this._elements[this._elements.length-1],h.raw=h.data=h.raw+b.raw+c,b.raw=b.data="",b.type=d.Text):b.raw=b.data=b.raw+c));if(b.type==d.Tag&&(b.name=f,f=f.toLowerCase(),0==b.raw.indexOf("!--")?(b.type=d.Comment,delete b.name,h=b.raw.length,"-"==b.raw.charAt(h- 1)&&"-"==b.raw.charAt(h-2)&&">"==c?b.raw=b.data=b.raw.replace(e._reTrimComment,""):(b.raw+=c,this._tagStack.push(d.Comment))):0==b.raw.indexOf("!")||0==b.raw.indexOf("?")?b.type=d.Directive:"script"==f?(b.type=d.Script,"/"!=b.data.charAt(b.data.length-1)&&this._tagStack.push(d.Script)):"/script"==f?b.type=d.Script:"style"==f?(b.type=d.Style,"/"!=b.data.charAt(b.data.length-1)&&this._tagStack.push(d.Style)):"/style"==f&&(b.type=d.Style),b.name&&"/"==b.name.charAt(0)))b.data=b.name;if(""!=b.raw||b.type!= d.Text)this._options.includeLocation&&!b.location&&(b.location=this.getLocation(b.type==d.Tag)),this.parseAttribs(b),this._elements.push(b),b.type!=d.Text&&b.type!=d.Comment&&b.type!=d.Directive&&"/"==b.data.charAt(b.data.length-1)&&this._elements.push({raw:"/"+b.name,data:"/"+b.name,name:"/"+b.name,type:b.type});this._parseState="<"==c?d.Tag:d.Text;this._current=this._next+1;this._prevTagSep=c}this._options.includeLocation&&(this.getLocation(),this._location.row+=this._location.inBuffer,this._location.inBuffer= 0,this._location.charOffset=0);this._buffer=this._current<=a?this._buffer.substring(this._current):"";this._current=0;this.writeHandler()};e.prototype.getLocation=function(a){for(var c=this._location,b=this._current-(a?1:0),d=a&&0==c.charOffset&&0==this._current;c.charOffset=d)return g;if(b&&c.children)c= c.children;else if(!(c instanceof Array))return g;for(i=0;i=d);i++);return g},getElementById:function(a,c,b){a=f.getElements({id:a},c,b,1);return a.length?a[0]:null},getElementsByTagName:function(a,c,b,d){return f.getElements({tag_name:a},c,b,d)},getElementsByTagType:function(a,c,b,d){return f.getElements({tag_type:a},c,b,d)}};exports.Parser=e;exports.DefaultHandler=g;exports.RssHandler=j;exports.ElementType=d;exports.DomUtils=f})(); \ No newline at end of file diff --git a/server/node_modules/jquery/node_modules/htmlparser/lib/node-htmlparser.js b/server/node_modules/jquery/node_modules/htmlparser/lib/node-htmlparser.js new file mode 100644 index 0000000..1fc03ea --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/lib/node-htmlparser.js @@ -0,0 +1,6 @@ +var htmlparser = require("./htmlparser"); +exports.Parser = htmlparser.Parser; +exports.DefaultHandler = htmlparser.DefaultHandler; +exports.RssHandler = htmlparser.RssHandler; +exports.ElementType = htmlparser.ElementType; +exports.DomUtils = htmlparser.DomUtils; diff --git a/server/node_modules/jquery/node_modules/htmlparser/lib/node-htmlparser.min.js b/server/node_modules/jquery/node_modules/htmlparser/lib/node-htmlparser.min.js new file mode 100644 index 0000000..27d5eea --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/lib/node-htmlparser.min.js @@ -0,0 +1,6 @@ +var htmlparser = require("./htmlparser.min"); +exports.Parser = htmlparser.Parser; +exports.DefaultHandler = htmlparser.DefaultHandler; +exports.RssHandler = htmlparser.RssHandler; +exports.ElementType = htmlparser.ElementType; +exports.DomUtils = htmlparser.DomUtils; diff --git a/server/node_modules/jquery/node_modules/htmlparser/libxmljs.node b/server/node_modules/jquery/node_modules/htmlparser/libxmljs.node new file mode 100644 index 0000000..07e08c8 Binary files /dev/null and b/server/node_modules/jquery/node_modules/htmlparser/libxmljs.node differ diff --git a/server/node_modules/jquery/node_modules/htmlparser/new/a b/server/node_modules/jquery/node_modules/htmlparser/new/a new file mode 100644 index 0000000..4506b37 --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/new/a @@ -0,0 +1,6 @@ +[ +{"type":"tag","name":"script","name_raw":"script","raw":"script language='javascript'"}, +{"type":"attr","name":"langauge","name_raw":"language","value":"javascript"}, +{"type":"text","data":"\nvar foo = 'xxx';\n"}, +{"type":"tag","name":"/script","name_raw":"/script","raw":"/script"} +] diff --git a/server/node_modules/jquery/node_modules/htmlparser/new/b b/server/node_modules/jquery/node_modules/htmlparser/new/b new file mode 100644 index 0000000..759c80f --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/new/b @@ -0,0 +1,6 @@ +[ +{"type":"tag","name":"script","name_raw":"script","raw":"script language='javascript'"}, +{"type":"attr","name":"language","name_raw":"language","value":"javascript"}, +{"type":"text","data":"\nvar foo = 'xxx';\n"}, +{"type":"tag","name":"/script","name_raw":"/script","raw":"/script"} +] diff --git a/server/node_modules/jquery/node_modules/htmlparser/new/compat.js b/server/node_modules/jquery/node_modules/htmlparser/new/compat.js new file mode 100644 index 0000000..eb1f4e7 --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/new/compat.js @@ -0,0 +1,954 @@ +var htmlparser_old = require('../lib/htmlparser'); +var htmlparser_new = require('./htmlparser'); + +var tests = { + + 'plain text': { + data: ['This is the text'] + , expected: [{ type: 'text', data: 'This is the text' }] + } + + , 'split text': { + data: ['This is', ' the text'] + , expected: [{ type: 'text', data: 'This is the text' }] + } + + , 'simple tag': { + data: ['
    '] + , expected: [{ type: 'tag', name: 'div', raw: 'div' }] + } + + , 'simple comment': { + data: [''] + , expected: [{ type: 'comment', data: ' content ' }] + } + + , 'simple cdata': { + data: [''] + , expected: [{ type: 'cdata', data: ' content ' }] + } + + , 'split simple tag #1': { + data: ['<', 'div>'] + , expected: [{ type: 'tag', name: 'div', raw: 'div' }] + } + + , 'split simple tag #2': { + data: [''] + , expected: [{ type: 'tag', name: 'div', raw: 'div' }] + } + + , 'split simple tag #3': { + data: [''] + , expected: [{ type: 'tag', name: 'div', raw: 'div' }] + } + + , 'text before tag': { + data: ['xxx
    '] + , expected: [ + { type: 'text', data: 'xxx'}, + { type: 'tag', name: 'div', raw: 'div' } + ] + } + + , 'text after tag': { + data: ['
    xxx'] + , expected: [ + { type: 'tag', name: 'div', raw: 'div' }, + { type: 'text', data: 'xxx'} + ] + } + + , 'text inside tag': { + data: ['
    xxx
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div' }, + { type: 'text', data: 'xxx'}, + { type: 'tag', name: '/div', raw: '/div' } + ] + } + + , 'attribute with single quotes': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div a=\'1\'' }, + { type: 'attr', name:'a', data: '1'} + ] + } + + , 'attribute with double quotes': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div a="1"' }, + { type: 'attr', name:'a', data: '1'} + ] + } + + , 'attribute with no quotes': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div a=1' }, + { type: 'attr', name:'a', data: '1'} + ] + } + + , 'attribute with no value': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div wierd' }, + { type: 'attr', name:'wierd', data: null} + ] + } + + , 'attribute with no value, trailing text': { + data: ['
    xxx'] + , expected: [ + { type: 'tag', name: 'div', raw: 'div wierd' }, + { type: 'attr', name:'wierd', data: null}, + { type: 'text', data: 'xxx' } + ] + } + + , 'tag with multiple attributes': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div a="1" b="2"' }, + { type: 'attr', name:'a', data: '1'}, + { type: 'attr', name:'b', data: '2'} + ] + } + + , 'tag with multiple attributes, trailing text': { + data: ['
    xxx'] + , expected: [ + { type: 'tag', name: 'div', raw: 'div a="1" b="2"' }, + { type: 'attr', name:'a', data: '1'}, + { type: 'attr', name:'b', data: '2'}, + { type: 'text', data: 'xxx' } + ] + } + + , 'tag with mixed attributes #1': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div a=1 b=\'2\' c="3"' }, + { type: 'attr', name:'a', data: '1'}, + { type: 'attr', name:'b', data: '2'}, + { type: 'attr', name:'c', data: '3'} + ] + } + + , 'tag with mixed attributes #2': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div a=1 b="2" c=\'3\'' }, + { type: 'attr', name:'a', data: '1'}, + { type: 'attr', name:'b', data: '2'}, + { type: 'attr', name:'c', data: '3'} + ] + } + + , 'tag with mixed attributes #3': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div a=\'1\' b=2 c="3"' }, + { type: 'attr', name:'a', data: '1'}, + { type: 'attr', name:'b', data: '2'}, + { type: 'attr', name:'c', data: '3'} + ] + } + + , 'tag with mixed attributes #4': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div a=\'1\' b="2" c=3' }, + { type: 'attr', name:'a', data: '1'}, + { type: 'attr', name:'b', data: '2'}, + { type: 'attr', name:'c', data: '3'} + ] + } + + , 'tag with mixed attributes #5': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div a="1" b=2 c=\'3\'' }, + { type: 'attr', name:'a', data: '1'}, + { type: 'attr', name:'b', data: '2'}, + { type: 'attr', name:'c', data: '3'} + ] + } + + , 'tag with mixed attributes #6': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div a="1" b=\'2\' c="3"' }, + { type: 'attr', name:'a', data: '1'}, + { type: 'attr', name:'b', data: '2'}, + { type: 'attr', name:'c', data: '3'} + ] + } + + , 'tag with mixed attributes, trailing text': { + data: ['
    xxx'] + , expected: [ + { type: 'tag', name: 'div', raw: 'div a=1 b=\'2\' c="3"' }, + { type: 'attr', name:'a', data: '1'}, + { type: 'attr', name:'b', data: '2'}, + { type: 'attr', name:'c', data: '3'}, + { type: 'text', data: 'xxx' } + ] + } + + , 'self closing tag': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div/' }, + { type: 'tag', name: '/div', raw: null } + ] + } + + , 'self closing tag, trailing text': { + data: ['
    xxx'] + , expected: [ + { type: 'tag', name: 'div', raw: 'div/' }, + { type: 'tag', name: '/div', raw: null }, + { type: 'text', data: 'xxx' } + ] + } + + , 'self closing tag with spaces #1': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div /' }, + { type: 'tag', name: '/div', raw: null } + ] + } + + , 'self closing tag with spaces #2': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div/ ' }, + { type: 'tag', name: '/div', raw: null } + ] + } + + , 'self closing tag with spaces #3': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div / ' }, + { type: 'tag', name: '/div', raw: null } + ] + } + + , 'self closing tag with spaces, trailing text': { + data: ['
    xxx'] + , expected: [ + { type: 'tag', name: 'div', raw: 'div / ' }, + { type: 'tag', name: '/div', raw: null }, + { type: 'text', data: 'xxx' } + ] + } + + , 'self closing tag with attribute': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div a=b /' }, + { type: 'attr', name:'a', data: 'b'}, + { type: 'tag', name: '/div', raw: null } + ] + } + + , 'self closing tag with attribute, trailing text': { + data: ['
    xxx'] + , expected: [ + { type: 'tag', name: 'div', raw: 'div a=b /' }, + { type: 'attr', name:'a', data: 'b'}, + { type: 'tag', name: '/div', raw: null }, + { type: 'text', data: 'xxx' } + ] + } + + , 'self closing tag split #1': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div/' }, + { type: 'tag', name: '/div', raw: null } + ] + } + + , 'self closing tag split #2': { + data: [''] + , expected: [ + { type: 'tag', name: 'div', raw: 'div/' }, + { type: 'tag', name: '/div', raw: null } + ] + } + + , 'attribute missing close quote': { + data: ['
    xxx'] + , expected: [ + { type: 'tag', name: 'div', raw: 'div a="1>'] + , expected: [ + { type: 'tag', name: 'div', raw: 'div xxx="yyy"' }, + { type: 'attr', name:'xxx', data: 'yyy'} + ] + } + + , 'split attribute #2': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div xxx="yyy"' }, + { type: 'attr', name:'xxx', data: 'yyy'} + ] + } + + , 'split attribute #3': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div xxx="yyy"' }, + { type: 'attr', name:'xxx', data: 'yyy'} + ] + } + + , 'split attribute #4': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div xxx="yyy"' }, + { type: 'attr', name:'xxx', data: 'yyy'} + ] + } + + , 'split attribute #5': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div xxx="yyy"' }, + { type: 'attr', name:'xxx', data: 'yyy'} + ] + } + + , 'split attribute #6': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div xxx="yyy"' }, + { type: 'attr', name:'xxx', data: 'yyy'} + ] + } + + , 'attribute split from tag #1': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div xxx="yyy"' }, + { type: 'attr', name:'xxx', data: 'yyy'} + ] + } + + , 'attribute split from tag #2': { + data: [''] + , expected: [ + { type: 'tag', name: 'div', raw: 'div xxx="yyy"' }, + { type: 'attr', name:'xxx', data: 'yyy'} + ] + } + + , 'text before complex tag': { + data: ['xxx
    '] + , expected: [ + { type: 'text', data: 'xxx' }, + { type: 'tag', name: 'div', raw: 'div yyy="123"'}, + { type: 'attr', name: 'yyy', data: '123' } + ] + } + + , 'text after complex tag': { + data: ['
    xxx'] + , expected: [ + { type: 'tag', name: 'div', raw: 'div yyy="123"'}, + { type: 'attr', name: 'yyy', data: '123' }, + { type: 'text', data: 'xxx' } + ] + } + + , 'text inside complex tag': { + data: ['
    xxx
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div yyy="123"'}, + { type: 'attr', name: 'yyy', data: '123' }, + { type: 'text', data: 'xxx' }, + { type: 'tag', name: '/div', raw: '/div'} + ] + } + + , 'nested tags': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div'}, + { type: 'tag', name: 'span', raw: 'span'}, + { type: 'tag', name: '/span', raw: '/span'}, + { type: 'tag', name: '/div', raw: '/div'} + ] + } + + , 'nested tags with attributes': { + data: ['
    xxx
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div aaa="bbb"'}, + { type: 'attr', name: 'aaa', data: 'bbb' }, + { type: 'tag', name: 'span', raw: 'span 123=\'456\''}, + { type: 'attr', name: '123', data: '456' }, + { type: 'text', data: 'xxx' }, + { type: 'tag', name: '/span', raw: '/span'}, + { type: 'tag', name: '/div', raw: '/div'} + ] + } + + , 'comment inside tag': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div'}, + { type: 'comment', data: ' comment text '}, + { type: 'tag', name: '/div', raw: '/div'} + ] + } + + , 'cdata inside tag': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div'}, + { type: 'cdata', data: ' CData content '}, + { type: 'tag', name: '/div', raw: '/div'} + ] + } + + , 'html inside comment': { + data: [''] + , expected: [{ type: 'comment', data: '
    foo
    '}] + } + + , 'html inside cdata': { + data: ['foo
    ]]>'] + , expected: [{ type: 'cdata', data: '
    foo
    '}] + } + + , 'quotes in attribute #1': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div xxx=\'a"b\''}, + { type: 'attr', name: 'xxx', data: 'a"b' } + ] + } + + , 'quotes in attribute #2': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div xxx="a\'b"'}, + { type: 'attr', name: 'xxx', data: 'a\'b' } + ] + } + + , 'brackets in attribute': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', raw: 'div xxx="
    "'}, + { type: 'attr', name: 'xxx', data: '
    ' } + ] + } + + , 'split comment #1': { + data: ['<','!-- comment text -->xxx'] + , expected: [ + { type: 'comment', data: ' comment text '}, + { type: 'text', data: 'xxx' } + ] + } + + , 'split comment #2': { + data: ['xxx'] + , expected: [ + { type: 'comment', data: ' comment text '}, + { type: 'text', data: 'xxx' } + ] + } + + , 'split comment #3': { + data: ['xxx'] + , expected: [ + { type: 'comment', data: ' comment text '}, + { type: 'text', data: 'xxx' } + ] + } + + , 'split comment #4': { + data: ['xxx'] + , expected: [ + { type: 'comment', data: ' comment text '}, + { type: 'text', data: 'xxx' } + ] + } + + , 'split comment #5': { + data: ['xxx'] + , expected: [ + { type: 'comment', data: ' comment text '}, + { type: 'text', data: 'xxx' } + ] + } + + , 'split comment #6': { + data: ['xxx'] + , expected: [ + { type: 'comment', data: ' comment text '}, + { type: 'text', data: 'xxx' } + ] + } + + , 'split comment #7': { + data: ['"] + , expected: [ + { type: 'comment', data: "\ncomment text\n" } + ] + } + + , 'cdata comment': { + data: [""] + , expected: [ + { type: 'cdata', data: "\nCData content\n" } + ] + } + + , 'multiline attribute #1': { + data: ["
    "] + , expected: [ + { type: 'tag', name: 'div', raw: "div id='\nxxx\nyyy\n'" }, + { type: 'attr', name: 'id', data: "\nxxx\nyyy\n" } + ] + } + + , 'multiline attribute #2': { + data: ["
    "] + , expected: [ + { type: 'tag', name: 'div', raw: "div id=\"\nxxx\nyyy\n\"" }, + { type: 'attr', name: 'id', data: "\nxxx\nyyy\n" } + ] + } + + , 'tags in script tag code': { + data: [""] + , expected: [ + { type: 'tag', name: 'script', raw: "script language='javascript'" }, + { type: 'attr', name: 'language', data: 'javascript' }, + { type: 'text', data: "\nvar foo = 'xxx';\n" }, + { type: 'tag', name: '/script', raw: "/script" }, + ] + } + + , 'closing script tag in script tag code': { + data: ["';\n"] + , expected: [ + { type: 'tag', name: 'script', raw: "script language='javascript'" }, + { type: 'attr', name: 'language', data: 'javascript' }, + { type: 'text', data: "\nvar foo = '" }, + { type: 'tag', name: '/script', raw: "/script" }, + { type: 'text', data: "';\n" }, + { type: 'tag', name: '/script', raw: "/script" } + ] + } + + , 'comment in script tag code': { + data: [""] + , expected: [ + { type: 'tag', name: 'script', raw: "script language='javascript'" }, + { type: 'attr', name: 'language', data: 'javascript' }, + { type: 'text', data: "\nvar foo = '';\n" }, + { type: 'tag', name: '/script', raw: "/script" }, + ] + } + + , 'cdata in script tag code': { + data: [""] + , expected: [ + { type: 'tag', name: 'script', raw: "script language='javascript'" }, + { type: 'attr', name: 'language', data: 'javascript' }, + { type: 'text', data: "\nvar foo = '';\n" }, + { type: 'tag', name: '/script', raw: "/script" }, + ] + } + + , 'commented script tag code': { + data: [""] + , expected: [ + { type: 'tag', name: 'script', raw: "script language='javascript'" }, + { type: 'attr', name: 'language', data: 'javascript' }, + { type: 'text', data: "\n\n" }, + { type: 'tag', name: '/script', raw: "/script" }, + ] + } + + , 'cdata in script tag': { + data: [""] + , expected: [ + { type: 'tag', name: 'script', raw: "script language='javascript'" }, + { type: 'attr', name: 'language', data: 'javascript' }, + { type: 'text', data: "\nxxx';\n]]>\n" }, + { type: 'tag', name: '/script', raw: "/script" }, + ] + } + +}; + +function runTests (permutator) { + var callback = function handlerCallback (err) { + if (err) { + console.log('Handler error', err); + } + } + var handler = new htmlparser_new.HtmlHandler(callback); + var parser = new htmlparser_new.Parser(handler); + + var passed = 0; + var failed = 0; + + console.time('Tests'); + for (var testName in tests) { + var test = permutator ? permutator(tests[testName]) : tests[testName]; + process.stdout.write('[TEST] ' + testName + ' : '); + parser.reset(); + for (var i = 0, len = test.data.length; i < len; i++) { + parser.parseChunk(test.data[i]); + } + parser.done(); + var expected = JSON.stringify(test.expected); + var result = JSON.stringify(parser.state.output); + if (expected !== result) { + failed++; + process.stdout.write("FAIL\n"); + console.log(' [EXPECTED]', expected); + console.log(' [ RESULT ]', result); + } else { + passed++; + process.stdout.write("Ok\n"); + } + } + console.timeEnd('Tests'); + console.log('Passed tests: ' + passed + '/' + (passed + failed) + ' (' + Math.round(passed / (passed + failed) * 100) + '%)'); +} + +runTests(); +runTests(function (test) { + test.data = test.data.join('').split(''); + return test; +}); + +function handlerCallback (err, dom) { + console.log(err || dom); +} + +var handlerOld = new htmlparser_old.DefaultHandler(handlerCallback); +var parserOld = new htmlparser_old.Parser(handlerOld); + +var handlerNew = new htmlparser_new.HtmlHandler(null, handlerCallback); +var parserNew = new htmlparser_new.Parser(handlerNew); +parserNew.parseComplete(''); + diff --git a/server/node_modules/jquery/node_modules/htmlparser/new/htmlparser.js b/server/node_modules/jquery/node_modules/htmlparser/new/htmlparser.js new file mode 100644 index 0000000..6b9bfe9 --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/new/htmlparser.js @@ -0,0 +1,645 @@ +(function () { + +function runningInNode () { + return( + (typeof require) == "function" + && + (typeof exports) == "object" + && + (typeof module) == "object" + && + (typeof __filename) == "string" + && + (typeof __dirname) == "string" + ); +} + +if (!runningInNode()) { + if (!this.Tautologistics) + this.Tautologistics = {}; + else if (this.Tautologistics.NodeHtmlParser) + return; //NodeHtmlParser already defined! + this.Tautologistics.NodeHtmlParser = {}; + exports = this.Tautologistics.NodeHtmlParser; +} + +var Mode = { + Text: 'text', + Tag: 'tag', + Attr: 'attr', + CData: 'cdata', + Comment: 'comment' +}; + +var re_parseText_scriptClose = /<\s*\/\s*script/ig; +function parseText (state) { + var foundPos; + if (state.isScript) { + re_parseText_scriptClose.lastIndex = state.pos; + foundPos = re_parseText_scriptClose.exec(state.data); + foundPos = (foundPos) ? + foundPos.index + : + -1 + ; + } else { + foundPos = state.data.indexOf('<', state.pos); + } + var text = (foundPos === -1) ? state.data.substring(state.pos, state.data.length) : state.data.substring(state.pos, foundPos); + if (foundPos < 0 && state.done) { + foundPos = state.data.length; + } + if (foundPos < 0) { + if (state.isScript) { + state.needData = true; + return; + } + if (!state.pendingText) { + state.pendingText = []; + } + state.pendingText.push(state.data.substring(state.pos, state.data.length)); + state.pos = state.data.length; + } else { + if (state.pendingText) { + state.pendingText.push(state.data.substring(state.pos, foundPos)); + text = state.pendingText.join(''); + state.pendingText = null; + } else { + text = state.data.substring(state.pos, foundPos); + } + if (text !== '') { + state.output.push({ type: Mode.Text, data: text }); + } + state.pos = foundPos + 1; + state.mode = Mode.Tag; + } +} + +var re_parseTag = /\s*(\/?)\s*([^\s>\/]+)(\s*)(>?)/g; +function parseTag (state) { + re_parseTag.lastIndex = state.pos; + var match = re_parseTag.exec(state.data); + if (match) { + if (!match[1] && match[2].substr(0, 3) === '!--') { + state.mode = Mode.Comment; + state.pos += 3; + return; + } + if (!match[1] && match[2].substr(0, 8) === '![CDATA[') { + state.mode = Mode.CData; + state.pos += 8; + return; + } + if (!state.done && (state.pos + match[0].length) === state.data.length) { + //We're at the and of the data, might be incomplete + state.needData = true; + return; + } + var raw; + if (match[4] === '>') { + state.mode = Mode.Text; + raw = match[0].substr(0, match[0].length - 1); + } else { + state.mode = Mode.Attr; + raw = match[0]; + } + state.pos += match[0].length; + var tag = { type: Mode.Tag, name: match[1] + match[2], raw: raw }; + if (state.mode === Mode.Attr) { + state.lastTag = tag; + } + if (tag.name.toLowerCase() === 'script') { + state.isScript = true; + } else if (tag.name.toLowerCase() === '/script') { + state.isScript = false; + } + state.output.push(tag); + } else { + //TODO: end of tag? + //TODO: push to pending? + state.needData = true; + } +} + +var re_parseAttr_findName = /\s*([^=<>\s'"\/]+)\s*/g; +function parseAttr_findName (state) { + re_parseAttr_findName.lastIndex = state.pos; + var match = re_parseAttr_findName.exec(state.data); + if (!match) { + return null; + } + if (state.pos + match[0].length !== re_parseAttr_findName.lastIndex) { + return null; + } + return { + match: match[0] + , name: match[1] + }; +} +var re_parseAttr_findValue = /\s*=\s*(?:'([^']*)'|"([^"]*)"|([^'"\s\/>]+))\s*/g; +var re_parseAttr_findValue_last = /\s*=\s*['"]?(.*)$/g; +function parseAttr_findValue (state) { + re_parseAttr_findValue.lastIndex = state.pos; + var match = re_parseAttr_findValue.exec(state.data); + if (!match) { + if (!state.done) { + return null; + } + re_parseAttr_findValue_last.lastIndex = state.pos; + match = re_parseAttr_findValue_last.exec(state.data); + if (!match) { + return null; + } + return { + match: match[0] + , value: (match[1] !== '') ? match[1] : null + }; + } + if (state.pos + match[0].length !== re_parseAttr_findValue.lastIndex) { + return null; + } + return { + match: match[0] + , value: match[1] || match[2] || match[3] + }; +} +var re_parseAttr_splitValue = /\s*=\s*['"]?/g; +var re_parseAttr_selfClose = /(\s*\/\s*)(>?)/g; +function parseAttr (state) { + var name_data = parseAttr_findName(state); + if (!name_data) { + re_parseAttr_selfClose.lastIndex = state.pos; + var matchTrailingSlash = re_parseAttr_selfClose.exec(state.data); + if (matchTrailingSlash && matchTrailingSlash.index === state.pos) { + if (!state.done && !matchTrailingSlash[2] && state.pos + matchTrailingSlash[0].length === state.data.length) { + state.needData = true; + return; + } + state.lastTag.raw += matchTrailingSlash[1]; + state.output.push({ type: Mode.Tag, name: '/' + state.lastTag.name, raw: null }); + state.pos += matchTrailingSlash[1].length; + } + var foundPos = state.data.indexOf('>', state.pos); + if (foundPos < 0) { + if (state.done) { //TODO: is this needed? + state.lastTag.raw += state.data.substr(state.pos); + state.pos = state.data.length; + return; + } + state.needData = true; + } else { + // state.lastTag = null; + state.pos = foundPos + 1; + state.mode = Mode.Text; + } + return; + } + if (!state.done && state.pos + name_data.match.length === state.data.length) { + state.needData = true; + return null; + } + state.pos += name_data.match.length; + var value_data = parseAttr_findValue(state); + if (value_data) { + if (!state.done && state.pos + value_data.match.length === state.data.length) { + state.needData = true; + state.pos -= name_data.match.length; + return; + } + state.pos += value_data.match.length; + } else { + re_parseAttr_splitValue.lastIndex = state.pos; + if (re_parseAttr_splitValue.exec(state.data)) { + state.needData = true; + state.pos -= name_data.match.length; + return; + } + value_data = { + match: '' + , value: null + }; + } + state.lastTag.raw += name_data.match + value_data.match; + + state.output.push({ type: Mode.Attr, name: name_data.name, data: value_data.value }); +} + +var re_parseCData_findEnding = /\]{1,2}$/; +function parseCData (state) { + var foundPos = state.data.indexOf(']]>', state.pos); + if (foundPos < 0 && state.done) { + foundPos = state.data.length; + } + if (foundPos < 0) { + re_parseCData_findEnding.lastIndex = state.pos; + var matchPartialCDataEnd = re_parseCData_findEnding.exec(state.data); + if (matchPartialCDataEnd) { + state.needData = true; + return; + } + if (!state.pendingText) { + state.pendingText = []; + } + state.pendingText.push(state.data.substr(state.pos, state.data.length)); + state.pos = state.data.length; + state.needData = true; + } else { + var text; + if (state.pendingText) { + state.pendingText.push(state.data.substring(state.pos, foundPos)); + text = state.pendingText.join(''); + state.pendingText = null; + } else { + text = state.data.substring(state.pos, foundPos); + } + state.output.push({ type: Mode.CData, data: text }); + state.mode = Mode.Text; + state.pos = foundPos + 3; + } +} + +var re_parseComment_findEnding = /\-{1,2}$/; +function parseComment (state) { + var foundPos = state.data.indexOf('-->', state.pos); + if (foundPos < 0 && state.done) { + foundPos = state.data.length; + } + if (foundPos < 0) { + re_parseComment_findEnding.lastIndex = state.pos; + var matchPartialCommentEnd = re_parseComment_findEnding.exec(state.data); + if (matchPartialCommentEnd) { + state.needData = true; + return; + } + if (!state.pendingText) { + state.pendingText = []; + } + state.pendingText.push(state.data.substr(state.pos, state.data.length)); + state.pos = state.data.length; + state.needData = true; + } else { + var text; + if (state.pendingText) { + state.pendingText.push(state.data.substring(state.pos, foundPos)); + text = state.pendingText.join(''); + state.pendingText = null; + } else { + text = state.data.substring(state.pos, foundPos); + } + state.output.push({ type: Mode.Comment, data: text }); + state.mode = Mode.Text; + state.pos = foundPos + 3; + } +} + +function parse (state) { + switch (state.mode) { + case Mode.Text: + return parseText(state); + case Mode.Tag: + return parseTag(state); + case Mode.Attr: + return parseAttr(state); + case Mode.CData: + return parseCData(state); + case Mode.Comment: + return parseComment(state); + } +} + +function Parser (handler, options) { + this._options = options ? options : { }; + if (this._options.includeLocation == undefined) { + this._options.includeLocation = false; //Do not track element position in document by default + } + + this.validateHandler(handler); + var self = this; + this._handler = handler; + this.reset(); +} + + Parser.prototype.reset = function Parser$reset () { + this.state = { + mode: Mode.Text, + pos: 0, + data: null, + pendingText: null, + lastTag: null, + isScript: false, + needData: false, + // output: [], + done: false + }; + }; + + Parser.prototype.parseChunk = function Parser$parseChunk (chunk) { + this.state.needData = false; + this.state.data = (this.state.data !== null) ? + this.state.data.substr(this.pos) + chunk + : + chunk + ; + while (this.state.pos < this.state.data.length && !this.state.needData) { + parse(this.state); + } + }; + + Parser.prototype.parseComplete = function Parser$parseComplete (data) { + this.reset(); + this.parseChunk(data); + this.done(); + } + + Parser.prototype.done = function Parser$done () { + this.state.done = true; + parse(this.state); + + }; + + Parser.prototype.validateHandler = function Parser$validateHandler (handler) { + if ((typeof handler) != "object") { + throw new Error("Handler is not an object"); + } + if ((typeof handler.reset) != "function") { + throw new Error("Handler method 'reset' is invalid"); + } + if ((typeof handler.done) != "function") { + throw new Error("Handler method 'done' is invalid"); + } + if ((typeof handler.write) != "function") { + throw new Error("Handler method 'write' is invalid"); + } + if ((typeof handler.error) != "function") { + throw new Error("Handler method 'error' is invalid"); + } + } + +// Parser.prototype.done_old = function Parser$done_old () { +// if (this.state.pendingText) { +// this.state.output.push({ type: this.state.mode, data: this.state.pendingText.join('') }); +// this.state.pendingText = null; +// } +// console.log(this.state); + +// var buffer = []; +// var lastType; +// for (var i = 0, len = this.state.output.length; i < len; i++) { +// var node = this.state.output[i]; +// if ((lastType === Mode.Attr && node.type !== Mode.Attr) || (lastType === Mode.Tag && node.type !== Mode.Attr)) { +// buffer.push('>'); +// } +// switch (node.type) { + +// case Mode.Text: +// buffer.push(node.data); +// break; + +// case Mode.Comment: +// buffer.push(''); +// break; + +// case Mode.CData: +// buffer.push(''); +// break; + +// case Mode.Tag: +// buffer.push('<', node.name); +// break; + +// case Mode.Attr: +// var quoteChar = (node.value.indexOf('\'') < 0) ? '\'' : '"'; +// buffer.push(' ', node.name, '=', quoteChar, node.value, quoteChar); +// break; + +// } +// lastType = node.type; +// } +// if (lastType === Mode.Tag || lastType === Mode.Attr) { +// buffer.push('>'); +// } +// console.log(buffer.join('')); +// }; + +function HtmlHandler (options, callback) { + this.reset(); + this._options = options ? options : { }; + if (this._options.ignoreWhitespace == undefined) { + this._options.ignoreWhitespace = false; //Keep whitespace-only text nodes + } + if (this._options.trackPosition == undefined) { + this._options.trackPosition = false; //Include position of element (row, col) on nodes + } + if (this._options.verbose == undefined) { + this._options.verbose = true; //Keep data property for tags and raw property for all + } + if (this._options.enforceEmptyTags == undefined) { + this._options.enforceEmptyTags = true; //Don't allow children for HTML tags defined as empty in spec + } + if (this._options.caseSensitiveTags == undefined) { + this._options.caseSensitiveTags = false; //Lowercase all tag names + } + if (this._options.caseSensitiveAttr == undefined) { + this._options.caseSensitiveAttr = false; //Lowercase all attribute names + } + if ((typeof callback) == "function") { + this._callback = callback; + } +} + + //**"Static"**// + //HTML Tags that shouldn't contain child nodes + HtmlHandler._emptyTags = { + area: 1 + , base: 1 + , basefont: 1 + , br: 1 + , col: 1 + , frame: 1 + , hr: 1 + , img: 1 + , input: 1 + , isindex: 1 + , link: 1 + , meta: 1 + , param: 1 + , embed: 1 + } + //Regex to detect whitespace only text nodes + HtmlHandler.reWhitespace = /^\s*$/; + + //**Public**// + //Properties// + HtmlHandler.prototype.dom = null; //The hierarchical object containing the parsed HTML + //Methods// + //Resets the handler back to starting state + HtmlHandler.prototype.reset = function HtmlHandler$reset() { + this.dom = []; + this._done = false; + this._tagStack = []; + this._tagStack.last = function HtmlHandler$_tagStack$last () { + return(this.length ? this[this.length - 1] : null); + } + } + //Signals the handler that parsing is done + HtmlHandler.prototype.done = function HtmlHandler$done () { + this._done = true; + this.handleCallback(null); + } + + HtmlHandler.prototype.error = function HtmlHandler$error (error) { + this.handleCallback(error); + } + + HtmlHandler.prototype.handleCallback = function HtmlHandler$handleCallback (error) { + if ((typeof this._callback) != "function") + if (error) + throw error; + else + return; + this._callback(error, this.dom); + } + + HtmlHandler.prototype.isEmptyTag = function HtmlHandler$isEmptyTag (element) { + var name = element.name.toLowerCase(); + if (name.charAt(0) == '/') { + name = name.substring(1); + } + return this._options.enforceEmptyTags && !!HtmlHandler._emptyTags[name]; + }; + + HtmlHandler.prototype._copyElement = function HtmlHandler$_copyElement (element) { + var newElement = { type: element.type }; + + if (this._options.verbose && element['raw'] !== undefined) { + newElement.raw = element.raw; + } + if (element['name'] !== undefined) { + switch (element.type) { + + case Mode.Tag: + newElement.name = this._options.caseSensitiveTags ? + element.name + : + element.name.toLowerCase() + ; + break; + + case Mode.Attr: + newElement.name = this._options.caseSensitiveAttr ? + element.name + : + element.name.toLowerCase() + ; + break; + + default: + newElement.name = this._options.caseSensitiveTags ? + element.name + : + element.name.toLowerCase() + ; + break; + + } + } + if (element['data'] !== undefined) { + newElement.data = element.name; + } + + return newElement; + } + + HtmlHandler.prototype.write = function HtmlHandler$write (element) { + if (this._done) { + this.handleCallback(new Error("Writing to the handler after done() called is not allowed without a reset()")); + } + if (element.type === Mode.Text && this._options.ignoreWhitespace) { + if (HtmlHandler.reWhitespace.test(element.data)) { + return; + } + } + var node; + if (!this._options.verbose) { +// element.raw = null; //FIXME: Not clean + //FIXME: Serious performance problem using delete + delete element.raw; + if (element.type == "tag" || element.type == "script" || element.type == "style") { + delete element.data; + } + } + if (!this._tagStack.last()) { //There are no parent elements + //If the element can be a container, add it to the tag stack and the top level list + if (element.type != Mode.Text && element.type != Mode.Comment && element.type != Mode.CData) { + if (element.name.charAt(0) != "/") { //Ignore closing tags that obviously don't have an opening tag + node = this._copyElement(element); + this.dom.push(node); + if (!this.isEmptyTag(node)) { //Don't add tags to the tag stack that can't have children + this._tagStack.push(node); + } + } + } + else //Otherwise just add to the top level list + this.dom.push(this._copyElement(element)); + } + else { //There are parent elements + //If the element can be a container, add it as a child of the element + //on top of the tag stack and then add it to the tag stack + if (element.type != Mode.Text && element.type != Mode.Comment && element.type != Mode.CData) { + if (element.name.charAt(0) == "/") { + //This is a closing tag, scan the tagStack to find the matching opening tag + //and pop the stack up to the opening tag's parent + var baseName = this._options.caseSensitiveTags ? + element.name.substring(1) + : + element.name.substring(1).toLowerCase() + ; + if (!this.isEmptyTag(element)) { + var pos = this._tagStack.length - 1; + while (pos > -1 && this._tagStack[pos--].name != baseName) { } + if (pos > -1 || this._tagStack[0].name == baseName) { + while (pos < this._tagStack.length - 1) { + this._tagStack.pop(); + } + } + } + } + else { //This is not a closing tag + if (!this._tagStack.last().children) { + this._tagStack.last().children = []; + } + node = this._copyElement(element); + this._tagStack.last().children.push(node); + if (!this.isEmptyTag(node)) { //Don't add tags to the tag stack that can't have children + this._tagStack.push(node); + } + } + } + else { //This is not a container element + if (!this._tagStack.last().children) + this._tagStack.last().children = []; + this._tagStack.last().children.push(this._copyElement(element)); + } + } + } + + + //**Private**// + //Properties// + HtmlHandler.prototype._options = null; //Handler options for how to behave + HtmlHandler.prototype._callback = null; //Callback to respond to when parsing done + HtmlHandler.prototype._done = false; //Flag indicating whether handler has been notified of parsing completed + HtmlHandler.prototype._tagStack = null; //List of parents to the currently element being processed + //Methods// + +exports.Parser = Parser; + +exports.HtmlHandler = HtmlHandler; + +// exports.RssHandler = RssHandler; + +exports.ElementType = Mode; + +// exports.DomUtils = DomUtils; + +})(); diff --git a/server/node_modules/jquery/node_modules/htmlparser/new/parser.zip b/server/node_modules/jquery/node_modules/htmlparser/new/parser.zip new file mode 100644 index 0000000..c562a64 Binary files /dev/null and b/server/node_modules/jquery/node_modules/htmlparser/new/parser.zip differ diff --git a/server/node_modules/jquery/node_modules/htmlparser/new/test01.js b/server/node_modules/jquery/node_modules/htmlparser/new/test01.js new file mode 100644 index 0000000..3582584 --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/new/test01.js @@ -0,0 +1,258 @@ +var data = "\ +starting text\n\ +aaabbb\n\ +\n\ +< htmL >\n\ + \n\ +
    \n\ +
    xxx
    \n\ +
    yyyyyyyyyyyy
    \n\ +
    zzzz zzz
    \n\ +
    aaaa
    \n\ + \n\ +\n\ +ending text\ +]+)(\s*)(>?)/g; +function parseTag (state) { + // console.log('parseTag', state); + // console.log('parseTag'); + re_parseTag.lastIndex = state.pos; + var match = re_parseTag.exec(state.data); + if (match) { + if (match[2].substr(0, 3) === '!--') { + state.mode = Mode.Comment; + state.pos += 3; + return; + } + if (match[2].substr(0, 8) === '![CDATA[') { + state.mode = Mode.CData; + state.pos += 8; + return; + } + var raw; + if (match[4] === '>') { + state.mode = Mode.Text; + raw = match[0].substr(0, match[0].length - 1); + } else { + state.mode = Mode.Attr; + raw = match[0]; + } + state.pos += match[0].length; + var tag = { type: Mode.Tag, name: match[2].toLowerCase(), name_raw: match[2], raw: raw }; + if (state.mode === Mode.Attr) { + state.lastTag = tag; + } + state.output.push(tag); + } else { + //TODO: end of tag? + //TODO: push to pending? + state.needData = true; + } +} + +var re_parseAttr_findName = /\s*([^=<>\s'"]+)\s*/g; +function parseAttr_findName (state) { + re_parseAttr_findName.lastIndex = state.pos; + var match = re_parseAttr_findName.exec(state.data); + if (!match) { + return null; + } + if (state.pos + match[0].length !== re_parseAttr_findName.lastIndex) { + return null; + } + state.pos += match[0].length; + state.lastTag.raw += match[0]; + return match[1]; +} +var re_parseAttr_findValue = /\s*=\s*(?:'([^']*)'|"([^"]*)"|([^'"\s>]*))/g; +function parseAttr_findValue (state) { + re_parseAttr_findValue.lastIndex = state.pos; + var match = re_parseAttr_findValue.exec(state.data); + if (!match) { + return null; + } + if (state.pos + match[0].length !== re_parseAttr_findValue.lastIndex) { + return null; + } + state.pos += match[0].length; + state.lastTag.raw += match[0]; + return match[1] || match[2] || match[3]; +}; +function parseAttr (state) { + // console.log('parseAttr', state); + // console.log('parseAttr'); + var name = parseAttr_findName(state); + if (!name) { + var foundPos = state.data.indexOf('>', state.pos); + if (foundPos < 0) { + state.needData = true; + } else { + state.lastTag = null; + state.pos = foundPos + 1; + state.mode = Mode.Text; + } + return; + } + state.output.push({ type: Mode.Attr, name: name, name_raw: name.toLowerCase(), value: parseAttr_findValue(state) }); +} + +function parseCData (state) { + // console.log('parseCData', state); + // console.log('parseCData'); + var foundPos = state.data.indexOf(']]>', state.pos); + if (foundPos < 0) { + if (!state.pendingText) { + state.pendingText = []; + } + state.pendingText.push(state.data.substr(state.pos, state.data.length)); + state.pos = state.data.length; + state.needData = true; + } else { + var text; + if (state.pendingText) { + state.pendingText.push(state.data.substring(state.pos, foundPos)); + text = state.pendingText.join(''); + state.pendingText = null; + } else { + text = state.data.substring(state.pos, foundPos); + } + state.output.push({ type: Mode.CData, data: text }); + state.mode = Mode.Text; + state.pos = foundPos + 3; + } +} + +function parseComment (state) { + // console.log('parseComment', state); + // console.log('parseComment'); + var foundPos = state.data.indexOf('-->', state.pos); + if (foundPos < 0) { + if (!state.pendingText) { + state.pendingText = []; + } + state.pendingText.push(state.data.substr(state.pos, state.data.length)); + state.pos = state.data.length; + state.needData = true; + } else { + var text; + if (state.pendingText) { + state.pendingText.push(state.data.substring(state.pos, foundPos)); + text = state.pendingText.join(''); + state.pendingText = null; + } else { + text = state.data.substring(state.pos, foundPos); + } + state.output.push({ type: Mode.Comment, data: text }); + state.mode = Mode.Text; + state.pos = foundPos + 3; + } +} + +while (state.pos < state.data.length && !state.needData) { + parse(state); +} +if (state.pendingText) { + state.output.push({ type: state.mode, data: state.pendingText }); + state.pendingText = null; +} +console.log(state); + +var buffer = []; +var lastType; +for (var i = 0, len = state.output.length; i < len; i++) { + var node = state.output[i]; + if ((lastType === Mode.Attr && node.type !== Mode.Attr) || (lastType === Mode.Tag && node.type !== Mode.Attr)) { + buffer.push('>'); + } + switch (node.type) { + + case Mode.Text: + buffer.push(node.data); + break; + + case Mode.Comment: + buffer.push(''); + break; + + case Mode.CData: + buffer.push(''); + break; + + case Mode.Tag: + buffer.push('<', node.name); + break; + + case Mode.Attr: + var quoteChar = (node.value.indexOf('\'') < 0) ? '\'' : '"'; + buffer.push(' ', node.name, '=', quoteChar, node.value, quoteChar); + break; + + } + lastType = node.type; +} +if (lastType === Mode.Tag || lastType === Mode.Attr) { + buffer.push('>'); +} +console.log(buffer.join('')); diff --git a/server/node_modules/jquery/node_modules/htmlparser/new/test02.js b/server/node_modules/jquery/node_modules/htmlparser/new/test02.js new file mode 100644 index 0000000..6221c95 --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/new/test02.js @@ -0,0 +1,856 @@ +var htmlparser = require('./htmlparser'); + +var tests = { + + 'plain text': { + data: ['This is the text'] + , expected: [{ type: 'text', data: 'This is the text' }] + } + + , 'split text': { + data: ['This is', ' the text'] + , expected: [{ type: 'text', data: 'This is the text' }] + } + + , 'simple tag': { + data: ['
    '] + , expected: [{ type: 'tag', name: 'div', name_raw: 'div', raw: 'div' }] + } + + , 'simple comment': { + data: [''] + , expected: [{ type: 'comment', data: ' content ' }] + } + + , 'simple cdata': { + data: [''] + , expected: [{ type: 'cdata', data: ' content ' }] + } + + , 'split simple tag #1': { + data: ['<', 'div>'] + , expected: [{ type: 'tag', name: 'div', name_raw: 'div', raw: 'div' }] + } + + , 'split simple tag #2': { + data: [''] + , expected: [{ type: 'tag', name: 'div', name_raw: 'div', raw: 'div' }] + } + + , 'split simple tag #3': { + data: [''] + , expected: [{ type: 'tag', name: 'div', name_raw: 'div', raw: 'div' }] + } + + , 'text before tag': { + data: ['xxx
    '] + , expected: [ + { type: 'text', data: 'xxx'}, + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div' } + ] + } + + , 'text after tag': { + data: ['
    xxx'] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div' }, + { type: 'text', data: 'xxx'} + ] + } + + , 'text inside tag': { + data: ['
    xxx
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div' }, + { type: 'text', data: 'xxx'}, + { type: 'tag', name: '/div', name_raw: '/div', raw: '/div' } + ] + } + + , 'attribute with single quotes': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div a=\'1\'' }, + { type: 'attr', name:'a', name_raw: 'a', value: '1'} + ] + } + + , 'attribute with double quotes': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div a="1"' }, + { type: 'attr', name:'a', name_raw: 'a', value: '1'} + ] + } + + , 'attribute with no quotes': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div a=1' }, + { type: 'attr', name:'a', name_raw: 'a', value: '1'} + ] + } + + , 'attribute with no value': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div wierd' }, + { type: 'attr', name:'wierd', name_raw: 'wierd', value: null} + ] + } + + , 'attribute with no value, trailing text': { + data: ['
    xxx'] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div wierd' }, + { type: 'attr', name:'wierd', name_raw: 'wierd', value: null}, + { type: 'text', data: 'xxx' } + ] + } + + , 'tag with multiple attributes': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div a="1" b="2"' }, + { type: 'attr', name:'a', name_raw: 'a', value: '1'}, + { type: 'attr', name:'b', name_raw: 'b', value: '2'} + ] + } + + , 'tag with multiple attributes, trailing text': { + data: ['
    xxx'] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div a="1" b="2"' }, + { type: 'attr', name:'a', name_raw: 'a', value: '1'}, + { type: 'attr', name:'b', name_raw: 'b', value: '2'}, + { type: 'text', data: 'xxx' } + ] + } + + , 'tag with mixed attributes #1': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div a=1 b=\'2\' c="3"' }, + { type: 'attr', name:'a', name_raw: 'a', value: '1'}, + { type: 'attr', name:'b', name_raw: 'b', value: '2'}, + { type: 'attr', name:'c', name_raw: 'c', value: '3'} + ] + } + + , 'tag with mixed attributes #2': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div a=1 b="2" c=\'3\'' }, + { type: 'attr', name:'a', name_raw: 'a', value: '1'}, + { type: 'attr', name:'b', name_raw: 'b', value: '2'}, + { type: 'attr', name:'c', name_raw: 'c', value: '3'} + ] + } + + , 'tag with mixed attributes #3': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div a=\'1\' b=2 c="3"' }, + { type: 'attr', name:'a', name_raw: 'a', value: '1'}, + { type: 'attr', name:'b', name_raw: 'b', value: '2'}, + { type: 'attr', name:'c', name_raw: 'c', value: '3'} + ] + } + + , 'tag with mixed attributes #4': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div a=\'1\' b="2" c=3' }, + { type: 'attr', name:'a', name_raw: 'a', value: '1'}, + { type: 'attr', name:'b', name_raw: 'b', value: '2'}, + { type: 'attr', name:'c', name_raw: 'c', value: '3'} + ] + } + + , 'tag with mixed attributes #5': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div a="1" b=2 c=\'3\'' }, + { type: 'attr', name:'a', name_raw: 'a', value: '1'}, + { type: 'attr', name:'b', name_raw: 'b', value: '2'}, + { type: 'attr', name:'c', name_raw: 'c', value: '3'} + ] + } + + , 'tag with mixed attributes #6': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div a="1" b=\'2\' c="3"' }, + { type: 'attr', name:'a', name_raw: 'a', value: '1'}, + { type: 'attr', name:'b', name_raw: 'b', value: '2'}, + { type: 'attr', name:'c', name_raw: 'c', value: '3'} + ] + } + + , 'tag with mixed attributes, trailing text': { + data: ['
    xxx'] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div a=1 b=\'2\' c="3"' }, + { type: 'attr', name:'a', name_raw: 'a', value: '1'}, + { type: 'attr', name:'b', name_raw: 'b', value: '2'}, + { type: 'attr', name:'c', name_raw: 'c', value: '3'}, + { type: 'text', data: 'xxx' } + ] + } + + , 'self closing tag': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div/' }, + { type: 'tag', name: '/div', name_raw: '/div', raw: null } + ] + } + + , 'self closing tag, trailing text': { + data: ['
    xxx'] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div/' }, + { type: 'tag', name: '/div', name_raw: '/div', raw: null }, + { type: 'text', data: 'xxx' } + ] + } + + , 'self closing tag with spaces #1': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div /' }, + { type: 'tag', name: '/div', name_raw: '/div', raw: null } + ] + } + + , 'self closing tag with spaces #2': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div/ ' }, + { type: 'tag', name: '/div', name_raw: '/div', raw: null } + ] + } + + , 'self closing tag with spaces #3': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div / ' }, + { type: 'tag', name: '/div', name_raw: '/div', raw: null } + ] + } + + , 'self closing tag with spaces, trailing text': { + data: ['
    xxx'] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div / ' }, + { type: 'tag', name: '/div', name_raw: '/div', raw: null }, + { type: 'text', data: 'xxx' } + ] + } + + , 'self closing tag with attribute': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div a=b /' }, + { type: 'attr', name:'a', name_raw: 'a', value: 'b'}, + { type: 'tag', name: '/div', name_raw: '/div', raw: null } + ] + } + + , 'self closing tag with attribute, trailing text': { + data: ['
    xxx'] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div a=b /' }, + { type: 'attr', name:'a', name_raw: 'a', value: 'b'}, + { type: 'tag', name: '/div', name_raw: '/div', raw: null }, + { type: 'text', data: 'xxx' } + ] + } + + , 'self closing tag split #1': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div/' }, + { type: 'tag', name: '/div', name_raw: '/div', raw: null } + ] + } + + , 'self closing tag split #2': { + data: [''] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div/' }, + { type: 'tag', name: '/div', name_raw: '/div', raw: null } + ] + } + + , 'attribute missing close quote': { + data: ['
    xxx'] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div a="1>'] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div xxx="yyy"' }, + { type: 'attr', name:'xxx', name_raw: 'xxx', value: 'yyy'} + ] + } + + , 'split attribute #2': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div xxx="yyy"' }, + { type: 'attr', name:'xxx', name_raw: 'xxx', value: 'yyy'} + ] + } + + , 'split attribute #3': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div xxx="yyy"' }, + { type: 'attr', name:'xxx', name_raw: 'xxx', value: 'yyy'} + ] + } + + , 'split attribute #4': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div xxx="yyy"' }, + { type: 'attr', name:'xxx', name_raw: 'xxx', value: 'yyy'} + ] + } + + , 'split attribute #5': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div xxx="yyy"' }, + { type: 'attr', name:'xxx', name_raw: 'xxx', value: 'yyy'} + ] + } + + , 'split attribute #6': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div xxx="yyy"' }, + { type: 'attr', name:'xxx', name_raw: 'xxx', value: 'yyy'} + ] + } + + , 'attribute split from tag #1': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div xxx="yyy"' }, + { type: 'attr', name:'xxx', name_raw: 'xxx', value: 'yyy'} + ] + } + + , 'attribute split from tag #2': { + data: [''] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div xxx="yyy"' }, + { type: 'attr', name:'xxx', name_raw: 'xxx', value: 'yyy'} + ] + } + + , 'text before complex tag': { + data: ['xxx
    '] + , expected: [ + { type: 'text', data: 'xxx' }, + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div yyy="123"'}, + { type: 'attr', name: 'yyy', name_raw: 'yyy', value: '123' } + ] + } + + , 'text after complex tag': { + data: ['
    xxx'] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div yyy="123"'}, + { type: 'attr', name: 'yyy', name_raw: 'yyy', value: '123' }, + { type: 'text', data: 'xxx' } + ] + } + + , 'text inside complex tag': { + data: ['
    xxx
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div yyy="123"'}, + { type: 'attr', name: 'yyy', name_raw: 'yyy', value: '123' }, + { type: 'text', data: 'xxx' }, + { type: 'tag', name: '/div', name_raw: '/div', raw: '/div'} + ] + } + + , 'nested tags': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div'}, + { type: 'tag', name: 'span', name_raw: 'span', raw: 'span'}, + { type: 'tag', name: '/span', name_raw: '/span', raw: '/span'}, + { type: 'tag', name: '/div', name_raw: '/div', raw: '/div'} + ] + } + + , 'nested tags with attributes': { + data: ['
    xxx
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div aaa="bbb"'}, + { type: 'attr', name: 'aaa', name_raw: 'aaa', value: 'bbb' }, + { type: 'tag', name: 'span', name_raw: 'span', raw: 'span 123=\'456\''}, + { type: 'attr', name: '123', name_raw: '123', value: '456' }, + { type: 'text', data: 'xxx' }, + { type: 'tag', name: '/span', name_raw: '/span', raw: '/span'}, + { type: 'tag', name: '/div', name_raw: '/div', raw: '/div'} + ] + } + + , 'comment inside tag': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div'}, + { type: 'comment', data: ' comment text '}, + { type: 'tag', name: '/div', name_raw: '/div', raw: '/div'} + ] + } + + , 'cdata inside tag': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div'}, + { type: 'cdata', data: ' CData content '}, + { type: 'tag', name: '/div', name_raw: '/div', raw: '/div'} + ] + } + + , 'html inside comment': { + data: [''] + , expected: [{ type: 'comment', data: '
    foo
    '}] + } + + , 'html inside cdata': { + data: ['foo
    ]]>'] + , expected: [{ type: 'cdata', data: '
    foo
    '}] + } + + , 'quotes in attribute #1': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div xxx=\'a"b\''}, + { type: 'attr', name: 'xxx', name_raw: 'xxx', value: 'a"b' } + ] + } + + , 'quotes in attribute #2': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div xxx="a\'b"'}, + { type: 'attr', name: 'xxx', name_raw: 'xxx', value: 'a\'b' } + ] + } + + , 'brackets in attribute': { + data: ['
    '] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: 'div xxx="
    "'}, + { type: 'attr', name: 'xxx', name_raw: 'xxx', value: '
    ' } + ] + } + + , 'split comment #1': { + data: ['<','!-- comment text -->xxx'] + , expected: [ + { type: 'comment', data: ' comment text '}, + { type: 'text', data: 'xxx' } + ] + } + + , 'split comment #2': { + data: ['xxx'] + , expected: [ + { type: 'comment', data: ' comment text '}, + { type: 'text', data: 'xxx' } + ] + } + + , 'split comment #3': { + data: ['xxx'] + , expected: [ + { type: 'comment', data: ' comment text '}, + { type: 'text', data: 'xxx' } + ] + } + + , 'split comment #4': { + data: ['xxx'] + , expected: [ + { type: 'comment', data: ' comment text '}, + { type: 'text', data: 'xxx' } + ] + } + + , 'split comment #5': { + data: ['xxx'] + , expected: [ + { type: 'comment', data: ' comment text '}, + { type: 'text', data: 'xxx' } + ] + } + + , 'split comment #6': { + data: ['xxx'] + , expected: [ + { type: 'comment', data: ' comment text '}, + { type: 'text', data: 'xxx' } + ] + } + + , 'split comment #7': { + data: ['"] + , expected: [ + { type: 'comment', data: "\ncomment text\n" } + ] + } + + , 'cdata comment': { + data: [""] + , expected: [ + { type: 'cdata', data: "\nCData content\n" } + ] + } + + , 'multiline attribute #1': { + data: ["
    "] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: "div id='\nxxx\nyyy\n'" }, + { type: 'attr', name: 'id', name_raw: 'id', value: "\nxxx\nyyy\n" } + ] + } + + , 'multiline attribute #2': { + data: ["
    "] + , expected: [ + { type: 'tag', name: 'div', name_raw: 'div', raw: "div id=\"\nxxx\nyyy\n\"" }, + { type: 'attr', name: 'id', name_raw: 'id', value: "\nxxx\nyyy\n" } + ] + } + + // script tags + // style tags + +}; + +function runTests (permutator) { + var parser = new htmlparser(); + + var passed = 0; + var failed = 0; + + console.time('Tests'); + for (var testName in tests) { + var test = permutator ? permutator(tests[testName]) : tests[testName]; + process.stdout.write('[TEST] ' + testName + ' : '); + parser.reset(); + for (var i = 0, len = test.data.length; i < len; i++) { + parser.parse(test.data[i]); + } + parser.done(); + var expected = JSON.stringify(test.expected); + var result = JSON.stringify(parser.state.output); + if (expected !== result) { + failed++; + process.stdout.write("FAIL\n"); + console.log(' [EXPECTED]', expected); + console.log(' [ RESULT ]', result); + } else { + passed++; + process.stdout.write("Ok\n"); + } + } + console.timeEnd('Tests'); + console.log('Passed tests: ' + passed + '/' + (passed + failed) + ' (' + Math.round(passed / (passed + failed) * 100) + '%)'); +} + +runTests(); +runTests(function (test) { + test.data = test.data.join('').split(''); + return test; +}); diff --git a/server/node_modules/jquery/node_modules/htmlparser/newparser.js b/server/node_modules/jquery/node_modules/htmlparser/newparser.js new file mode 100644 index 0000000..2487f23 --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/newparser.js @@ -0,0 +1,54 @@ +//node --prof --prof_auto profile.js +//deps/v8/tools/mac-tick-processor v8.log +var sys = require("sys"); +var fs = require("fs"); + +var testHtml = "./testdata/api.html"; //Test HTML file to load +var testIterations = 100; //Number of test loops to run + +var html = fs.readFileSync(testHtml).toString(); + +function getMillisecs () { + return((new Date()).getTime()); +} + +function timeExecutions (loops, func) { + var start = getMillisecs(); + + while (loops--) + func(); + + return(getMillisecs() - start); +} + +sys.puts("HTML Length: " + html.length); + +sys.puts("Test 1: " + timeExecutions(testIterations, function () { +// function parseText (data) { +// // +// } +// function parseTag (data) { +// // +// } +// function parseAttrib (data) { +// // +// } +// function parseComment (data) { +// // +// } + var data = html.split(""); + data.meta = { + length: data.length + , pos: 0 + } + while (data.meta.length > data.meta.pos && data[data.meta.pos++] !== ""); +// sys.puts("Found: " + [data.meta.pos, data[data.meta.pos]]); +}) + "ms"); + +sys.puts("Test 2: " + timeExecutions(testIterations, function () { + var data = html; + var dataLen = data.length; + var pos = 0; + while (dataLen > pos && data.charAt(pos++) !== ""); +// sys.puts("Found: " + [pos, data.charAt(pos)]); +}) + "ms"); diff --git a/server/node_modules/jquery/node_modules/htmlparser/node-htmlparser.old.js b/server/node_modules/jquery/node_modules/htmlparser/node-htmlparser.old.js new file mode 100644 index 0000000..cfc1664 --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/node-htmlparser.old.js @@ -0,0 +1,754 @@ +/*********************************************** +Copyright 2010, Chris Winberry . All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +***********************************************/ +/* v1.5.0 */ + +(function () { + +function runningInNode () { + return( + (typeof require) == "function" + && + (typeof exports) == "object" + && + (typeof module) == "object" + && + (typeof __filename) == "string" + && + (typeof __dirname) == "string" + ); +} + +if (!runningInNode()) { + if (!this.Tautologistics) + this.Tautologistics = {}; + else if (this.Tautologistics.NodeHtmlParser) + return; //NodeHtmlParser already defined! + this.Tautologistics.NodeHtmlParser = {}; + exports = this.Tautologistics.NodeHtmlParser; +} + +//Types of elements found in the DOM +var ElementType = { + Text: "text" //Plain text + , Directive: "directive" //Special tag + , Comment: "comment" //Special tag + , Script: "script" //Special tag + , Style: "style" //Special tag + , Tag: "tag" //Any tag that isn't special +} + +function Parser (handler) { + this.validateHandler(handler); + this._handler = handler; + this.reset(); +} + + //**"Static"**// + //Regular expressions used for cleaning up and parsing (stateless) + Parser._reTrim = /(^\s+|\s+$)/g; //Trim leading/trailing whitespace + Parser._reTrimComment = /(^\!--|--$)/g; //Remove comment tag markup from comment contents + Parser._reWhitespace = /\s/g; //Used to find any whitespace to split on + Parser._reTagName = /^\s*(\/?)\s*([^\s\/]+)/; //Used to find the tag name for an element + + //Regular expressions used for parsing (stateful) + Parser._reAttrib = //Find attributes in a tag + /([^=<>\"\'\s]+)\s*=\s*"([^"]*)"|([^=<>\"\'\s]+)\s*=\s*'([^']*)'|([^=<>\"\'\s]+)\s*=\s*([^'"\s]+)|([^=<>\"\'\s\/]+)/g; + Parser._reTags = /[\<\>]/g; //Find tag markers + + //**Public**// + //Methods// + //Parses a complete HTML and pushes it to the handler + Parser.prototype.parseComplete = function Parser$parseComplete (data) { + this.reset(); + this.parseChunk(data); + this.done(); + } + + //Parses a piece of an HTML document + Parser.prototype.parseChunk = function Parser$parseChunk (data) { + if (this._done) + this.handleError(new Error("Attempted to parse chunk after parsing already done")); + this._buffer += data; //FIXME: this can be a bottleneck + this.parseTags(); + } + + //Tells the parser that the HTML being parsed is complete + Parser.prototype.done = function Parser$done () { + if (this._done) + return; + this._done = true; + + //Push any unparsed text into a final element in the element list + if (this._buffer.length) { + var rawData = this._buffer; + this._buffer = ""; + var element = { + raw: rawData + , data: (this._parseState == ElementType.Text) ? rawData : rawData.replace(Parser._reTrim, "") + , type: this._parseState + }; + if (this._parseState == ElementType.Tag || this._parseState == ElementType.Script || this._parseState == ElementType.Style) + element.name = this.parseTagName(element.data); + this.parseAttribs(element); + this._elements.push(element); + } + + this.writeHandler(); + this._handler.done(); + } + + //Resets the parser to a blank state, ready to parse a new HTML document + Parser.prototype.reset = function Parser$reset () { + this._buffer = ""; + this._done = false; + this._elements = []; + this._elementsCurrent = 0; + this._current = 0; + this._next = 0; + this._parseState = ElementType.Text; + this._prevTagSep = ''; + this._tagStack = []; + this._handler.reset(); + } + + //**Private**// + //Properties// + Parser.prototype._handler = null; //Handler for parsed elements + Parser.prototype._buffer = null; //Buffer of unparsed data + Parser.prototype._done = false; //Flag indicating whether parsing is done + Parser.prototype._elements = null; //Array of parsed elements + Parser.prototype._elementsCurrent = 0; //Pointer to last element in _elements that has been processed + Parser.prototype._current = 0; //Position in data that has already been parsed + Parser.prototype._next = 0; //Position in data of the next tag marker (<>) + Parser.prototype._parseState = ElementType.Text; //Current type of element being parsed + Parser.prototype._prevTagSep = ''; //Previous tag marker found + //Stack of element types previously encountered; keeps track of when + //parsing occurs inside a script/comment/style tag + Parser.prototype._tagStack = null; + + //Methods// + //Takes an array of elements and parses any found attributes + Parser.prototype.parseTagAttribs = function Parser$parseTagAttribs (elements) { + var idxEnd = elements.length; + var idx = 0; + + while (idx < idxEnd) { + var element = elements[idx++]; + if (element.type == ElementType.Tag || element.type == ElementType.Script || element.type == ElementType.style) + this.parseAttribs(element); + } + + return(elements); + } + + //Takes an element and adds an "attribs" property for any element attributes found + Parser.prototype.parseAttribs = function Parser$parseAttribs (element) { + //Only parse attributes for tags + if (element.type != ElementType.Script && element.type != ElementType.Style && element.type != ElementType.Tag) + return; + + var tagName = element.data.split(Parser._reWhitespace, 1)[0]; + var attribRaw = element.data.substring(tagName.length); + if (attribRaw.length < 1) + return; + + var match; + Parser._reAttrib.lastIndex = 0; + while (match = Parser._reAttrib.exec(attribRaw)) { + if (element.attribs == undefined) + element.attribs = {}; + + if (typeof match[1] == "string" && match[1].length) { + element.attribs[match[1]] = match[2]; + } else if (typeof match[3] == "string" && match[3].length) { + element.attribs[match[3].toString()] = match[4].toString(); + } else if (typeof match[5] == "string" && match[5].length) { + element.attribs[match[5]] = match[6]; + } else if (typeof match[7] == "string" && match[7].length) { + element.attribs[match[7]] = match[7]; + } + } + } + + //Extracts the base tag name from the data value of an element + Parser.prototype.parseTagName = function Parser$parseTagName (data) { + if (data == null || data == "") + return(""); + var match = Parser._reTagName.exec(data); + if (!match) + return(""); + return((match[1] ? "/" : "") + match[2]); + } + + //Parses through HTML text and returns an array of found elements + //I admit, this function is rather large but splitting up had an noticeable impact on speed + Parser.prototype.parseTags = function Parser$parseTags () { + var bufferEnd = this._buffer.length - 1; + while (Parser._reTags.test(this._buffer)) { + this._next = Parser._reTags.lastIndex - 1; + var tagSep = this._buffer.charAt(this._next); //The currently found tag marker + var rawData = this._buffer.substring(this._current, this._next); //The next chunk of data to parse + + //A new element to eventually be appended to the element list + var element = { + raw: rawData + , data: (this._parseState == ElementType.Text) ? rawData : rawData.replace(Parser._reTrim, "") + , type: this._parseState + }; + + var elementName = this.parseTagName(element.data); + + //This section inspects the current tag stack and modifies the current + //element if we're actually parsing a special area (script/comment/style tag) + if (this._tagStack.length) { //We're parsing inside a script/comment/style tag + if (this._tagStack[this._tagStack.length - 1] == ElementType.Script) { //We're currently in a script tag + if (elementName == "/script") //Actually, we're no longer in a script tag, so pop it off the stack + this._tagStack.pop(); + else { //Not a closing script tag + if (element.raw.indexOf("!--") != 0) { //Make sure we're not in a comment + //All data from here to script close is now a text element + element.type = ElementType.Text; + //If the previous element is text, append the current text to it + if (this._elements.length && this._elements[this._elements.length - 1].type == ElementType.Text) { + var prevElement = this._elements[this._elements.length - 1]; + prevElement.raw = prevElement.data = prevElement.raw + this._prevTagSep + element.raw; + element.raw = element.data = ""; //This causes the current element to not be added to the element list + } + } + } + } + else if (this._tagStack[this._tagStack.length - 1] == ElementType.Style) { //We're currently in a style tag + if (elementName == "/style") //Actually, we're no longer in a style tag, so pop it off the stack + this._tagStack.pop(); + else { + if (element.raw.indexOf("!--") != 0) { //Make sure we're not in a comment + //All data from here to style close is now a text element + element.type = ElementType.Text; + //If the previous element is text, append the current text to it + if (this._elements.length && this._elements[this._elements.length - 1].type == ElementType.Text) { + if (element.raw != "") { + var prevElement = this._elements[this._elements.length - 1]; + prevElement.raw = prevElement.data = prevElement.raw + this._prevTagSep + element.raw; + element.raw = element.data = ""; //This causes the current element to not be added to the element list + } + else //Element is empty, so just append the last tag marker found + prevElement.raw = prevElement.data = prevElement.raw + this._prevTagSep; + } + else //The previous element was not text + if (element.raw != "") + element.raw = element.data = element.raw; + } + } + } + else if (this._tagStack[this._tagStack.length - 1] == ElementType.Comment) { //We're currently in a comment tag + var rawLen = element.raw.length; + if (element.raw.charAt(rawLen - 2) == "-" && element.raw.charAt(rawLen - 1) == "-" && tagSep == ">") { + //Actually, we're no longer in a style tag, so pop it off the stack + this._tagStack.pop(); + //If the previous element is a comment, append the current text to it + if (this._elements.length && this._elements[this._elements.length - 1].type == ElementType.Comment) { + var prevElement = this._elements[this._elements.length - 1]; + prevElement.raw = prevElement.data = (prevElement.raw + element.raw).replace(Parser._reTrimComment, ""); + element.raw = element.data = ""; //This causes the current element to not be added to the element list + element.type = ElementType.Text; + } + else //Previous element not a comment + element.type = ElementType.Comment; //Change the current element's type to a comment + } + else { //Still in a comment tag + element.type = ElementType.Comment; + //If the previous element is a comment, append the current text to it + if (this._elements.length && this._elements[this._elements.length - 1].type == ElementType.Comment) { + var prevElement = this._elements[this._elements.length - 1]; + prevElement.raw = prevElement.data = prevElement.raw + element.raw + tagSep; + element.raw = element.data = ""; //This causes the current element to not be added to the element list + element.type = ElementType.Text; + } + else + element.raw = element.data = element.raw + tagSep; + } + } + } + + //Processing of non-special tags + if (element.type == ElementType.Tag) { + element.name = elementName; + + if (element.raw.indexOf("!--") == 0) { //This tag is really comment + element.type = ElementType.Comment; + delete element["name"]; + var rawLen = element.raw.length; + //Check if the comment is terminated in the current element + if (element.raw.charAt(rawLen - 1) == "-" && element.raw.charAt(rawLen - 2) == "-" && tagSep == ">") + element.raw = element.data = element.raw.replace(Parser._reTrimComment, ""); + else { //It's not so push the comment onto the tag stack + element.raw += tagSep; + this._tagStack.push(ElementType.Comment); + } + } + else if (element.raw.indexOf("!") == 0 || element.raw.indexOf("?") == 0) { + element.type = ElementType.Directive; + //TODO: what about CDATA? + } + else if (element.name == "script") { + element.type = ElementType.Script; + //Special tag, push onto the tag stack if not terminated + if (element.data.charAt(element.data.length - 1) != "/") + this._tagStack.push(ElementType.Script); + } + else if (element.name == "/script") + element.type = ElementType.Script; + else if (element.name == "style") { + element.type = ElementType.Style; + //Special tag, push onto the tag stack if not terminated + if (element.data.charAt(element.data.length - 1) != "/") + this._tagStack.push(ElementType.Style); + } + else if (element.name == "/style") + element.type = ElementType.Style; + if (element.name && element.name.charAt(0) == "/") + element.data = element.name; + } + + //Add all tags and non-empty text elements to the element list + if (element.raw != "" || element.type != ElementType.Text) { + this.parseAttribs(element); + this._elements.push(element); + //If tag self-terminates, add an explicit, separate closing tag + if ( + element.type != ElementType.Text + && + element.type != ElementType.Comment + && + element.type != ElementType.Directive + && + element.data.charAt(element.data.length - 1) == "/" + ) + this._elements.push({ + raw: "/" + element.name + , data: "/" + element.name + , name: "/" + element.name + , type: element.type + }); + } + this._parseState = (tagSep == "<") ? ElementType.Tag : ElementType.Text; + this._current = this._next + 1; + this._prevTagSep = tagSep; + } + + this._buffer = (this._current <= bufferEnd) ? this._buffer.substring(this._current) : ""; + this._current = 0; + + this.writeHandler(); + } + + //Checks the handler to make it is an object with the right "interface" + Parser.prototype.validateHandler = function Parser$validateHandler (handler) { + if ((typeof handler) != "object") + throw new Error("Handler is not an object"); + if ((typeof handler.reset) != "function") + throw new Error("Handler method 'reset' is invalid"); + if ((typeof handler.done) != "function") + throw new Error("Handler method 'done' is invalid"); + if ((typeof handler.writeTag) != "function") + throw new Error("Handler method 'writeTag' is invalid"); + if ((typeof handler.writeText) != "function") + throw new Error("Handler method 'writeText' is invalid"); + if ((typeof handler.writeComment) != "function") + throw new Error("Handler method 'writeComment' is invalid"); + if ((typeof handler.writeDirective) != "function") + throw new Error("Handler method 'writeDirective' is invalid"); + } + + //Writes parsed elements out to the handler + Parser.prototype.writeHandler = function Parser$writeHandler (forceFlush) { + forceFlush = !!forceFlush; + if (this._tagStack.length && !forceFlush) + return; + while (this._elements.length) { + var element = this._elements.shift(); + switch (element.type) { + case ElementType.Comment: + this._handler.writeComment(element); + break; + case ElementType.Directive: + this._handler.writeDirective(element); + break; + case ElementType.Text: + this._handler.writeText(element); + break; + default: + this._handler.writeTag(element); + break; + } + } + } + + Parser.prototype.handleError = function Parser$handleError (error) { + if ((typeof this._handler.error) == "function") + this._handler.error(error); + else + throw error; + } + +//TODO: make this a trully streamable handler +function RssHandler (callback) { + RssHandler.super_.call(this, callback, { ignoreWhitespace: true, verbose: false, enforceEmptyTags: false }); +} +inherits(RssHandler, DefaultHandler); + + RssHandler.prototype.done = function RssHandler$done () { + var feed = { }; + var feedRoot; + + var found = DomUtils.getElementsByTagName(function (value) { return(value == "rss" || value == "feed"); }, this.dom, false); + if (found.length) { + feedRoot = found[0]; + } + if (feedRoot) { + if (feedRoot.name == "rss") { + feed.type = "rss"; + feedRoot = feedRoot.children[0]; // + feed.id = ""; +// require("sys").debug(require("sys").inspect(feedRoot, false, null)); +// require("sys").debug(require("sys").inspect(DomUtils.getElementsByTagName("title", feedRoot.children, false)[0].children[0].data, false, null)); + try { + feed.title = DomUtils.getElementsByTagName("title", feedRoot.children, false)[0].children[0].data; + } catch (ex) { } + try { + feed.link = DomUtils.getElementsByTagName("link", feedRoot.children, false)[0].children[0].data; + } catch (ex) { } + try { + feed.description = DomUtils.getElementsByTagName("description", feedRoot.children, false)[0].children[0].data; + } catch (ex) { } + try { + feed.updated = new Date(DomUtils.getElementsByTagName("lastBuildDate", feedRoot.children, false)[0].children[0].data); + } catch (ex) { } + try { + feed.author = DomUtils.getElementsByTagName("managingEditor", feedRoot.children, false)[0].children[0].data; + } catch (ex) { } + feed.items = []; + DomUtils.getElementsByTagName("item", feedRoot.children).forEach(function (item, index, list) { + var entry = {}; + try { + entry.id = DomUtils.getElementsByTagName("guid", item.children, false)[0].children[0].data; + } catch (ex) { } + try { + entry.title = DomUtils.getElementsByTagName("title", item.children, false)[0].children[0].data; + } catch (ex) { } + try { + entry.link = DomUtils.getElementsByTagName("link", item.children, false)[0].children[0].data; + } catch (ex) { } + try { + entry.description = DomUtils.getElementsByTagName("description", item.children, false)[0].children[0].data; + } catch (ex) { } + try { + entry.pubDate = new Date(DomUtils.getElementsByTagName("pubDate", item.children, false)[0].children[0].data); + } catch (ex) { } + feed.items.push(entry); + }); + } else { + feed.type = "atom"; + try { + feed.id = DomUtils.getElementsByTagName("id", feedRoot.children, false)[0].children[0].data; + } catch (ex) { } + try { + feed.title = DomUtils.getElementsByTagName("title", feedRoot.children, false)[0].children[0].data; + } catch (ex) { } + try { + feed.link = DomUtils.getElementsByTagName("link", feedRoot.children, false)[0].attribs.href; + } catch (ex) { } + try { + feed.description = DomUtils.getElementsByTagName("subtitle", feedRoot.children, false)[0].children[0].data; + } catch (ex) { } + try { + feed.updated = new Date(DomUtils.getElementsByTagName("updated", feedRoot.children, false)[0].children[0].data); + } catch (ex) { } + try { + feed.author = DomUtils.getElementsByTagName("email", feedRoot.children, true)[0].children[0].data; + } catch (ex) { } + feed.items = []; + DomUtils.getElementsByTagName("entry", feedRoot.children).forEach(function (item, index, list) { + var entry = {}; + try { + entry.id = DomUtils.getElementsByTagName("id", item.children, false)[0].children[0].data; + } catch (ex) { } + try { + entry.title = DomUtils.getElementsByTagName("title", item.children, false)[0].children[0].data; + } catch (ex) { } + try { + entry.link = DomUtils.getElementsByTagName("link", item.children, false)[0].attribs.href; + } catch (ex) { } + try { + entry.description = DomUtils.getElementsByTagName("summary", item.children, false)[0].children[0].data; + } catch (ex) { } + try { + entry.pubDate = new Date(DomUtils.getElementsByTagName("updated", item.children, false)[0].children[0].data); + } catch (ex) { } + feed.items.push(entry); + }); + } + + this.dom = feed; + } + RssHandler.super_.prototype.done.call(this); + } + +/////////////////////////////////////////////////// + +function DefaultHandler (callback, options) { + this.reset(); + this._options = options ? options : { }; + if (this._options.ignoreWhitespace == undefined) + this._options.ignoreWhitespace = false; //Keep whitespace-only text nodes + if (this._options.verbose == undefined) + this._options.verbose = true; //Keep data property for tags and raw property for all + if (this._options.enforceEmptyTags == undefined) + this._options.enforceEmptyTags = true; //Don't allow children for HTML tags defined as empty in spec + if ((typeof callback) == "function") + this._callback = callback; +} + + //**"Static"**// + //HTML Tags that shouldn't contain child nodes + DefaultHandler._emptyTags = { + area: 1 + , base: 1 + , basefont: 1 + , br: 1 + , col: 1 + , frame: 1 + , hr: 1 + , img: 1 + , input: 1 + , isindex: 1 + , link: 1 + , meta: 1 + , param: 1 + , embed: 1 + } + //Regex to detect whitespace only text nodes + DefaultHandler.reWhitespace = /^\s*$/; + + //**Public**// + //Properties// + DefaultHandler.prototype.dom = null; //The hierarchical object containing the parsed HTML + //Methods// + //Resets the handler back to starting state + DefaultHandler.prototype.reset = function DefaultHandler$reset() { + this.dom = []; + this._done = false; + this._tagStack = []; + this._tagStack.last = function DefaultHandler$_tagStack$last () { + return(this.length ? this[this.length - 1] : null); + } + } + //Signals the handler that parsing is done + DefaultHandler.prototype.done = function DefaultHandler$done () { + this._done = true; + this.handleCallback(null); + } + DefaultHandler.prototype.writeTag = function DefaultHandler$writeTag (element) { + this.handleElement(element); + } + DefaultHandler.prototype.writeText = function DefaultHandler$writeText (element) { + if (this._options.ignoreWhitespace) + if (DefaultHandler.reWhitespace.test(element.data)) + return; + this.handleElement(element); + } + DefaultHandler.prototype.writeComment = function DefaultHandler$writeComment (element) { + this.handleElement(element); + } + DefaultHandler.prototype.writeDirective = function DefaultHandler$writeDirective (element) { + this.handleElement(element); + } + DefaultHandler.prototype.error = function DefaultHandler$error (error) { + this.handleCallback(error); + } + + //**Private**// + //Properties// + DefaultHandler.prototype._options = null; //Handler options for how to behave + DefaultHandler.prototype._callback = null; //Callback to respond to when parsing done + DefaultHandler.prototype._done = false; //Flag indicating whether handler has been notified of parsing completed + DefaultHandler.prototype._tagStack = null; //List of parents to the currently element being processed + //Methods// + DefaultHandler.prototype.handleCallback = function DefaultHandler$handleCallback (error) { + if ((typeof this._callback) != "function") + if (error) + throw error; + else + return; + this._callback(error, this.dom); + } + DefaultHandler.prototype.handleElement = function DefaultHandler$handleElement (element) { + if (this._done) + this.handleCallback(new Error("Writing to the handler after done() called is not allowed without a reset()")); + if (!this._options.verbose) { +// element.raw = null; //FIXME: Not clean + //FIXME: Serious performance problem using delete + delete element.raw; + if (element.type == "tag" || element.type == "script" || element.type == "style") + delete element.data; + } + if (!this._tagStack.last()) { //There are no parent elements + //If the element can be a container, add it to the tag stack and the top level list + if (element.type != ElementType.Text && element.type != ElementType.Comment && element.type != ElementType.Directive) { + if (element.name.charAt(0) != "/") { //Ignore closing tags that obviously don't have an opening tag + this.dom.push(element); + if (!this._options.enforceEmptyTags || !DefaultHandler._emptyTags[element.name]) { //Don't add tags to the tag stack that can't have children + this._tagStack.push(element); + } + } + } + else //Otherwise just add to the top level list + this.dom.push(element); + } + else { //There are parent elements + //If the element can be a container, add it as a child of the element + //on top of the tag stack and then add it to the tag stack + if (element.type != ElementType.Text && element.type != ElementType.Comment && element.type != ElementType.Directive) { + if (element.name.charAt(0) == "/") { + //This is a closing tag, scan the tagStack to find the matching opening tag + //and pop the stack up to the opening tag's parent + var baseName = element.name.substring(1); + if (!this._options.enforceEmptyTags || !DefaultHandler._emptyTags[baseName]) { + var pos = this._tagStack.length - 1; + while (pos > -1 && this._tagStack[pos--].name != baseName) { } + if (pos > -1 || this._tagStack[0].name == baseName) + while (pos < this._tagStack.length - 1) + this._tagStack.pop(); + } + } + else { //This is not a closing tag + if (!this._tagStack.last().children) + this._tagStack.last().children = []; + this._tagStack.last().children.push(element); + if (!this._options.enforceEmptyTags || !DefaultHandler._emptyTags[element.name]) //Don't add tags to the tag stack that can't have children + this._tagStack.push(element); + } + } + else { //This is not a container element + if (!this._tagStack.last().children) + this._tagStack.last().children = []; + this._tagStack.last().children.push(element); + } + } + } + + var DomUtils = { + testElement: function DomUtils$testElement (options, element) { + if (!element) { + return(false); + } + + for (var key in options) { + if (key == "tag_name") { + if (element.type != "tag" && element.type != "script" && element.type != "style") { + return(false); + } + return(options["tag_name"](element.name)); + } else if (key == "tag_type") { + return(options["tag_type"](element.type)); + } else if (key == "tag_contains") { + if (element.type != "text" && element.type != "comment" && element.type != "directive") { + return(false); + } + return(options["tag_contains"](element.data)); + } else { + return(element.attribs && options[key](element.attribs[key])); + } + } + + return(true); + } + + , getElements: function DomUtils$getElements (options, currentElement, recurse) { + recurse = (recurse === undefined || recurse === null) || !!recurse; + + if (!currentElement) { + return([]); + } + + var found = []; + var elementList; + + function getTest (checkVal) { + return(((typeof options[key]) == "function") ? checkVal : function (value) { return(value == checkVal); }); + } + for (var key in options) { + options[key] = getTest(options[key]); + } + + if (DomUtils.testElement(options, currentElement)) { + found.push(currentElement); + } + + if (recurse && currentElement.children) + elementList = currentElement.children; + else if (currentElement instanceof Array) + elementList = currentElement; + else + return(found); + + for (var i = 0; i < elementList.length; i++) + found = found.concat(DomUtils.getElements(options, elementList[i], recurse)); + + return(found); + } + + , getElementById: function DomUtils$getElementById (id, currentElement, recurse) { + recurse = (recurse === undefined || recurse === null) || !!recurse; + var result = DomUtils.getElements({ id: id }, currentElement, recurse); + return(result.length ? result[0] : null); + } + + , getElementsByTagName: function DomUtils$getElementsByTagName (name, currentElement, recurse) { + recurse = (recurse === undefined || recurse === null) || !!recurse; + return(DomUtils.getElements({ tag_name: name }, currentElement, recurse)); + } + + , getElementsByTagType: function DomUtils$getElementsByTagType (type, currentElement, recurse) { + recurse = (recurse === undefined || recurse === null) || !!recurse; + return(DomUtils.getElements({ tag_type: type }, currentElement, recurse)); + } + } + + function inherits (ctor, superCtor) { + var tempCtor = function(){}; + tempCtor.prototype = superCtor.prototype; + ctor.super_ = superCtor; + ctor.prototype = new tempCtor(); + ctor.prototype.constructor = ctor; + } + +exports.Parser = Parser; + +exports.DefaultHandler = DefaultHandler; + +exports.RssHandler = RssHandler; + +exports.ElementType = ElementType; + +exports.DomUtils = DomUtils; + +})(); diff --git a/server/node_modules/jquery/node_modules/htmlparser/package.json b/server/node_modules/jquery/node_modules/htmlparser/package.json new file mode 100644 index 0000000..488bbc2 --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/package.json @@ -0,0 +1,45 @@ +{ + "name": "htmlparser", + "description": "Forgiving HTML/XML/RSS Parser in JS for *both* Node and Browsers", + "version": "1.7.6", + "author": { + "name": "Chris Winberry", + "email": "chris@winberry.net" + }, + "contributors": [], + "repository": { + "type": "git", + "url": "https://github.com/tautologistics/node-htmlparser" + }, + "keywords": [ + "html", + "xml", + "rss", + "parser" + ], + "directories": { + "lib": "./lib/" + }, + "main": "./lib/htmlparser", + "homepage": "http://github.com/tautologistics/node-htmlparser", + "bugs": { + "url": "http://github.com/tautologistics/node-htmlparser/issues" + }, + "engines": { + "node": ">=0.1.33" + }, + "licenses": [ + { + "type": "MIT", + "url": "http://github.com/tautologistics/node-htmlparser/raw/master/LICENSE" + } + ], + "readme": "#NodeHtmlParser\nA forgiving HTML/XML/RSS parser written in JS for both the browser and NodeJS (yes, despite the name it works just fine in any modern browser). The parser can handle streams (chunked data) and supports custom handlers for writing custom DOMs/output.\n\n##Installing\n\n\tnpm install htmlparser\n\n##Running Tests\n\n###Run tests under node:\n\tnode runtests.js\n\n###Run tests in browser:\nView runtests.html in any browser\n\n##Usage In Node\n\n```javascript\nvar htmlparser = require(\"htmlparser\");\nvar rawHtml = \"Xyz + , Style: "style" //Special tag + , Tag: "tag" //Any tag that isn't special + } + + function Parser (handler) { + this.validateHandler(handler); + this._handler = handler; + this.reset(); + } + + //**"Static"**// + //Regular expressions used for cleaning up and parsing (stateless) + Parser._reTrim = /(^\s+|\s+$)/g; //Trim leading/trailing whitespace + Parser._reTrimComment = /(^\!--|--$)/g; //Remove comment tag markup from comment contents + Parser._reWhitespace = /\s/g; //Used to find any whitespace to split on + Parser._reTagName = /^\s*(\/?)\s*([^\s\/]+)/; //Used to find the tag name for an element + + //Regular expressions used for parsing (stateful) + Parser._reAttrib = //Find attributes in a tag + /([^=<>\"\'\s]+)\s*=\s*"([^"]*)"|([^=<>\"\'\s]+)\s*=\s*'([^']*)'|([^=<>\"\'\s]+)\s*=\s*([^'"\s]+)|([^=<>\"\'\s\/]+)/g; +Parser._reTags = /[\<\>]/g; //Find tag markers + +//**Public**// +//Methods// +//Parses a complete HTML and pushes it to the handler +Parser.prototype.parseComplete = function Parser$parseComplete (data) { + this.reset(); + this.parseChunk(data); + this.done(); +} + +//Parses a piece of an HTML document +Parser.prototype.parseChunk = function Parser$parseChunk (data) { + if (this._done) + this.handleError(new Error("Attempted to parse chunk after parsing already done")); + this._buffer += data; //FIXME: this can be a bottleneck + this.parseTags(); +} + +//Tells the parser that the HTML being parsed is complete +Parser.prototype.done = function Parser$done () { + if (this._done) + return; + this._done = true; + + //Push any unparsed text into a final element in the element list + if (this._buffer.length) { + var rawData = this._buffer; + this._buffer = ""; + var element = { + raw: rawData + , data: (this._parseState == ElementType.Text) ? rawData : rawData.replace(Parser._reTrim, "") + , type: this._parseState + }; + if (this._parseState == ElementType.Tag || this._parseState == ElementType.Script || this._parseState == ElementType.Style) + element.name = this.parseTagName(element.data); + this.parseAttribs(element); + this._elements.push(element); + } + + this.writeHandler(); + this._handler.done(); +} + +//Resets the parser to a blank state, ready to parse a new HTML document +Parser.prototype.reset = function Parser$reset () { + this._buffer = ""; + this._done = false; + this._elements = []; + this._elementsCurrent = 0; + this._current = 0; + this._next = 0; + this._parseState = ElementType.Text; + this._prevTagSep = ''; + this._tagStack = []; + this._handler.reset(); +} + +//**Private**// +//Properties// +Parser.prototype._handler = null; //Handler for parsed elements +Parser.prototype._buffer = null; //Buffer of unparsed data +Parser.prototype._done = false; //Flag indicating whether parsing is done +Parser.prototype._elements = null; //Array of parsed elements +Parser.prototype._elementsCurrent = 0; //Pointer to last element in _elements that has been processed +Parser.prototype._current = 0; //Position in data that has already been parsed +Parser.prototype._next = 0; //Position in data of the next tag marker (<>) +Parser.prototype._parseState = ElementType.Text; //Current type of element being parsed +Parser.prototype._prevTagSep = ''; //Previous tag marker found +//Stack of element types previously encountered; keeps track of when +//parsing occurs inside a script/comment/style tag +Parser.prototype._tagStack = null; + +//Methods// +//Takes an array of elements and parses any found attributes +Parser.prototype.parseTagAttribs = function Parser$parseTagAttribs (elements) { + var idxEnd = elements.length; + var idx = 0; + + while (idx < idxEnd) { + var element = elements[idx++]; + if (element.type == ElementType.Tag || element.type == ElementType.Script || element.type == ElementType.style) + this.parseAttribs(element); + } + + return(elements); +} + +//Takes an element and adds an "attribs" property for any element attributes found +Parser.prototype.parseAttribs = function Parser$parseAttribs (element) { + //Only parse attributes for tags + if (element.type != ElementType.Script && element.type != ElementType.Style && element.type != ElementType.Tag) + return; + + var tagName = element.data.split(Parser._reWhitespace, 1)[0]; + var attribRaw = element.data.substring(tagName.length); + if (attribRaw.length < 1) + return; + + var match; + Parser._reAttrib.lastIndex = 0; + while (match = Parser._reAttrib.exec(attribRaw)) { + if (element.attribs == undefined) + element.attribs = {}; + + if (typeof match[1] == "string" && match[1].length) { + element.attribs[match[1]] = match[2]; + } else if (typeof match[3] == "string" && match[3].length) { + element.attribs[match[3].toString()] = match[4].toString(); + } else if (typeof match[5] == "string" && match[5].length) { + element.attribs[match[5]] = match[6]; + } else if (typeof match[7] == "string" && match[7].length) { + element.attribs[match[7]] = match[7]; + } + } +} + +//Extracts the base tag name from the data value of an element +Parser.prototype.parseTagName = function Parser$parseTagName (data) { + if (data == null || data == "") + return(""); + var match = Parser._reTagName.exec(data); + if (!match) + return(""); + return((match[1] ? "/" : "") + match[2]); +} + +//Parses through HTML text and returns an array of found elements +//I admit, this function is rather large but splitting up had an noticeable impact on speed +Parser.prototype.parseTags = function Parser$parseTags () { + var bufferEnd = this._buffer.length - 1; + while (Parser._reTags.test(this._buffer)) { + this._next = Parser._reTags.lastIndex - 1; + var tagSep = this._buffer.charAt(this._next); //The currently found tag marker + var rawData = this._buffer.substring(this._current, this._next); //The next chunk of data to parse + + //A new element to eventually be appended to the element list + var element = { + raw: rawData + , data: (this._parseState == ElementType.Text) ? rawData : rawData.replace(Parser._reTrim, "") + , type: this._parseState + }; + + var elementName = this.parseTagName(element.data); + + //This section inspects the current tag stack and modifies the current + //element if we're actually parsing a special area (script/comment/style tag) + if (this._tagStack.length) { //We're parsing inside a script/comment/style tag + if (this._tagStack[this._tagStack.length - 1] == ElementType.Script) { //We're currently in a script tag + if (elementName == "/script") //Actually, we're no longer in a script tag, so pop it off the stack + this._tagStack.pop(); + else { //Not a closing script tag + if (element.raw.indexOf("!--") != 0) { //Make sure we're not in a comment + //All data from here to script close is now a text element + element.type = ElementType.Text; + //If the previous element is text, append the current text to it + if (this._elements.length && this._elements[this._elements.length - 1].type == ElementType.Text) { + var prevElement = this._elements[this._elements.length - 1]; + prevElement.raw = prevElement.data = prevElement.raw + this._prevTagSep + element.raw; + element.raw = element.data = ""; //This causes the current element to not be added to the element list + } + } + } + } + else if (this._tagStack[this._tagStack.length - 1] == ElementType.Style) { //We're currently in a style tag + if (elementName == "/style") //Actually, we're no longer in a style tag, so pop it off the stack + this._tagStack.pop(); + else { + if (element.raw.indexOf("!--") != 0) { //Make sure we're not in a comment + //All data from here to style close is now a text element + element.type = ElementType.Text; + //If the previous element is text, append the current text to it + if (this._elements.length && this._elements[this._elements.length - 1].type == ElementType.Text) { + if (element.raw != "") { + var prevElement = this._elements[this._elements.length - 1]; + prevElement.raw = prevElement.data = prevElement.raw + this._prevTagSep + element.raw; + element.raw = element.data = ""; //This causes the current element to not be added to the element list + } + else{ //Element is empty, so just append the last tag marker found + if (prevElement) { + prevElement.raw = prevElement.data = prevElement.raw + this._prevTagSep; + } + } + } + else //The previous element was not text + if (element.raw != "") + element.raw = element.data = element.raw; + } + } + } + else if (this._tagStack[this._tagStack.length - 1] == ElementType.Comment) { //We're currently in a comment tag + var rawLen = element.raw.length; + if (element.raw.charAt(rawLen - 2) == "-" && element.raw.charAt(rawLen - 1) == "-" && tagSep == ">") { + //Actually, we're no longer in a style tag, so pop it off the stack + this._tagStack.pop(); + //If the previous element is a comment, append the current text to it + if (this._elements.length && this._elements[this._elements.length - 1].type == ElementType.Comment) { + var prevElement = this._elements[this._elements.length - 1]; + prevElement.raw = prevElement.data = (prevElement.raw + element.raw).replace(Parser._reTrimComment, ""); + element.raw = element.data = ""; //This causes the current element to not be added to the element list + element.type = ElementType.Text; + } + else //Previous element not a comment + element.type = ElementType.Comment; //Change the current element's type to a comment + } + else { //Still in a comment tag + element.type = ElementType.Comment; + //If the previous element is a comment, append the current text to it + if (this._elements.length && this._elements[this._elements.length - 1].type == ElementType.Comment) { + var prevElement = this._elements[this._elements.length - 1]; + prevElement.raw = prevElement.data = prevElement.raw + element.raw + tagSep; + element.raw = element.data = ""; //This causes the current element to not be added to the element list + element.type = ElementType.Text; + } + else + element.raw = element.data = element.raw + tagSep; + } + } + } + + //Processing of non-special tags + if (element.type == ElementType.Tag) { + element.name = elementName; + + if (element.raw.indexOf("!--") == 0) { //This tag is really comment + element.type = ElementType.Comment; + delete element["name"]; + var rawLen = element.raw.length; + //Check if the comment is terminated in the current element + if (element.raw.charAt(rawLen - 1) == "-" && element.raw.charAt(rawLen - 2) == "-" && tagSep == ">") + element.raw = element.data = element.raw.replace(Parser._reTrimComment, ""); + else { //It's not so push the comment onto the tag stack + element.raw += tagSep; + this._tagStack.push(ElementType.Comment); + } + } + else if (element.raw.indexOf("!") == 0 || element.raw.indexOf("?") == 0) { + element.type = ElementType.Directive; + //TODO: what about CDATA? + } + else if (element.name == "script") { + element.type = ElementType.Script; + //Special tag, push onto the tag stack if not terminated + if (element.data.charAt(element.data.length - 1) != "/") + this._tagStack.push(ElementType.Script); + } + else if (element.name == "/script") + element.type = ElementType.Script; + else if (element.name == "style") { + element.type = ElementType.Style; + //Special tag, push onto the tag stack if not terminated + if (element.data.charAt(element.data.length - 1) != "/") + this._tagStack.push(ElementType.Style); + } + else if (element.name == "/style") + element.type = ElementType.Style; + if (element.name && element.name.charAt(0) == "/") + element.data = element.name; + } + + //Add all tags and non-empty text elements to the element list + if (element.raw != "" || element.type != ElementType.Text) { + this.parseAttribs(element); + this._elements.push(element); + //If tag self-terminates, add an explicit, separate closing tag + if ( + element.type != ElementType.Text + && + element.type != ElementType.Comment + && + element.type != ElementType.Directive + && + element.data.charAt(element.data.length - 1) == "/" + ) + this._elements.push({ + raw: "/" + element.name + , data: "/" + element.name + , name: "/" + element.name + , type: element.type + }); + } + this._parseState = (tagSep == "<") ? ElementType.Tag : ElementType.Text; + this._current = this._next + 1; + this._prevTagSep = tagSep; + } + + this._buffer = (this._current <= bufferEnd) ? this._buffer.substring(this._current) : ""; + this._current = 0; + + this.writeHandler(); +} + +//Checks the handler to make it is an object with the right "interface" +Parser.prototype.validateHandler = function Parser$validateHandler (handler) { + if ((typeof handler) != "object") + throw new Error("Handler is not an object"); + if ((typeof handler.reset) != "function") + throw new Error("Handler method 'reset' is invalid"); + if ((typeof handler.done) != "function") + throw new Error("Handler method 'done' is invalid"); + if ((typeof handler.writeTag) != "function") + throw new Error("Handler method 'writeTag' is invalid"); + if ((typeof handler.writeText) != "function") + throw new Error("Handler method 'writeText' is invalid"); + if ((typeof handler.writeComment) != "function") + throw new Error("Handler method 'writeComment' is invalid"); + if ((typeof handler.writeDirective) != "function") + throw new Error("Handler method 'writeDirective' is invalid"); +} + +//Writes parsed elements out to the handler +Parser.prototype.writeHandler = function Parser$writeHandler (forceFlush) { + forceFlush = !!forceFlush; + if (this._tagStack.length && !forceFlush) + return; + while (this._elements.length) { + var element = this._elements.shift(); + switch (element.type) { + case ElementType.Comment: + this._handler.writeComment(element); + break; + case ElementType.Directive: + this._handler.writeDirective(element); + break; + case ElementType.Text: + this._handler.writeText(element); + break; + default: + this._handler.writeTag(element); + break; + } + } +} + +Parser.prototype.handleError = function Parser$handleError (error) { + if ((typeof this._handler.error) == "function") + this._handler.error(error); + else + throw error; +} + +//TODO: make this a trully streamable handler +function RssHandler (callback) { + RssHandler.super_.call(this, callback, { ignoreWhitespace: true, verbose: false, enforceEmptyTags: false }); +} +inherits(RssHandler, DefaultHandler); + +RssHandler.prototype.done = function RssHandler$done () { + var feed = { }; + var feedRoot; + + var found = DomUtils.getElementsByTagName(function (value) { return(value == "rss" || value == "feed"); }, this.dom, false); + if (found.length) { + feedRoot = found[0]; + } + if (feedRoot) { + if (feedRoot.name == "rss") { + feed.type = "rss"; + feedRoot = feedRoot.children[0]; // + feed.id = ""; + try { + feed.title = DomUtils.getElementsByTagName("title", feedRoot.children, false)[0].children[0].data; + } catch (ex) { } + try { + feed.link = DomUtils.getElementsByTagName("link", feedRoot.children, false)[0].children[0].data; + } catch (ex) { } + try { + feed.description = DomUtils.getElementsByTagName("description", feedRoot.children, false)[0].children[0].data; + } catch (ex) { } + try { + feed.updated = new Date(DomUtils.getElementsByTagName("lastBuildDate", feedRoot.children, false)[0].children[0].data); + } catch (ex) { } + try { + feed.author = DomUtils.getElementsByTagName("managingEditor", feedRoot.children, false)[0].children[0].data; + } catch (ex) { } + feed.items = []; + DomUtils.getElementsByTagName("item", feedRoot.children).forEach(function (item, index, list) { + var entry = {}; + try { + entry.id = DomUtils.getElementsByTagName("guid", item.children, false)[0].children[0].data; + } catch (ex) { } + try { + entry.title = DomUtils.getElementsByTagName("title", item.children, false)[0].children[0].data; + } catch (ex) { } + try { + entry.link = DomUtils.getElementsByTagName("link", item.children, false)[0].children[0].data; + } catch (ex) { } + try { + entry.description = DomUtils.getElementsByTagName("description", item.children, false)[0].children[0].data; + } catch (ex) { } + try { + entry.pubDate = new Date(DomUtils.getElementsByTagName("pubDate", item.children, false)[0].children[0].data); + } catch (ex) { } + feed.items.push(entry); + }); + } else { + feed.type = "atom"; + try { + feed.id = DomUtils.getElementsByTagName("id", feedRoot.children, false)[0].children[0].data; + } catch (ex) { } + try { + feed.title = DomUtils.getElementsByTagName("title", feedRoot.children, false)[0].children[0].data; + } catch (ex) { } + try { + feed.link = DomUtils.getElementsByTagName("link", feedRoot.children, false)[0].attribs.href; + } catch (ex) { } + try { + feed.description = DomUtils.getElementsByTagName("subtitle", feedRoot.children, false)[0].children[0].data; + } catch (ex) { } + try { + feed.updated = new Date(DomUtils.getElementsByTagName("updated", feedRoot.children, false)[0].children[0].data); + } catch (ex) { } + try { + feed.author = DomUtils.getElementsByTagName("email", feedRoot.children, true)[0].children[0].data; + } catch (ex) { } + feed.items = []; + DomUtils.getElementsByTagName("entry", feedRoot.children).forEach(function (item, index, list) { + var entry = {}; + try { + entry.id = DomUtils.getElementsByTagName("id", item.children, false)[0].children[0].data; + } catch (ex) { } + try { + entry.title = DomUtils.getElementsByTagName("title", item.children, false)[0].children[0].data; + } catch (ex) { } + try { + entry.link = DomUtils.getElementsByTagName("link", item.children, false)[0].attribs.href; + } catch (ex) { } + try { + entry.description = DomUtils.getElementsByTagName("summary", item.children, false)[0].children[0].data; + } catch (ex) { } + try { + entry.pubDate = new Date(DomUtils.getElementsByTagName("updated", item.children, false)[0].children[0].data); + } catch (ex) { } + feed.items.push(entry); + }); + } + + this.dom = feed; + } + RssHandler.super_.prototype.done.call(this); +} + +/////////////////////////////////////////////////// + +function DefaultHandler (callback, options) { + this.reset(); + this._options = options ? options : { }; + if (this._options.ignoreWhitespace == undefined) + this._options.ignoreWhitespace = false; //Keep whitespace-only text nodes + if (this._options.verbose == undefined) + this._options.verbose = true; //Keep data property for tags and raw property for all + if (this._options.enforceEmptyTags == undefined) + this._options.enforceEmptyTags = true; //Don't allow children for HTML tags defined as empty in spec + if ((typeof callback) == "function") + this._callback = callback; +} + +//**"Static"**// +//HTML Tags that shouldn't contain child nodes +DefaultHandler._emptyTags = { + area: 1 + , base: 1 + , basefont: 1 + , br: 1 + , col: 1 + , frame: 1 + , hr: 1 + , img: 1 + , input: 1 + , isindex: 1 + , link: 1 + , meta: 1 + , param: 1 + , embed: 1 +} +//Regex to detect whitespace only text nodes +DefaultHandler.reWhitespace = /^\s*$/; + +//**Public**// +//Properties// +DefaultHandler.prototype.dom = null; //The hierarchical object containing the parsed HTML +//Methods// +//Resets the handler back to starting state +DefaultHandler.prototype.reset = function DefaultHandler$reset() { + this.dom = []; + this._done = false; + this._tagStack = []; + this._tagStack.last = function DefaultHandler$_tagStack$last () { + return(this.length ? this[this.length - 1] : null); + } +} +//Signals the handler that parsing is done +DefaultHandler.prototype.done = function DefaultHandler$done () { + this._done = true; + this.handleCallback(null); +} +DefaultHandler.prototype.writeTag = function DefaultHandler$writeTag (element) { + this.handleElement(element); +} +DefaultHandler.prototype.writeText = function DefaultHandler$writeText (element) { + if (this._options.ignoreWhitespace) + if (DefaultHandler.reWhitespace.test(element.data)) + return; + this.handleElement(element); +} +DefaultHandler.prototype.writeComment = function DefaultHandler$writeComment (element) { + this.handleElement(element); +} +DefaultHandler.prototype.writeDirective = function DefaultHandler$writeDirective (element) { + this.handleElement(element); +} +DefaultHandler.prototype.error = function DefaultHandler$error (error) { + this.handleCallback(error); +} + +//**Private**// +//Properties// +DefaultHandler.prototype._options = null; //Handler options for how to behave +DefaultHandler.prototype._callback = null; //Callback to respond to when parsing done +DefaultHandler.prototype._done = false; //Flag indicating whether handler has been notified of parsing completed +DefaultHandler.prototype._tagStack = null; //List of parents to the currently element being processed +//Methods// +DefaultHandler.prototype.handleCallback = function DefaultHandler$handleCallback (error) { + if ((typeof this._callback) != "function") + if (error) + throw error; + else + return; + this._callback(error, this.dom); +} +DefaultHandler.prototype.handleElement = function DefaultHandler$handleElement (element) { + if (this._done) + this.handleCallback(new Error("Writing to the handler after done() called is not allowed without a reset()")); + if (!this._options.verbose) { + // element.raw = null; //FIXME: Not clean + //FIXME: Serious performance problem using delete + delete element.raw; + if (element.type == "tag" || element.type == "script" || element.type == "style") + delete element.data; + } + if (!this._tagStack.last()) { //There are no parent elements + //If the element can be a container, add it to the tag stack and the top level list + if (element.type != ElementType.Text && element.type != ElementType.Comment && element.type != ElementType.Directive) { + if (element.name.charAt(0) != "/") { //Ignore closing tags that obviously don't have an opening tag + this.dom.push(element); + if (!this._options.enforceEmptyTags || !DefaultHandler._emptyTags[element.name]) { //Don't add tags to the tag stack that can't have children + this._tagStack.push(element); + } + } + } + else //Otherwise just add to the top level list + this.dom.push(element); + } + else { //There are parent elements + //If the element can be a container, add it as a child of the element + //on top of the tag stack and then add it to the tag stack + if (element.type != ElementType.Text && element.type != ElementType.Comment && element.type != ElementType.Directive) { + if (element.name.charAt(0) == "/") { + //This is a closing tag, scan the tagStack to find the matching opening tag + //and pop the stack up to the opening tag's parent + var baseName = element.name.substring(1); + if (!this._options.enforceEmptyTags || !DefaultHandler._emptyTags[baseName]) { + var pos = this._tagStack.length - 1; + while (pos > -1 && this._tagStack[pos--].name != baseName) { } + if (pos > -1 || this._tagStack[0].name == baseName) + while (pos < this._tagStack.length - 1) + this._tagStack.pop(); + } + } + else { //This is not a closing tag + if (!this._tagStack.last().children) + this._tagStack.last().children = []; + this._tagStack.last().children.push(element); + if (!this._options.enforceEmptyTags || !DefaultHandler._emptyTags[element.name]) //Don't add tags to the tag stack that can't have children + this._tagStack.push(element); + } + } + else { //This is not a container element + if (!this._tagStack.last().children) + this._tagStack.last().children = []; + this._tagStack.last().children.push(element); + } + } +} + +var DomUtils = { + testElement: function DomUtils$testElement (options, element) { + if (!element) { + return false; + } + + for (var key in options) { + if (key == "tag_name") { + if (element.type != "tag" && element.type != "script" && element.type != "style") { + return false; + } + if (!options["tag_name"](element.name)) { + return false; + } + } else if (key == "tag_type") { + if (!options["tag_type"](element.type)) { + return false; + } + } else if (key == "tag_contains") { + if (element.type != "text" && element.type != "comment" && element.type != "directive") { + return false; + } + if (!options["tag_contains"](element.data)) { + return false; + } + } else { + if (!element.attribs || !options[key](element.attribs[key])) { + return false; + } + } + } + + return true; + } + + , getElements: function DomUtils$getElements (options, currentElement, recurse, limit) { + recurse = (recurse === undefined || recurse === null) || !!recurse; + limit = isNaN(parseInt(limit)) ? -1 : parseInt(limit); + + if (!currentElement) { + return([]); + } + + var found = []; + var elementList; + + function getTest (checkVal) { + return(function (value) { return(value == checkVal); }); + } + for (var key in options) { + if ((typeof options[key]) != "function") { + options[key] = getTest(options[key]); + } + } + + if (DomUtils.testElement(options, currentElement)) { + found.push(currentElement); + } + + if (limit >= 0 && found.length >= limit) { + return(found); + } + + if (recurse && currentElement.children) { + elementList = currentElement.children; + } else if (currentElement instanceof Array) { + elementList = currentElement; + } else { + return(found); + } + + for (var i = 0; i < elementList.length; i++) { + found = found.concat(DomUtils.getElements(options, elementList[i], recurse, limit)); + if (limit >= 0 && found.length >= limit) { + break; + } + } + + return(found); + } + + , getElementById: function DomUtils$getElementById (id, currentElement, recurse) { + var result = DomUtils.getElements({ id: id }, currentElement, recurse, 1); + return(result.length ? result[0] : null); + } + + , getElementsByTagName: function DomUtils$getElementsByTagName (name, currentElement, recurse, limit) { + return(DomUtils.getElements({ tag_name: name }, currentElement, recurse, limit)); + } + + , getElementsByTagType: function DomUtils$getElementsByTagType (type, currentElement, recurse, limit) { + return(DomUtils.getElements({ tag_type: type }, currentElement, recurse, limit)); + } +} + +function inherits (ctor, superCtor) { + var tempCtor = function(){}; + tempCtor.prototype = superCtor.prototype; + ctor.super_ = superCtor; + ctor.prototype = new tempCtor(); + ctor.prototype.constructor = ctor; +} + +exports.Parser = Parser; + +exports.DefaultHandler = DefaultHandler; + +exports.RssHandler = RssHandler; + +exports.ElementType = ElementType; + +exports.DomUtils = DomUtils; + +})(); diff --git a/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/lib/node-htmlparser.min.js b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/lib/node-htmlparser.min.js new file mode 100644 index 0000000..5ab1e72 --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/lib/node-htmlparser.min.js @@ -0,0 +1,22 @@ +/*********************************************** +Copyright 2010, Chris Winberry . All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +***********************************************/ +/* v1.6.3 */ +(function(){function e(a){this.validateHandler(a);this._handler=a;this.reset()}function n(a){n.super_.call(this,a,{ignoreWhitespace:true,verbose:false,enforceEmptyTags:false})}function g(a,c){this.reset();this._options=c?c:{};if(this._options.ignoreWhitespace==undefined)this._options.ignoreWhitespace=false;if(this._options.verbose==undefined)this._options.verbose=true;if(this._options.enforceEmptyTags==undefined)this._options.enforceEmptyTags=true;if(typeof a=="function")this._callback=a}if(!(typeof require== "function"&&typeof exports=="object"&&typeof module=="object"&&typeof __filename=="string"&&typeof __dirname=="string")){if(this.Tautologistics){if(this.Tautologistics.NodeHtmlParser)return}else this.Tautologistics={};this.Tautologistics.NodeHtmlParser={};exports=this.Tautologistics.NodeHtmlParser}var d={Text:"text",Directive:"directive",Comment:"comment",Script:"script",Style:"style",Tag:"tag"};e._reTrim=/(^\s+|\s+$)/g;e._reTrimComment=/(^\!--|--$)/g;e._reWhitespace=/\s/g;e._reTagName=/^\s*(\/?)\s*([^\s\/]+)/; e._reAttrib=/([^=<>\"\'\s]+)\s*=\s*"([^"]*)"|([^=<>\"\'\s]+)\s*=\s*'([^']*)'|([^=<>\"\'\s]+)\s*=\s*([^'"\s]+)|([^=<>\"\'\s\/]+)/g;e._reTags=/[\<\>]/g;e.prototype.parseComplete=function(a){this.reset();this.parseChunk(a);this.done()};e.prototype.parseChunk=function(a){this._done&&this.handleError(Error("Attempted to parse chunk after parsing already done"));this._buffer+=a;this.parseTags()};e.prototype.done=function(){if(!this._done){this._done=true;if(this._buffer.length){var a=this._buffer;this._buffer= "";a={raw:a,data:this._parseState==d.Text?a:a.replace(e._reTrim,""),type:this._parseState};if(this._parseState==d.Tag||this._parseState==d.Script||this._parseState==d.Style)a.name=this.parseTagName(a.data);this.parseAttribs(a);this._elements.push(a)}this.writeHandler();this._handler.done()}};e.prototype.reset=function(){this._buffer="";this._done=false;this._elements=[];this._next=this._current=this._elementsCurrent=0;this._parseState=d.Text;this._prevTagSep="";this._tagStack=[];this._handler.reset()}; e.prototype._handler=null;e.prototype._buffer=null;e.prototype._done=false;e.prototype._elements=null;e.prototype._elementsCurrent=0;e.prototype._current=0;e.prototype._next=0;e.prototype._parseState=d.Text;e.prototype._prevTagSep="";e.prototype._tagStack=null;e.prototype.parseTagAttribs=function(a){for(var c=a.length,b=0;b"){this._tagStack.pop();if(this._elements.length&&this._elements[this._elements.length-1].type==d.Comment){i=this._elements[this._elements.length-1];i.raw=i.data=(i.raw+b.raw).replace(e._reTrimComment, "");b.raw=b.data="";b.type=d.Text}else b.type=d.Comment}else{b.type=d.Comment;if(this._elements.length&&this._elements[this._elements.length-1].type==d.Comment){i=this._elements[this._elements.length-1];i.raw=i.data=i.raw+b.raw+c;b.raw=b.data="";b.type=d.Text}else b.raw=b.data=b.raw+c}}if(b.type==d.Tag){b.name=h;if(b.raw.indexOf("!--")==0){b.type=d.Comment;delete b.name;j=b.raw.length;if(b.raw.charAt(j-1)=="-"&&b.raw.charAt(j-2)=="-"&&c==">")b.raw=b.data=b.raw.replace(e._reTrimComment,"");else{b.raw+= c;this._tagStack.push(d.Comment)}}else if(b.raw.indexOf("!")==0||b.raw.indexOf("?")==0)b.type=d.Directive;else if(b.name=="script"){b.type=d.Script;b.data.charAt(b.data.length-1)!="/"&&this._tagStack.push(d.Script)}else if(b.name=="/script")b.type=d.Script;else if(b.name=="style"){b.type=d.Style;b.data.charAt(b.data.length-1)!="/"&&this._tagStack.push(d.Style)}else if(b.name=="/style")b.type=d.Style;if(b.name&&b.name.charAt(0)=="/")b.data=b.name}if(b.raw!=""||b.type!=d.Text){this.parseAttribs(b); this._elements.push(b);b.type!=d.Text&&b.type!=d.Comment&&b.type!=d.Directive&&b.data.charAt(b.data.length-1)=="/"&&this._elements.push({raw:"/"+b.name,data:"/"+b.name,name:"/"+b.name,type:b.type})}this._parseState=c=="<"?d.Tag:d.Text;this._current=this._next+1;this._prevTagSep=c}this._buffer=this._current<=a?this._buffer.substring(this._current):"";this._current=0;this.writeHandler()};e.prototype.validateHandler=function(a){if(typeof a!="object")throw Error("Handler is not an object");if(typeof a.reset!= "function")throw Error("Handler method 'reset' is invalid");if(typeof a.done!="function")throw Error("Handler method 'done' is invalid");if(typeof a.writeTag!="function")throw Error("Handler method 'writeTag' is invalid");if(typeof a.writeText!="function")throw Error("Handler method 'writeText' is invalid");if(typeof a.writeComment!="function")throw Error("Handler method 'writeComment' is invalid");if(typeof a.writeDirective!="function")throw Error("Handler method 'writeDirective' is invalid");}; e.prototype.writeHandler=function(a){a=!!a;if(!(this._tagStack.length&&!a))for(;this._elements.length;){a=this._elements.shift();switch(a.type){case d.Comment:this._handler.writeComment(a);break;case d.Directive:this._handler.writeDirective(a);break;case d.Text:this._handler.writeText(a);break;default:this._handler.writeTag(a)}}};e.prototype.handleError=function(a){if(typeof this._handler.error=="function")this._handler.error(a);else throw a;};(function(a,c){var b=function(){};b.prototype=c.prototype; a.super_=c;a.prototype=new b;a.prototype.constructor=a})(n,g);n.prototype.done=function(){var a={},c,b=f.getElementsByTagName(function(k){return k=="rss"||k=="feed"},this.dom,false);if(b.length)c=b[0];if(c){if(c.name=="rss"){a.type="rss";c=c.children[0];a.id="";try{a.title=f.getElementsByTagName("title",c.children,false)[0].children[0].data}catch(h){}try{a.link=f.getElementsByTagName("link",c.children,false)[0].children[0].data}catch(i){}try{a.description=f.getElementsByTagName("description",c.children, false)[0].children[0].data}catch(j){}try{a.updated=new Date(f.getElementsByTagName("lastBuildDate",c.children,false)[0].children[0].data)}catch(m){}try{a.author=f.getElementsByTagName("managingEditor",c.children,false)[0].children[0].data}catch(o){}a.items=[];f.getElementsByTagName("item",c.children).forEach(function(k){var l={};try{l.id=f.getElementsByTagName("guid",k.children,false)[0].children[0].data}catch(q){}try{l.title=f.getElementsByTagName("title",k.children,false)[0].children[0].data}catch(r){}try{l.link= f.getElementsByTagName("link",k.children,false)[0].children[0].data}catch(s){}try{l.description=f.getElementsByTagName("description",k.children,false)[0].children[0].data}catch(t){}try{l.pubDate=new Date(f.getElementsByTagName("pubDate",k.children,false)[0].children[0].data)}catch(u){}a.items.push(l)})}else{a.type="atom";try{a.id=f.getElementsByTagName("id",c.children,false)[0].children[0].data}catch(p){}try{a.title=f.getElementsByTagName("title",c.children,false)[0].children[0].data}catch(v){}try{a.link= f.getElementsByTagName("link",c.children,false)[0].attribs.href}catch(w){}try{a.description=f.getElementsByTagName("subtitle",c.children,false)[0].children[0].data}catch(x){}try{a.updated=new Date(f.getElementsByTagName("updated",c.children,false)[0].children[0].data)}catch(y){}try{a.author=f.getElementsByTagName("email",c.children,true)[0].children[0].data}catch(z){}a.items=[];f.getElementsByTagName("entry",c.children).forEach(function(k){var l={};try{l.id=f.getElementsByTagName("id",k.children, false)[0].children[0].data}catch(q){}try{l.title=f.getElementsByTagName("title",k.children,false)[0].children[0].data}catch(r){}try{l.link=f.getElementsByTagName("link",k.children,false)[0].attribs.href}catch(s){}try{l.description=f.getElementsByTagName("summary",k.children,false)[0].children[0].data}catch(t){}try{l.pubDate=new Date(f.getElementsByTagName("updated",k.children,false)[0].children[0].data)}catch(u){}a.items.push(l)})}this.dom=a}n.super_.prototype.done.call(this)};g._emptyTags={area:1, base:1,basefont:1,br:1,col:1,frame:1,hr:1,img:1,input:1,isindex:1,link:1,meta:1,param:1,embed:1};g.reWhitespace=/^\s*$/;g.prototype.dom=null;g.prototype.reset=function(){this.dom=[];this._done=false;this._tagStack=[];this._tagStack.last=function(){return this.length?this[this.length-1]:null}};g.prototype.done=function(){this._done=true;this.handleCallback(null)};g.prototype.writeTag=function(a){this.handleElement(a)};g.prototype.writeText=function(a){if(this._options.ignoreWhitespace)if(g.reWhitespace.test(a.data))return; this.handleElement(a)};g.prototype.writeComment=function(a){this.handleElement(a)};g.prototype.writeDirective=function(a){this.handleElement(a)};g.prototype.error=function(a){this.handleCallback(a)};g.prototype._options=null;g.prototype._callback=null;g.prototype._done=false;g.prototype._tagStack=null;g.prototype.handleCallback=function(a){if(typeof this._callback!="function")if(a)throw a;else return;this._callback(a,this.dom)};g.prototype.handleElement=function(a){this._done&&this.handleCallback(Error("Writing to the handler after done() called is not allowed without a reset()")); if(!this._options.verbose){delete a.raw;if(a.type=="tag"||a.type=="script"||a.type=="style")delete a.data}if(this._tagStack.last())if(a.type!=d.Text&&a.type!=d.Comment&&a.type!=d.Directive)if(a.name.charAt(0)=="/"){a=a.name.substring(1);if(!this._options.enforceEmptyTags||!g._emptyTags[a]){for(var c=this._tagStack.length-1;c>-1&&this._tagStack[c--].name!=a;);if(c>-1||this._tagStack[0].name==a)for(;c=0&&j.length>=h)return j;if(b&&c.children)c=c.children;else if(c instanceof Array)c=c;else return j;for(m=0;m=0&&j.length>=h)break}return j},getElementById:function(a,c,b){a=f.getElements({id:a},c,b,1);return a.length?a[0]:null},getElementsByTagName:function(a,c,b,h){return f.getElements({tag_name:a},c,b,h)},getElementsByTagType:function(a, c,b,h){return f.getElements({tag_type:a},c,b,h)}};exports.Parser=e;exports.DefaultHandler=g;exports.RssHandler=n;exports.ElementType=d;exports.DomUtils=f})(); \ No newline at end of file diff --git a/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/package.json b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/package.json new file mode 100644 index 0000000..ced4c0a --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/package.json @@ -0,0 +1,23 @@ +{ + "name": "htmlparser" + , "description": "Forgiving HTML/XML/RSS Parser in JS for *both* Node and Browsers" + , "version": "1.6.2" + , "author": "Chris Winberry " + , "contributors": [] + , "repository": { + "type": "git" + , "url": "git://github.com/tautologistics/node-htmlparser.git" + } + , "bugs": { + "mail": "chris@winberry.net" + , "web": "http://github.com/tautologistics/node-htmlparser/issues" + } + , "os": [ "linux", "darwin", "freebsd" ] + , "directories": { "lib": "./lib/" } + , "main": "./lib/node-htmlparser" + , "engines": { "node": ">=0.1.33" } + , "licenses": [{ + "type": "MIT" + , "url": "http://github.com/tautologistics/node-htmlparser/raw/master/LICENSE" + }] +} diff --git a/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/profile.js b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/profile.js new file mode 100644 index 0000000..c5a474e --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/profile.js @@ -0,0 +1,63 @@ +//node --prof --prof_auto profile.js +//deps/v8/tools/mac-tick-processor v8.log +var sys = require("sys"); +var fs = require("fs"); +var http = require("http"); +var htmlparser = require("./node-htmlparser"); +var libxml = require('./libxmljs'); + +var testNHP = true; //Should node-htmlparser be exercised? +var testLXJS = true; //Should libxmljs be exercised? +var testIterations = 100; //Number of test loops to run + +var testHost = "nodejs.org"; //Host to fetch test HTML from +var testPort = 80; //Port on host to fetch test HTML from +var testPath = "/api.html"; //Path on host to fetch HTML from + +function getMillisecs () { + return((new Date()).getTime()); +} + +function timeExecutions (loops, func) { + var start = getMillisecs(); + + while (loops--) + func(); + + return(getMillisecs() - start); +} + +var html = ""; +http.createClient(testPort, testHost) + .request("GET", testPath, { host: testHost }) + .addListener("response", function (response) { + if (response.statusCode == "200") { + response.setEncoding("utf8"); + response.addListener("data", function (chunk) { + html += chunk; + }).addListener("end", function() { + var timeNodeHtmlParser = !testNHP ? 0 : timeExecutions(testIterations, function () { + var handler = new htmlparser.DefaultHandler(function(err, dom) { + if (err) + sys.debug("Error: " + err); + }); + var parser = new htmlparser.Parser(handler); + parser.parseComplete(html); + }) + + var timeLibXmlJs = !testLXJS ? 0 : timeExecutions(testIterations, function () { + var dom = libxml.parseHtmlString(html); + }) + + if (testNHP) + sys.debug("NodeHtmlParser: " + timeNodeHtmlParser); + if (testLXJS) + sys.debug("LibXmlJs: " + timeLibXmlJs); + if (testNHP && testLXJS) + sys.debug("Difference: " + ((timeNodeHtmlParser - timeLibXmlJs) / timeLibXmlJs) * 100); + }); + } + else + sys.debug("Error: got response status " + response.statusCode); + }) + .end(); diff --git a/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/runtests.html b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/runtests.html new file mode 100644 index 0000000..3543adc --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/runtests.html @@ -0,0 +1,107 @@ + + + + + Node.js HTML Parser + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/runtests.js b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/runtests.js new file mode 100644 index 0000000..c59393c --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/runtests.js @@ -0,0 +1,75 @@ +/*********************************************** +Copyright 2010, Chris Winberry . All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +***********************************************/ + +var sys = require("sys"); +var fs = require("fs"); +var htmlparser = require("./lib/node-htmlparser"); + +var testFolder = "./tests"; +var chunkSize = 5; + +var testFiles = fs.readdirSync(testFolder); +var testCount = 0; +var failedCount = 0; +for (var i in testFiles) { + testCount++; + var fileParts = testFiles[i].split("."); + fileParts.pop(); + var moduleName = fileParts.join("."); + var test = require(testFolder + "/" + moduleName); + var handlerCallback = function handlerCallback (error) { + if (error) + sys.puts("Handler error: " + error); + } + var handler = (test.type == "rss") ? + new htmlparser.RssHandler(handlerCallback, test.options) + : + new htmlparser.DefaultHandler(handlerCallback, test.options) + ; + var parser = new htmlparser.Parser(handler); + parser.parseComplete(test.html); + var resultComplete = handler.dom; + var chunkPos = 0; + parser.reset(); + while (chunkPos < test.html.length) { + parser.parseChunk(test.html.substring(chunkPos, chunkPos + chunkSize)); + chunkPos += chunkSize; + } + parser.done(); + var resultChunk = handler.dom; + var testResult = + sys.inspect(resultComplete, false, null) === sys.inspect(test.expected, false, null) + && + sys.inspect(resultChunk, false, null) === sys.inspect(test.expected, false, null) + ; + sys.puts("[" + test.name + "\]: " + (testResult ? "passed" : "FAILED")); + if (!testResult) { + failedCount++; + sys.puts("== Complete =="); + sys.puts(sys.inspect(resultComplete, false, null)); + sys.puts("== Chunked =="); + sys.puts(sys.inspect(resultChunk, false, null)); + sys.puts("== Expected =="); + sys.puts(sys.inspect(test.expected, false, null)); + } +} +sys.puts("Total tests: " + testCount); +sys.puts("Failed tests: " + failedCount); diff --git a/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/runtests.min.html b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/runtests.min.html new file mode 100644 index 0000000..2ec9e8f --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/runtests.min.html @@ -0,0 +1,107 @@ + + + + + Node.js HTML Parser + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/runtests.min.js b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/runtests.min.js new file mode 100644 index 0000000..d32c3aa --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/runtests.min.js @@ -0,0 +1,75 @@ +/*********************************************** +Copyright 2010, Chris Winberry . All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +***********************************************/ + +var sys = require("sys"); +var fs = require("fs"); +var htmlparser = require("./lib/node-htmlparser.min"); + +var testFolder = "./tests"; +var chunkSize = 5; + +var testFiles = fs.readdirSync(testFolder); +var testCount = 0; +var failedCount = 0; +for (var i in testFiles) { + testCount++; + var fileParts = testFiles[i].split("."); + fileParts.pop(); + var moduleName = fileParts.join("."); + var test = require(testFolder + "/" + moduleName); + var handlerCallback = function handlerCallback (error) { + if (error) + sys.puts("Handler error: " + error); + } + var handler = (test.type == "rss") ? + new htmlparser.RssHandler(handlerCallback, test.options) + : + new htmlparser.DefaultHandler(handlerCallback, test.options) + ; + var parser = new htmlparser.Parser(handler); + parser.parseComplete(test.html); + var resultComplete = handler.dom; + var chunkPos = 0; + parser.reset(); + while (chunkPos < test.html.length) { + parser.parseChunk(test.html.substring(chunkPos, chunkPos + chunkSize)); + chunkPos += chunkSize; + } + parser.done(); + var resultChunk = handler.dom; + var testResult = + sys.inspect(resultComplete, false, null) === sys.inspect(test.expected, false, null) + && + sys.inspect(resultChunk, false, null) === sys.inspect(test.expected, false, null) + ; + sys.puts("[" + test.name + "\]: " + (testResult ? "passed" : "FAILED")); + if (!testResult) { + failedCount++; + sys.puts("== Complete =="); + sys.puts(sys.inspect(resultComplete, false, null)); + sys.puts("== Chunked =="); + sys.puts(sys.inspect(resultChunk, false, null)); + sys.puts("== Expected =="); + sys.puts(sys.inspect(test.expected, false, null)); + } +} +sys.puts("Total tests: " + testCount); +sys.puts("Failed tests: " + failedCount); diff --git a/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/snippet.js b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/snippet.js new file mode 100644 index 0000000..2f54b36 --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/snippet.js @@ -0,0 +1,15 @@ +//node --prof --prof_auto profile.js +//deps/v8/tools/mac-tick-processor v8.log +var sys = require("sys"); +var htmlparser = require("./node-htmlparser"); + +var html = "text"; + +var handler = new htmlparser.DefaultHandler(function(err, dom) { + if (err) + sys.debug("Error: " + err); + else + sys.debug(sys.inspect(dom, false, null)); +}, { enforceEmptyTags: true }); +var parser = new htmlparser.Parser(handler); +parser.parseComplete(html); diff --git a/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/01-basic.js b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/01-basic.js new file mode 100644 index 0000000..57de7dc --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/01-basic.js @@ -0,0 +1,57 @@ +(function () { + +function RunningInNode () { + return( + (typeof require) == "function" + && + (typeof exports) == "object" + && + (typeof module) == "object" + && + (typeof __filename) == "string" + && + (typeof __dirname) == "string" + ); +} + +if (!RunningInNode()) { + if (!this.Tautologistics) + this.Tautologistics = {}; + if (!this.Tautologistics.NodeHtmlParser) + this.Tautologistics.NodeHtmlParser = {}; + if (!this.Tautologistics.NodeHtmlParser.Tests) + this.Tautologistics.NodeHtmlParser.Tests = []; + exports = {}; + this.Tautologistics.NodeHtmlParser.Tests.push(exports); +} + +exports.name = "Basic test"; +exports.html = "The TitleHello world"; +exports.expected = + [ { raw: 'html' + , data: 'html' + , type: 'tag' + , name: 'html' + , children: + [ { raw: 'title' + , data: 'title' + , type: 'tag' + , name: 'title' + , children: [ { raw: 'The Title', data: 'The Title', type: 'text' } ] + } + , { raw: 'body' + , data: 'body' + , type: 'tag' + , name: 'body' + , children: + [ { raw: 'Hello world' + , data: 'Hello world' + , type: 'text' + } + ] + } + ] + } + ]; + +})(); diff --git a/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/02-single_tag_1.js b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/02-single_tag_1.js new file mode 100644 index 0000000..8af04d9 --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/02-single_tag_1.js @@ -0,0 +1,35 @@ +(function () { + +function RunningInNode () { + return( + (typeof require) == "function" + && + (typeof exports) == "object" + && + (typeof module) == "object" + && + (typeof __filename) == "string" + && + (typeof __dirname) == "string" + ); +} + +if (!RunningInNode()) { + if (!this.Tautologistics) + this.Tautologistics = {}; + if (!this.Tautologistics.NodeHtmlParser) + this.Tautologistics.NodeHtmlParser = {}; + if (!this.Tautologistics.NodeHtmlParser.Tests) + this.Tautologistics.NodeHtmlParser.Tests = []; + exports = {}; + this.Tautologistics.NodeHtmlParser.Tests.push(exports); +} + +exports.name = "Single Tag 1"; +exports.html = "
    text
    "; +exports.expected = + [ { raw: 'br', data: 'br', type: 'tag', name: 'br' } + , { raw: 'text', data: 'text', type: 'text' } + ]; + +})(); diff --git a/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/03-single_tag_2.js b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/03-single_tag_2.js new file mode 100644 index 0000000..beae642 --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/03-single_tag_2.js @@ -0,0 +1,36 @@ +(function () { + +function RunningInNode () { + return( + (typeof require) == "function" + && + (typeof exports) == "object" + && + (typeof module) == "object" + && + (typeof __filename) == "string" + && + (typeof __dirname) == "string" + ); +} + +if (!RunningInNode()) { + if (!this.Tautologistics) + this.Tautologistics = {}; + if (!this.Tautologistics.NodeHtmlParser) + this.Tautologistics.NodeHtmlParser = {}; + if (!this.Tautologistics.NodeHtmlParser.Tests) + this.Tautologistics.NodeHtmlParser.Tests = []; + exports = {}; + this.Tautologistics.NodeHtmlParser.Tests.push(exports); +} + +exports.name = "Single Tag 2"; +exports.html = "
    text
    "; +exports.expected = + [ { raw: 'br', data: 'br', type: 'tag', name: 'br' } + , { raw: 'text', data: 'text', type: 'text' } + , { raw: 'br', data: 'br', type: 'tag', name: 'br' } + ]; + +})(); diff --git a/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/04-unescaped_in_script.js b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/04-unescaped_in_script.js new file mode 100644 index 0000000..1a23385 --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/04-unescaped_in_script.js @@ -0,0 +1,52 @@ +(function () { + +function RunningInNode () { + return( + (typeof require) == "function" + && + (typeof exports) == "object" + && + (typeof module) == "object" + && + (typeof __filename) == "string" + && + (typeof __dirname) == "string" + ); +} + +if (!RunningInNode()) { + if (!this.Tautologistics) + this.Tautologistics = {}; + if (!this.Tautologistics.NodeHtmlParser) + this.Tautologistics.NodeHtmlParser = {}; + if (!this.Tautologistics.NodeHtmlParser.Tests) + this.Tautologistics.NodeHtmlParser.Tests = []; + exports = {}; + this.Tautologistics.NodeHtmlParser.Tests.push(exports); +} + +exports.name = "Unescaped chars in script"; +exports.html = ""; +exports.expected = +[ { raw: 'head' + , data: 'head' + , type: 'tag' + , name: 'head' + , children: + [ { raw: 'script language="Javascript"' + , data: 'script language="Javascript"' + , type: 'script' + , name: 'script' + , attribs: { language: 'Javascript' } + , children: + [ { raw: 'var foo = ""; alert(2 > foo); var baz = 10 << 2; var zip = 10 >> 1; var yap = \"<<>>>><<\";' + , data: 'var foo = ""; alert(2 > foo); var baz = 10 << 2; var zip = 10 >> 1; var yap = \"<<>>>><<\";' + , type: 'text' + } + ] + } + ] + } +]; + +})(); diff --git a/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/05-tags_in_comment.js b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/05-tags_in_comment.js new file mode 100644 index 0000000..79fdd8c --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/05-tags_in_comment.js @@ -0,0 +1,44 @@ +(function () { + +function RunningInNode () { + return( + (typeof require) == "function" + && + (typeof exports) == "object" + && + (typeof module) == "object" + && + (typeof __filename) == "string" + && + (typeof __dirname) == "string" + ); +} + +if (!RunningInNode()) { + if (!this.Tautologistics) + this.Tautologistics = {}; + if (!this.Tautologistics.NodeHtmlParser) + this.Tautologistics.NodeHtmlParser = {}; + if (!this.Tautologistics.NodeHtmlParser.Tests) + this.Tautologistics.NodeHtmlParser.Tests = []; + exports = {}; + this.Tautologistics.NodeHtmlParser.Tests.push(exports); +} + +exports.name = "Special char in comment"; +exports.html = ""; +exports.expected = +[ { raw: 'head' + , data: 'head' + , type: 'tag' + , name: 'head' + , children: + [ { raw: ' commented out tags Test' + , data: ' commented out tags Test' + , type: 'comment' + } + ] + } +]; + +})(); diff --git a/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/06-comment_in_script.js b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/06-comment_in_script.js new file mode 100644 index 0000000..7737f38 --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/06-comment_in_script.js @@ -0,0 +1,44 @@ +(function () { + +function RunningInNode () { + return( + (typeof require) == "function" + && + (typeof exports) == "object" + && + (typeof module) == "object" + && + (typeof __filename) == "string" + && + (typeof __dirname) == "string" + ); +} + +if (!RunningInNode()) { + if (!this.Tautologistics) + this.Tautologistics = {}; + if (!this.Tautologistics.NodeHtmlParser) + this.Tautologistics.NodeHtmlParser = {}; + if (!this.Tautologistics.NodeHtmlParser.Tests) + this.Tautologistics.NodeHtmlParser.Tests = []; + exports = {}; + this.Tautologistics.NodeHtmlParser.Tests.push(exports); +} + +exports.name = "Script source in comment"; +exports.html = ""; +exports.expected = +[ { raw: 'script' + , data: 'script' + , type: 'script' + , name: 'script' + , children: + [ { raw: 'var foo = 1;' + , data: 'var foo = 1;' + , type: 'comment' + } + ] + } +]; + +})(); diff --git a/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/07-unescaped_in_style.js b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/07-unescaped_in_style.js new file mode 100644 index 0000000..fe4efad --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/07-unescaped_in_style.js @@ -0,0 +1,45 @@ +(function () { + +function RunningInNode () { + return( + (typeof require) == "function" + && + (typeof exports) == "object" + && + (typeof module) == "object" + && + (typeof __filename) == "string" + && + (typeof __dirname) == "string" + ); +} + +if (!RunningInNode()) { + if (!this.Tautologistics) + this.Tautologistics = {}; + if (!this.Tautologistics.NodeHtmlParser) + this.Tautologistics.NodeHtmlParser = {}; + if (!this.Tautologistics.NodeHtmlParser.Tests) + this.Tautologistics.NodeHtmlParser.Tests = []; + exports = {}; + this.Tautologistics.NodeHtmlParser.Tests.push(exports); +} + +exports.name = "Unescaped chars in style"; +exports.html = ""; +exports.expected = +[ { raw: 'style type="text/css"' + , data: 'style type="text/css"' + , type: 'style' + , name: 'style' + , attribs: { type: 'text/css' } + , children: + [ { raw: '\n body > p\n { font-weight: bold; }' + , data: '\n body > p\n { font-weight: bold; }' + , type: 'text' + } + ] + } +]; + +})(); diff --git a/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/08-extra_spaces_in_tag.js b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/08-extra_spaces_in_tag.js new file mode 100644 index 0000000..84192af --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/08-extra_spaces_in_tag.js @@ -0,0 +1,45 @@ +(function () { + +function RunningInNode () { + return( + (typeof require) == "function" + && + (typeof exports) == "object" + && + (typeof module) == "object" + && + (typeof __filename) == "string" + && + (typeof __dirname) == "string" + ); +} + +if (!RunningInNode()) { + if (!this.Tautologistics) + this.Tautologistics = {}; + if (!this.Tautologistics.NodeHtmlParser) + this.Tautologistics.NodeHtmlParser = {}; + if (!this.Tautologistics.NodeHtmlParser.Tests) + this.Tautologistics.NodeHtmlParser.Tests = []; + exports = {}; + this.Tautologistics.NodeHtmlParser.Tests.push(exports); +} + +exports.name = "Extra spaces in tag"; +exports.html = "<\n font \n size='14' \n>the text<\n / \nfont \n>"; +exports.expected = +[ { raw: '\n font \n size=\'14\' \n' + , data: 'font \n size=\'14\'' + , type: 'tag' + , name: 'font' + , attribs: { size: '14' } + , children: + [ { raw: 'the text' + , data: 'the text' + , type: 'text' + } + ] + } +]; + +})(); diff --git a/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/09-unquoted_attrib.js b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/09-unquoted_attrib.js new file mode 100644 index 0000000..3ad54e6 --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/09-unquoted_attrib.js @@ -0,0 +1,45 @@ +(function () { + +function RunningInNode () { + return( + (typeof require) == "function" + && + (typeof exports) == "object" + && + (typeof module) == "object" + && + (typeof __filename) == "string" + && + (typeof __dirname) == "string" + ); +} + +if (!RunningInNode()) { + if (!this.Tautologistics) + this.Tautologistics = {}; + if (!this.Tautologistics.NodeHtmlParser) + this.Tautologistics.NodeHtmlParser = {}; + if (!this.Tautologistics.NodeHtmlParser.Tests) + this.Tautologistics.NodeHtmlParser.Tests = []; + exports = {}; + this.Tautologistics.NodeHtmlParser.Tests.push(exports); +} + +exports.name = "Unquoted attributes"; +exports.html = "the text"; +exports.expected = +[ { raw: 'font size= 14' + , data: 'font size= 14' + , type: 'tag' + , name: 'font' + , attribs: { size: '14' } + , children: + [ { raw: 'the text' + , data: 'the text' + , type: 'text' + } + ] + } +]; + +})(); diff --git a/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/10-singular_attribute.js b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/10-singular_attribute.js new file mode 100644 index 0000000..37897dc --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/pulls/node-htmlparser/tests/10-singular_attribute.js @@ -0,0 +1,39 @@ +(function () { + +function RunningInNode () { + return( + (typeof require) == "function" + && + (typeof exports) == "object" + && + (typeof module) == "object" + && + (typeof __filename) == "string" + && + (typeof __dirname) == "string" + ); +} + +if (!RunningInNode()) { + if (!this.Tautologistics) + this.Tautologistics = {}; + if (!this.Tautologistics.NodeHtmlParser) + this.Tautologistics.NodeHtmlParser = {}; + if (!this.Tautologistics.NodeHtmlParser.Tests) + this.Tautologistics.NodeHtmlParser.Tests = []; + exports = {}; + this.Tautologistics.NodeHtmlParser.Tests.push(exports); +} + +exports.name = "Singular attribute"; +exports.html = "
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     Strunk & White + Daniel DiPaolo
     Notable Question + jonty
     Notable Question + Tim Jarvis
     Good Answer + Jay Godse
     Good Answer + Bastien Léonard
     Necromancer + J Chris A
     Civic Duty + Steve
     Notable Question + juvenis
     Famous Question + Ray Vega
     Enlightened + Konrad Rudolph
     Enlightened + sbi
     Enlightened + Kobi
     Enlightened + Joe Koberg
     Enlightened + Mehrdad Afshari
     Enlightened + Yacoby
     Enlightened + Peter Tillemans
     Enlightened + unomi
     Enlightened + JaredPar
     Enlightened + Ed Swangren
     Enlightened + Jon Skeet
     Notable Question + Sam
     Notable Question + Adam Backstrom
     Taxonomist + Marcus Krueger
     Notable Question + paxdiablo
     Famous Question + Gerhard Weiss
     Necromancer + JHurrah
     Necromancer + meder
     Good Question + joek1975
     Yearling + bbudge
     Notable Question + iPhoney
     Notable Question + Tony Lambert
     Notable Question + Fostah
    + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/server/node_modules/jquery/node_modules/htmlparser/testdata/trackerchecker.html b/server/node_modules/jquery/node_modules/htmlparser/testdata/trackerchecker.html new file mode 100644 index 0000000..2dfb1a0 --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/testdata/trackerchecker.html @@ -0,0 +1,2733 @@ + + + +www.trackerchecker.com - We check your trackers + + + + + + +
    + + + + + + +
    Home || Add a tracker || Latest trackers
    +
    + + +
    +follow us on Twitter
    +Follow trackerchecker on Twitter --> @trackerchecker. +
    +
    + + + + + + + +
    +We need your input!
    We're developing a new version of Trackerchecker and we want to know what functionality the users like to see in Trackerchecker V3! :) Contribute please and tell us what you like to see in Trackerchecker V3! +
    Name:
    E-mail:
    Suggestion
    +
    +
    + + +
    +
    + TrackerList
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     TrackerNameHistoryLastCheckedStatus
    1337x.orgview history2010-04-15 18:14:14Open
    420project.orgview history2010-04-15 18:23:46Closed
    acehd.netview history2010-04-15 18:05:34Closed
    acetorrents.netview history2010-04-15 18:09:10Open
    adult-cinema-network.netview history2010-04-15 18:21:21Open
    all4nothin.netview history2010-04-15 17:35:20Open
    allgirltorrents.comview history2010-04-15 17:45:50Closed
    allotracker.comview history2010-04-15 17:38:15Open
    appz.bitshock.orgview history2010-04-15 17:53:16Open
    appzuniverse.orgview history2010-04-15 17:49:10Closed
    arabfilms.orgview history2010-04-15 17:36:29Closed
    arabseries.orgview history2010-04-15 17:53:15Closed
    araditracker.comview history2010-04-15 17:37:17Closed
    arena-tr.comview history2010-04-15 18:21:22Closed
    arenabg.comview history2010-04-15 17:36:30Closed
    artofmisdirection.comview history2010-04-15 17:49:51Closed
    asiandvdclub.orgview history2010-04-15 17:35:13Open
    at-tracker.orgview history2010-04-15 18:09:40Offline
    atomico-torrent.comview history2010-04-15 18:00:09Closed
    audionews.ruview history2010-04-15 17:57:00Closed
    audiotracer.comview history2010-04-15 18:09:12Closed
    audiozonetorrents.comview history2010-04-15 18:22:18Open
    avatarbg.infoview history2010-04-15 18:09:40Closed
    awesome-hd.comview history2010-04-15 18:03:09Closed
    awesome-hd.netview history2010-04-15 18:09:25Closed
    baltracker.netview history2010-04-15 18:14:10Open
    bestmmatorrents.comview history2010-04-15 18:09:18Closed
    bestshare.roview history2010-04-15 17:37:02Closed
    bestxvid.orgview history2010-04-15 17:35:13Closed
    bit-hdtv.comview history2010-04-15 18:14:09Closed
    bitaddict.orgview history2010-04-15 17:45:11Closed
    bitchil.comview history2010-04-15 17:45:16Open
    bitflamers.comview history2010-04-15 18:21:13Closed
    bitgamer.comview history2010-04-15 18:05:52Closed
    bithq.orgview history2010-04-15 18:05:56Open
    bithumen.ath.cxview history2010-04-15 17:44:41Closed
    bitlove.huview history2010-04-15 17:35:02Closed
    bitme.orgview history2010-04-15 17:35:16Closed
    bitmetv.orgview history2010-04-15 17:40:50Closed
    bitmusic.huview history2010-04-15 18:22:56Closed
    bitnation.comview history2010-04-15 18:22:18Closed
    bitseduce.comview history2010-04-15 17:49:53Closed
    bitseek.orgview history2010-04-15 18:21:12Closed
    bitseek.orgview history2010-04-15 18:03:09Closed
    bitshock.orgview history2010-04-15 17:35:11Closed
    bitshock.orgview history2010-04-15 17:56:11Closed
    bitsoup.orgview history2010-04-15 17:35:20Closed
    bitSpyder.netview history2010-04-15 17:38:08Closed
    bittorrents.roview history2010-04-15 17:36:29Closed
    bitturk.netview history2010-04-15 18:09:18Closed
    biztorrents.comview history2010-04-15 17:45:11Open
    blackcats-games.netview history2010-04-15 17:36:28Closed
    blades-heaven.comview history2010-04-15 18:24:28Open
    blue-bytez.comview history2010-04-15 18:23:03Closed
    blue-whitegt.comview history2010-04-15 18:09:29Closed
    bmtorrents.netview history2010-04-15 17:38:13Closed
    bootytape.comview history2010-04-15 17:45:17Open
    bootytorrents.comview history2010-04-15 18:22:21Closed
    boxtorrents.comview history2010-04-15 18:22:19Closed
    bt.avistaz.comview history2010-04-15 18:14:10Closed
    bt.davka.infoview history2010-04-15 17:56:15Open
    bt.xbox-sky.ccview history2010-04-15 18:06:25Closed
    bt.xbox-sky.comview history2010-04-15 18:21:45Closed
    btgigs.infoview history2010-04-15 18:24:22Closed
    bwtorrents.comview history2010-04-15 18:05:56Closed
    bytelist.orgview history2010-04-15 17:53:12Offline
    cartoonchaos.orgview history2010-04-15 17:53:13Closed
    ccfbits.orgview history2010-04-15 17:56:59Closed
    chdbits.orgview history2010-04-15 18:13:08Open
    cheggit.net/view history2010-04-15 17:35:14Open
    chilebt.comview history2010-04-15 17:38:18Closed
    christiantorrents.comview history2010-04-15 17:56:54Closed
    chronictracker.comview history2010-04-15 18:00:23Closed
    cinema-obscura.comview history2010-04-15 18:17:13Closed
    cinemageddon.orgview history2010-04-15 17:45:11Closed
    cinematik.netview history2010-04-15 18:21:20Closed
    cleanvobs.orgview history2010-04-15 18:04:32Closed
    colombo-bt.orgview history2010-04-15 17:37:16Closed
    contego.wsview history2010-04-15 17:45:15Closed
    crazytorrent.euview history2010-04-15 18:22:17Closed
    czone.roview history2010-04-15 17:49:08Closed
    danger.lvview history2010-04-15 18:09:16Closed
    definitive-scene.comview history2010-04-15 18:23:46Open
    demonoid.comview history2010-04-15 17:45:48Closed
    devilwolfs.comview history2010-04-15 18:14:43Open
    diablotorrent.huview history2010-04-15 18:04:34Closed
    dididave.comview history2010-04-15 17:36:36Closed
    digitalhive.orgview history2010-04-15 17:45:10Open
    dimeadozen.orgview history2010-04-15 17:40:53Closed
    dnbtracker.orgview history2010-04-15 18:17:16Open
    docs.torrents.roview history2010-04-15 17:49:53Closed
    dvdseed.orgview history2010-04-15 17:56:11Closed
    dvdtreasure.euview history2010-04-15 17:53:16Closed
    ebookvortex.comview history2010-04-15 17:38:12Open
    eclipsetorrents.orgview history2010-04-15 17:40:45Closed
    egytorrent.comview history2010-04-15 17:49:09Closed
    egytorrent.comview history2010-04-15 18:16:07Open
    elbitz.netview history2010-04-15 18:09:35Closed
    elektronik.roview history2010-04-15 17:45:16Closed
    empornium.usview history2010-04-15 18:10:02Open
    eroticsource.plview history2010-04-15 18:06:22Closed
    estrenoslatinos.comview history2010-04-15 17:49:09Closed
    ethor.netview history2010-04-15 18:09:38Closed
    evopt.orgview history2010-04-15 17:49:03Closed
    Exigomusicview history2010-04-15 18:17:18Closed
    extremebits.orgview history2010-04-15 18:17:19Closed
    extremeshare.orgview history2010-04-15 17:36:35Open
    faplife.netview history2010-04-15 17:36:44Closed
    feedthe.netview history2010-04-15 17:37:23Closed
    filebits.orgview history2010-04-15 17:56:52Closed
    filelist.orgview history2010-04-15 18:09:17Closed
    filelist.roview history2010-04-15 17:49:53Closed
    filemp3.orgview history2010-04-15 17:36:43Closed
    fileporn.orgview history2010-04-15 17:53:11Closed
    flashtorrents.com.arview history2010-04-15 17:56:15Closed
    Free The Scene (FTS)view history2010-04-15 18:22:19Closed
    Fresh On TV (TvT)view history2010-04-15 18:17:13Open
    frztracker.sytes.netview history2010-04-15 18:03:49Closed
    fst.omnilounge.co.ukview history2010-04-15 17:36:29Closed
    fullcontactzone.comview history2010-04-15 18:03:17Closed
    gamecrook.comview history2010-04-15 17:42:03Closed
    gbvnet.roview history2010-04-15 17:38:09Closed
    gettorrents.orgview history2010-04-15 17:45:03Open
    gfxnews.ruview history2010-04-15 17:36:28Open
    gigatorrents.wsview history2010-04-15 18:14:10Closed
    glabella.orgview history2010-04-15 17:35:12Open
    globus-tracker.comview history2010-04-15 17:45:46Closed
    goem.orgview history2010-04-15 17:40:51Closed
    gormogon.comview history2010-04-15 18:09:29Open
    greek-tracker.comview history2010-04-15 17:35:15Closed
    grimetorrent.comview history2010-04-15 18:00:31Closed
    grtorrent.comview history2010-04-15 18:24:19Closed
    guiks.netview history2010-04-15 17:37:19Open
    h264torrents.comview history2010-04-15 18:21:54Closed
    h33t.comview history2010-04-15 18:17:20Open
    hd-bits.roview history2010-04-15 17:37:21Closed
    hd-torrents.orgview history2010-04-15 17:56:15Closed
    hdbits.orgview history2010-04-15 17:45:47Closed
    hdchina.orgview history2010-04-15 18:24:15Closed
    hdfrench.comview history2010-04-15 17:53:15Closed
    hdme.euview history2010-04-15 18:00:28Closed
    hdpre.comview history2010-04-15 17:35:03Closed
    hdsource.bizview history2010-04-15 18:04:34Closed
    hdstar.orgview history2010-04-15 17:45:12Closed
    hdvnbits.orgview history2010-04-15 17:49:50Closed
    heaventracker.orgview history2010-04-15 17:36:33Closed
    hermeticos.orgview history2010-04-15 18:14:44Closed
    horrorcharnel.kicks-ass.orgview history2010-04-15 18:21:15Closed
    hungercity.orgview history2010-04-15 17:34:14Open
    ifyounotknow.comview history2010-04-15 18:21:19Closed
    ilovetorrents.comview history2010-04-15 18:16:08Open
    indeep.bizview history2010-04-15 18:17:18Closed
    indietorrents.comview history2010-04-15 18:24:30Closed
    iplay.roview history2010-04-15 18:09:41Closed
    ipodnova.tvview history2010-04-15 17:36:36Closed
    iptorrents.comview history2010-04-15 18:23:02Closed
    joompalace.comview history2010-04-15 18:24:21Closed
    karagarga.netview history2010-04-15 18:09:40Closed
    killawaves.netview history2010-04-15 18:06:23Closed
    killawaves.orgview history2010-04-15 17:45:13Closed
    kinozal.wsview history2010-04-15 18:22:18Open
    kludd.comview history2010-04-15 18:00:23Closed
    lasttorrents.orgview history2010-04-15 18:09:38Open
    leecherslair.comview history2010-04-15 17:49:52Closed
    libble.comview history2010-04-15 18:03:18Closed
    libitina.netview history2010-04-15 18:21:16Closed
    linuxmafia.netview history2010-04-15 17:40:43Offline
    linuxtracker.orgview history2010-04-15 17:56:11Closed
    lostfilm.tvview history2010-04-15 18:03:20Open
    magiciantorrents.comview history2010-04-15 17:49:07Closed
    masterstb.comview history2010-04-15 17:34:17Closed
    mazetorrents.netview history2010-04-15 17:40:43Offline
    medioteka.comview history2010-04-15 17:37:02Closed
    mega-bits.comview history2010-04-15 17:34:16Closed
    mentol.roview history2010-04-15 17:36:42Closed
    metal.iplay.roview history2010-04-15 18:21:21Closed
    metalbits.orgview history2010-04-15 17:45:09Closed
    midnight-scene.comview history2010-04-15 18:23:43Closed
    midnight-torrents.comview history2010-04-15 17:38:14Closed
    mma-central.org.ukview history2010-04-15 17:36:27Closed
    mma-tracker.netview history2010-04-15 17:38:15Closed
    mp3nerds.orgview history2010-04-15 18:22:21Closed
    mucis-vid.comview history2010-04-15 17:35:16Closed
    musicplace.lvview history2010-04-15 17:53:17Closed
    mytorrent.tvview history2010-04-15 17:35:14Closed
    norbits.netview history2010-04-15 18:00:21Closed
    nordic-t.orgview history2010-04-15 18:00:22Closed
    nordicbits.orgview history2010-04-15 18:24:26Closed
    novaro.infoview history2010-04-15 18:16:56Closed
    novaro.infoview history2010-04-15 18:07:02Closed
    noviteti.comview history2010-04-15 18:09:35Closed
    ntorrents.netview history2010-04-15 17:34:13Closed
    nutorrent.comview history2010-04-15 18:14:45Open
    opennetwork.roview history2010-04-15 17:40:42Closed
    overtopropetorrents.comview history2010-04-15 17:49:55Closed
    ozone-torrents.orgview history2010-04-15 18:00:26Closed
    packme.inview history2010-04-15 17:49:54Closed
    Pedros btmusicview history2010-04-15 18:17:17Closed
    pianosheets.orgview history2010-04-15 18:17:16Closed
    pinkytorrents.comview history2010-04-15 18:03:11Open
    piranha.excom.usview history2010-04-15 17:40:43Open
    Pirate The Net (PtN)view history2010-04-15 17:40:45Closed
    piratebits.orgview history2010-04-15 17:44:14Closed
    piratefiles.seview history2010-04-15 18:17:11Closed
    piratetorrents.nuview history2010-04-15 18:00:28Closed
    pisexy.orgview history2010-04-15 17:56:16Closed
    polishbytes.netview history2010-04-15 17:57:00Open
    polishtracker.orgview history2010-04-15 18:05:34Closed
    pornbay.orgview history2010-04-15 18:09:11Open
    PotUKview history2010-04-15 17:37:21Closed
    powerscene.orgview history2010-04-15 18:14:14Closed
    pretome.netview history2010-04-15 17:40:44Closed
    proaudiotorrents.orgview history2010-04-15 18:03:17Closed
    psytorrents.infoview history2010-04-15 17:49:54Closed
    ptfiles.orgview history2010-04-15 18:03:49Closed
    punkhc.dyndns.orgview history2010-04-15 17:56:12Closed
    puretna.comview history2010-04-15 17:40:53Open
    pussytorrents.orgview history2010-04-15 18:05:34Open
    rapthe.netview history2010-04-15 18:21:22Closed
    rarbg.comview history2010-04-15 18:04:33Open
    reload-paradise.netview history2010-04-15 18:05:55Closed
    revolutiontt.netview history2010-04-15 18:21:55Closed
    rmvbustersview history2010-04-15 18:00:23Open
    rmvbusters.plview history2010-04-15 18:03:09Open
    scaliwags.orgview history2010-04-15 18:09:42Closed
    scene-gold.infoview history2010-04-15 17:37:27Offline
    scene-inspired.comview history2010-04-15 18:03:18Open
    sceneaccess.orgview history2010-04-15 17:40:44Closed
    scenebytes.clview history2010-04-15 17:38:10Closed
    scenehd.orgview history2010-04-15 17:45:03Closed
    sceneleech.orgview history2010-04-15 17:57:00Closed
    SceneLife (ScL)view history2010-04-15 18:23:43Closed
    scenetorrents.orgview history2010-04-15 18:23:44Closed
    scenetuga.orgview history2010-04-15 18:09:37Closed
    sciencehd.netview history2010-04-15 17:53:14Closed
    scifitorrents.netview history2010-04-15 17:45:16Offline
    secret-cinema.netview history2010-04-15 18:09:44Closed
    seductiongr.comview history2010-04-15 18:22:20Closed
    seedgames.orgview history2010-04-15 18:10:01Open
    seedmore.orgview history2010-04-15 17:49:07Closed
    sendthatshit.orgview history2010-04-15 18:23:44Open
    sharetorrents.plview history2010-04-15 17:34:13Closed
    sharing-torrents.comview history2010-04-15 18:21:18Closed
    slosoul.netview history2010-04-15 17:35:19Closed
    snowtigers.netview history2010-04-15 17:49:49Closed
    softmp3.orgview history2010-04-15 18:14:44Closed
    softmupparna.netview history2010-04-15 18:09:11Closed
    sounddamage.comview history2010-04-15 18:09:25Closed
    spanishtracker.comview history2010-04-15 17:37:27Open
    spank-d-monkey.comview history2010-04-15 18:24:23Closed
    special.pwtorrents.netview history2010-04-15 18:17:19Closed
    speed.cdview history2010-04-15 17:53:17Closed
    spiryt.ath.cxview history2010-04-15 17:49:49Closed
    sport-scene.netview history2010-04-15 18:17:19Closed
    sportbit.orgview history2010-04-15 18:09:09Closed
    sportleech.netview history2010-04-15 17:40:48Closed
    stmusic.orgview history2010-04-15 18:09:23Open
    supertorrents.orgview history2010-04-15 17:57:00Open
    swebits.orgview history2010-04-15 17:49:54Closed
    sweninjaz.orgview history2010-04-15 18:21:15Closed
    swep2p.orgview history2010-04-15 17:37:25Closed
    swepiracy.orgview history2010-04-15 18:24:22Closed
    swetorrents.no-ip.orgview history2010-04-15 17:35:10Closed
    taiphimhd.comview history2010-04-15 17:40:49Open
    tastetherainbow.wsview history2010-04-15 17:56:13Closed
    teamofgreekz.comview history2010-04-15 18:23:46Closed
    tehconnection.euview history2010-04-15 18:06:22Closed
    the-zomb.comview history2010-04-15 17:38:13Open
    thebox.bzview history2010-04-15 18:09:44Open
    thedvdclub.orgview history2010-04-15 17:49:50Open
    thegt.netview history2010-04-15 18:00:20Closed
    themixingbowl.orgview history2010-04-15 17:38:10Closed
    theoccult.bzview history2010-04-15 17:56:12Closed
    thepeerhub.comview history2010-04-15 18:24:16Open
    theplace.bzview history2010-04-15 18:09:17Closed
    thepokerbay.orgview history2010-04-15 17:36:33Open
    thevault.bzview history2010-04-15 18:14:45Closed
    titaniumtorrents.netview history2010-04-15 17:45:48Offline
    tmtorrents.orgview history2010-04-15 18:00:21Closed
    topbytes.netview history2010-04-15 17:37:17Closed
    topbytes.netview history2010-04-15 18:14:15Closed
    tophos.orgview history2010-04-15 18:24:22Closed
    torrent-damage.netview history2010-04-15 18:22:22Closed
    torrent.itview history2010-04-15 18:24:16Closed
    torrent411.comview history2010-04-15 17:45:12Open
    torrentbits.roview history2010-04-15 17:37:20Closed
    torrentbully.comview history2010-04-15 18:17:15Closed
    torrentdownloads.netview history2010-04-15 18:05:55Open
    torrentgaming.netview history2010-04-15 18:24:19Closed
    torrentgeeks.comview history2010-04-15 18:17:20Offline
    torrenthr.orgview history2010-04-15 18:24:16Open
    torrentkings.orgview history2010-04-15 17:53:09Closed
    torrentleech.orgview history2010-04-15 18:22:46Closed
    torrentseed.orgview history2010-04-15 17:35:15Closed
    torrentsforall.netview history2010-04-15 17:45:47Open
    torrentsmd.comview history2010-04-15 18:00:20Open
    torrentvault.orgview history2010-04-15 17:40:51Closed
    torrentzilla.orgview history2010-04-15 18:24:11Open
    torrentzone.netview history2010-04-15 17:45:04Open
    totaltorrents.comview history2010-04-15 17:37:31Closed
    trancebits.comview history2010-04-15 18:05:34Closed
    trancebooster.netview history2010-04-15 17:56:11Closed
    trancetraffic.comview history2010-04-15 17:37:18Closed
    tri-tavern.comview history2010-04-15 18:23:19Closed
    tribalmixes.comview history2010-04-15 18:20:39Open
    tugaleech.comview history2010-04-15 18:24:25Closed
    tunebully.comview history2010-04-15 17:49:08Closed
    tv.torrents.roview history2010-04-15 17:35:11Closed
    tvtorrents.comview history2010-04-15 17:38:02Open
    ugstorrents.comview history2010-04-15 18:09:15Closed
    uknova.comview history2010-04-15 18:14:15Closed
    underground-gamer.comview history2010-04-15 17:49:06Open
    victorrent.netview history2010-04-15 17:45:14Closed
    vortexnetwork.orgview history2010-04-15 17:53:16Offline
    waffles.fmview history2010-04-15 17:53:12Closed
    wantedfiles.roview history2010-04-15 17:49:07Open
    warezbros.orgview history2010-04-15 18:03:20Closed
    what.cdview history2010-04-15 17:53:12Closed
    wild-bytes.orgview history2010-04-15 18:00:27Closed
    wolfbits.orgview history2010-04-15 18:14:09Closed
    worldboxingvideoarchive.comview history2010-04-15 18:09:10Open
    www.bt-pt.netview history2010-04-15 18:09:45Closed
    www.llywot.comview history2010-04-15 17:53:13Closed
    xbitz.orgview history2010-04-15 18:21:21Open
    xider.huview history2010-04-15 18:05:34Open
    xtremespeeds.netview history2010-04-15 18:04:33Closed
    xtremewrestlingtorrents.netview history2010-04-15 18:00:27Open
    xtremezone.roview history2010-04-15 18:14:43Closed
    yuwabits.netview history2010-04-15 17:45:17Open
    zanettetorrent.comview history2010-04-15 18:17:12Closed
    zinebytes.orgview history2010-04-15 17:34:18Closed
    +
    +


    + + +
    + + + + + + diff --git a/server/node_modules/jquery/node_modules/htmlparser/tests/01-basic.js b/server/node_modules/jquery/node_modules/htmlparser/tests/01-basic.js new file mode 100644 index 0000000..7846898 --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/tests/01-basic.js @@ -0,0 +1,61 @@ +(function () { + +function RunningInNode () { + return( + (typeof require) == "function" + && + (typeof exports) == "object" + && + (typeof module) == "object" + && + (typeof __filename) == "string" + && + (typeof __dirname) == "string" + ); +} + +if (!RunningInNode()) { + if (!this.Tautologistics) + this.Tautologistics = {}; + if (!this.Tautologistics.NodeHtmlParser) + this.Tautologistics.NodeHtmlParser = {}; + if (!this.Tautologistics.NodeHtmlParser.Tests) + this.Tautologistics.NodeHtmlParser.Tests = []; + exports = {}; + this.Tautologistics.NodeHtmlParser.Tests.push(exports); +} + +exports.name = "Basic test"; +exports.options = { + handler: {} + , parser: {} +}; +exports.html = "The TitleHello world"; +exports.expected = + [ { raw: 'html' + , data: 'html' + , type: 'tag' + , name: 'html' + , children: + [ { raw: 'title' + , data: 'title' + , type: 'tag' + , name: 'title' + , children: [ { raw: 'The Title', data: 'The Title', type: 'text' } ] + } + , { raw: 'body' + , data: 'body' + , type: 'tag' + , name: 'body' + , children: + [ { raw: 'Hello world' + , data: 'Hello world' + , type: 'text' + } + ] + } + ] + } + ]; + +})(); diff --git a/server/node_modules/jquery/node_modules/htmlparser/tests/02-single_tag_1.js b/server/node_modules/jquery/node_modules/htmlparser/tests/02-single_tag_1.js new file mode 100644 index 0000000..1735b5e --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/tests/02-single_tag_1.js @@ -0,0 +1,39 @@ +(function () { + +function RunningInNode () { + return( + (typeof require) == "function" + && + (typeof exports) == "object" + && + (typeof module) == "object" + && + (typeof __filename) == "string" + && + (typeof __dirname) == "string" + ); +} + +if (!RunningInNode()) { + if (!this.Tautologistics) + this.Tautologistics = {}; + if (!this.Tautologistics.NodeHtmlParser) + this.Tautologistics.NodeHtmlParser = {}; + if (!this.Tautologistics.NodeHtmlParser.Tests) + this.Tautologistics.NodeHtmlParser.Tests = []; + exports = {}; + this.Tautologistics.NodeHtmlParser.Tests.push(exports); +} + +exports.name = "Single Tag 1"; +exports.options = { + handler: {} + , parser: {} +}; +exports.html = "
    text
    "; +exports.expected = + [ { raw: 'br', data: 'br', type: 'tag', name: 'br' } + , { raw: 'text', data: 'text', type: 'text' } + ]; + +})(); diff --git a/server/node_modules/jquery/node_modules/htmlparser/tests/03-single_tag_2.js b/server/node_modules/jquery/node_modules/htmlparser/tests/03-single_tag_2.js new file mode 100644 index 0000000..2e6e92c --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/tests/03-single_tag_2.js @@ -0,0 +1,40 @@ +(function () { + +function RunningInNode () { + return( + (typeof require) == "function" + && + (typeof exports) == "object" + && + (typeof module) == "object" + && + (typeof __filename) == "string" + && + (typeof __dirname) == "string" + ); +} + +if (!RunningInNode()) { + if (!this.Tautologistics) + this.Tautologistics = {}; + if (!this.Tautologistics.NodeHtmlParser) + this.Tautologistics.NodeHtmlParser = {}; + if (!this.Tautologistics.NodeHtmlParser.Tests) + this.Tautologistics.NodeHtmlParser.Tests = []; + exports = {}; + this.Tautologistics.NodeHtmlParser.Tests.push(exports); +} + +exports.name = "Single Tag 2"; +exports.options = { + handler: {} + , parser: {} +}; +exports.html = "
    text
    "; +exports.expected = + [ { raw: 'br', data: 'br', type: 'tag', name: 'br' } + , { raw: 'text', data: 'text', type: 'text' } + , { raw: 'br', data: 'br', type: 'tag', name: 'br' } + ]; + +})(); diff --git a/server/node_modules/jquery/node_modules/htmlparser/tests/04-unescaped_in_script.js b/server/node_modules/jquery/node_modules/htmlparser/tests/04-unescaped_in_script.js new file mode 100644 index 0000000..fb2cc3a --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/tests/04-unescaped_in_script.js @@ -0,0 +1,56 @@ +(function () { + +function RunningInNode () { + return( + (typeof require) == "function" + && + (typeof exports) == "object" + && + (typeof module) == "object" + && + (typeof __filename) == "string" + && + (typeof __dirname) == "string" + ); +} + +if (!RunningInNode()) { + if (!this.Tautologistics) + this.Tautologistics = {}; + if (!this.Tautologistics.NodeHtmlParser) + this.Tautologistics.NodeHtmlParser = {}; + if (!this.Tautologistics.NodeHtmlParser.Tests) + this.Tautologistics.NodeHtmlParser.Tests = []; + exports = {}; + this.Tautologistics.NodeHtmlParser.Tests.push(exports); +} + +exports.name = "Unescaped chars in script"; +exports.options = { + handler: {} + , parser: {} +}; +exports.html = ""; +exports.expected = +[ { raw: 'head' + , data: 'head' + , type: 'tag' + , name: 'head' + , children: + [ { raw: 'script language="Javascript"' + , data: 'script language="Javascript"' + , type: 'script' + , name: 'script' + , attribs: { language: 'Javascript' } + , children: + [ { raw: 'var foo = ""; alert(2 > foo); var baz = 10 << 2; var zip = 10 >> 1; var yap = \"<<>>>><<\";' + , data: 'var foo = ""; alert(2 > foo); var baz = 10 << 2; var zip = 10 >> 1; var yap = \"<<>>>><<\";' + , type: 'text' + } + ] + } + ] + } +]; + +})(); diff --git a/server/node_modules/jquery/node_modules/htmlparser/tests/05-tags_in_comment.js b/server/node_modules/jquery/node_modules/htmlparser/tests/05-tags_in_comment.js new file mode 100644 index 0000000..68a0779 --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/tests/05-tags_in_comment.js @@ -0,0 +1,48 @@ +(function () { + +function RunningInNode () { + return( + (typeof require) == "function" + && + (typeof exports) == "object" + && + (typeof module) == "object" + && + (typeof __filename) == "string" + && + (typeof __dirname) == "string" + ); +} + +if (!RunningInNode()) { + if (!this.Tautologistics) + this.Tautologistics = {}; + if (!this.Tautologistics.NodeHtmlParser) + this.Tautologistics.NodeHtmlParser = {}; + if (!this.Tautologistics.NodeHtmlParser.Tests) + this.Tautologistics.NodeHtmlParser.Tests = []; + exports = {}; + this.Tautologistics.NodeHtmlParser.Tests.push(exports); +} + +exports.name = "Special char in comment"; +exports.options = { + handler: {} + , parser: {} +}; +exports.html = ""; +exports.expected = +[ { raw: 'head' + , data: 'head' + , type: 'tag' + , name: 'head' + , children: + [ { raw: ' commented out tags Test' + , data: ' commented out tags Test' + , type: 'comment' + } + ] + } +]; + +})(); diff --git a/server/node_modules/jquery/node_modules/htmlparser/tests/06-comment_in_script.js b/server/node_modules/jquery/node_modules/htmlparser/tests/06-comment_in_script.js new file mode 100644 index 0000000..2d04ec0 --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/tests/06-comment_in_script.js @@ -0,0 +1,48 @@ +(function () { + +function RunningInNode () { + return( + (typeof require) == "function" + && + (typeof exports) == "object" + && + (typeof module) == "object" + && + (typeof __filename) == "string" + && + (typeof __dirname) == "string" + ); +} + +if (!RunningInNode()) { + if (!this.Tautologistics) + this.Tautologistics = {}; + if (!this.Tautologistics.NodeHtmlParser) + this.Tautologistics.NodeHtmlParser = {}; + if (!this.Tautologistics.NodeHtmlParser.Tests) + this.Tautologistics.NodeHtmlParser.Tests = []; + exports = {}; + this.Tautologistics.NodeHtmlParser.Tests.push(exports); +} + +exports.name = "Script source in comment"; +exports.options = { + handler: {} + , parser: {} +}; +exports.html = ""; +exports.expected = +[ { raw: 'script' + , data: 'script' + , type: 'script' + , name: 'script' + , children: + [ { raw: 'var foo = 1;' + , data: 'var foo = 1;' + , type: 'comment' + } + ] + } +]; + +})(); diff --git a/server/node_modules/jquery/node_modules/htmlparser/tests/07-unescaped_in_style.js b/server/node_modules/jquery/node_modules/htmlparser/tests/07-unescaped_in_style.js new file mode 100644 index 0000000..563a64a --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/tests/07-unescaped_in_style.js @@ -0,0 +1,49 @@ +(function () { + +function RunningInNode () { + return( + (typeof require) == "function" + && + (typeof exports) == "object" + && + (typeof module) == "object" + && + (typeof __filename) == "string" + && + (typeof __dirname) == "string" + ); +} + +if (!RunningInNode()) { + if (!this.Tautologistics) + this.Tautologistics = {}; + if (!this.Tautologistics.NodeHtmlParser) + this.Tautologistics.NodeHtmlParser = {}; + if (!this.Tautologistics.NodeHtmlParser.Tests) + this.Tautologistics.NodeHtmlParser.Tests = []; + exports = {}; + this.Tautologistics.NodeHtmlParser.Tests.push(exports); +} + +exports.name = "Unescaped chars in style"; +exports.options = { + handler: {} + , parser: {} +}; +exports.html = ""; +exports.expected = +[ { raw: 'style type="text/css"' + , data: 'style type="text/css"' + , type: 'style' + , name: 'style' + , attribs: { type: 'text/css' } + , children: + [ { raw: '\n body > p\n { font-weight: bold; }' + , data: '\n body > p\n { font-weight: bold; }' + , type: 'text' + } + ] + } +]; + +})(); diff --git a/server/node_modules/jquery/node_modules/htmlparser/tests/08-extra_spaces_in_tag.js b/server/node_modules/jquery/node_modules/htmlparser/tests/08-extra_spaces_in_tag.js new file mode 100644 index 0000000..1767565 --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/tests/08-extra_spaces_in_tag.js @@ -0,0 +1,49 @@ +(function () { + +function RunningInNode () { + return( + (typeof require) == "function" + && + (typeof exports) == "object" + && + (typeof module) == "object" + && + (typeof __filename) == "string" + && + (typeof __dirname) == "string" + ); +} + +if (!RunningInNode()) { + if (!this.Tautologistics) + this.Tautologistics = {}; + if (!this.Tautologistics.NodeHtmlParser) + this.Tautologistics.NodeHtmlParser = {}; + if (!this.Tautologistics.NodeHtmlParser.Tests) + this.Tautologistics.NodeHtmlParser.Tests = []; + exports = {}; + this.Tautologistics.NodeHtmlParser.Tests.push(exports); +} + +exports.name = "Extra spaces in tag"; +exports.options = { + handler: {} + , parser: {} +}; +exports.html = "<\n font \n size='14' \n>the text<\n / \nfont \n>"; +exports.expected = +[ { raw: '\n font \n size=\'14\' \n' + , data: 'font \n size=\'14\'' + , type: 'tag' + , name: 'font' + , attribs: { size: '14' } + , children: + [ { raw: 'the text' + , data: 'the text' + , type: 'text' + } + ] + } +]; + +})(); diff --git a/server/node_modules/jquery/node_modules/htmlparser/tests/09-unquoted_attrib.js b/server/node_modules/jquery/node_modules/htmlparser/tests/09-unquoted_attrib.js new file mode 100644 index 0000000..da6bac7 --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/tests/09-unquoted_attrib.js @@ -0,0 +1,49 @@ +(function () { + +function RunningInNode () { + return( + (typeof require) == "function" + && + (typeof exports) == "object" + && + (typeof module) == "object" + && + (typeof __filename) == "string" + && + (typeof __dirname) == "string" + ); +} + +if (!RunningInNode()) { + if (!this.Tautologistics) + this.Tautologistics = {}; + if (!this.Tautologistics.NodeHtmlParser) + this.Tautologistics.NodeHtmlParser = {}; + if (!this.Tautologistics.NodeHtmlParser.Tests) + this.Tautologistics.NodeHtmlParser.Tests = []; + exports = {}; + this.Tautologistics.NodeHtmlParser.Tests.push(exports); +} + +exports.name = "Unquoted attributes"; +exports.options = { + handler: {} + , parser: {} +}; +exports.html = "the text"; +exports.expected = +[ { raw: 'font size= 14' + , data: 'font size= 14' + , type: 'tag' + , name: 'font' + , attribs: { size: '14' } + , children: + [ { raw: 'the text' + , data: 'the text' + , type: 'text' + } + ] + } +]; + +})(); diff --git a/server/node_modules/jquery/node_modules/htmlparser/tests/10-singular_attribute.js b/server/node_modules/jquery/node_modules/htmlparser/tests/10-singular_attribute.js new file mode 100644 index 0000000..6c22e1a --- /dev/null +++ b/server/node_modules/jquery/node_modules/htmlparser/tests/10-singular_attribute.js @@ -0,0 +1,43 @@ +(function () { + +function RunningInNode () { + return( + (typeof require) == "function" + && + (typeof exports) == "object" + && + (typeof module) == "object" + && + (typeof __filename) == "string" + && + (typeof __dirname) == "string" + ); +} + +if (!RunningInNode()) { + if (!this.Tautologistics) + this.Tautologistics = {}; + if (!this.Tautologistics.NodeHtmlParser) + this.Tautologistics.NodeHtmlParser = {}; + if (!this.Tautologistics.NodeHtmlParser.Tests) + this.Tautologistics.NodeHtmlParser.Tests = []; + exports = {}; + this.Tautologistics.NodeHtmlParser.Tests.push(exports); +} + +exports.name = "Singular attribute"; +exports.options = { + handler: {} + , parser: {} +}; +exports.html = "